Management needs a way for QEMU to confirm that no I/O has been sent to the target and not to the source. To provide this guarantee we rely on a file in local persistent storage. QEMU receives a file descriptor via SCM_RIGHTS and writes a single byte to it. If it fails, it will fail the drive-reopen command too and management knows that no I/O request has been issued to the new destination. Likewise, if management finds the file to have nonzero size it knows that the target is valid and that indeed I/O requests could have been submitted to it.
The argument does not have an HMP equivalent. Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- blockdev.c | 24 +++++++++++++++++++++--- hmp.c | 2 +- qapi-schema.json | 10 +++++++++- qmp-commands.hx | 12 +++++++++++- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/blockdev.c b/blockdev.c index 08953fa..78b72f2 100644 --- a/blockdev.c +++ b/blockdev.c @@ -660,7 +660,7 @@ void do_commit(Monitor *mon, const QDict *qdict) } static void change_blockdev_image(BlockDriverState *bs, const char *new_image_file, - const char *format, Error **errp) + const char *format, int fd, Error **errp) { BlockDriver *old_drv, *proto_drv; BlockDriver *drv = NULL; @@ -702,6 +702,16 @@ static void change_blockdev_image(BlockDriverState *bs, const char *new_image_fi bdrv_close(bs); ret = bdrv_open(bs, new_image_file, flags, drv); + + if (ret == 0 && fd != -1) { + ret = write(fd, "", 1) == 1 ? 0 : -1; + qemu_fdatasync(fd); + close(fd); + if (ret < 0) { + bdrv_close(bs); + } + } + /* * If reopening the image file we just created fails, fall back * and try to re-open the original image. If that fails too, we @@ -718,9 +725,20 @@ static void change_blockdev_image(BlockDriverState *bs, const char *new_image_fi } void qmp_drive_reopen(const char *device, const char *new_image_file, - bool has_format, const char *format, Error **errp) + bool has_format, const char *format, + bool has_witness, const char *witness, + Error **errp) { BlockDriverState *bs; + int fd = -1; + + if (has_witness) { + fd = monitor_get_fd(cur_mon, witness); + if (fd == -1) { + error_set(errp, QERR_FD_NOT_FOUND, witness); + return; + } + } bs = bdrv_find(device); if (!bs) { @@ -731,7 +749,7 @@ void qmp_drive_reopen(const char *device, const char *new_image_file, block_job_cancel_sync(bs->job); } change_blockdev_image(bs, new_image_file, - has_format ? format : NULL, errp); + has_format ? format : NULL, fd, errp); } static void blockdev_do_action(int kind, void *data, Error **errp) diff --git a/hmp.c b/hmp.c index 28697ec..f67c441 100644 --- a/hmp.c +++ b/hmp.c @@ -744,7 +744,7 @@ void hmp_drive_reopen(Monitor *mon, const QDict *qdict) const char *format = qdict_get_try_str(qdict, "format"); Error *errp = NULL; - qmp_drive_reopen(device, filename, !!format, format, &errp); + qmp_drive_reopen(device, filename, !!format, format, false, NULL, &errp); hmp_handle_error(mon, &errp); } diff --git a/qapi-schema.json b/qapi-schema.json index 0bf3a25..2e5a925 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1228,6 +1228,13 @@ # # @format: #optional the format of the new image, default is 'qcow2'. # +# @witness: A file descriptor name that was passed via getfd. QEMU will write +# a single byte to this file descriptor before completing the command +# successfully. If the byte is not written to the file, it is +# guaranteed that the guest has not issued any I/O to the new image. +# Failure to write the byte is fatal just like failure to open the new +# image, and will cause the guest to revert to the currently open file. +# # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound # If @new-image-file can't be opened, OpenFileFailed @@ -1236,7 +1243,8 @@ # Since 1.1 ## { 'command': 'drive-reopen', - 'data': { 'device': 'str', 'new-image-file': 'str', '*format': 'str' } } + 'data': { 'device': 'str', 'new-image-file': 'str', '*format': 'str', + '*witness': 'str' } } ## # @human-monitor-command: diff --git a/qmp-commands.hx b/qmp-commands.hx index 6ea0ef5..fedfc36 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -829,7 +829,7 @@ EQMP { .name = "drive-reopen", - .args_type = "device:B,new-image-file:s,format:s?", + .args_type = "device:B,new-image-file:s,format:s?,witness:s?", .mhandler.cmd_new = qmp_marshal_input_drive_reopen, }, @@ -842,11 +842,21 @@ guest is expecting the drive to change its content, the new image should contain the same data of the current one. One use case is to terminate a drive-mirror command. +The command can optionally write a single byte to a file descriptor name +that was passed via SCM rights (getfd). QEMU will write a single byte +to this file descriptor before completing the command successfully. +If the byte is not written to the file, it is guaranteed that the +guest has not issued any I/O to the new image. Failure to write the +byte is fatal just like failure to open the new image, and will cause +the guest to revert to the currently open file. + + Arguments: - "device": device name to operate on (json-string) - "new-image-file": name of new image file (json-string) - "format": format of new image (json-string, optional) +- "witness": file descriptor previously passed via SCM rights (json-string, optional) Example: -- 1.7.9.3