Add a new transaction type, "mirror". It stacks a new blkmirror file (instead of a snapshot) on top of the existing image.
It is possible to combine snapshot and mirror as two actions in the same transaction. Because of atomicity ensured by blockdev-transaction, this will create a snapshot *and* ensure that _all_ operations that are sent to it are also mirrored. Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- blockdev.c | 47 +++++++++++++++++++++++++++++++++++------------ qapi-schema.json | 19 ++++++++++++++++++- qmp-commands.hx | 12 +++++++++++- 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/blockdev.c b/blockdev.c index 61e20f7..eef93f0 100644 --- a/blockdev.c +++ b/blockdev.c @@ -735,12 +735,13 @@ void qmp_blockdev_transaction(BlockdevActionList *dev_list, BlockdevAction *dev_info = NULL; BlkTransactionStates *states; BlockDriver *proto_drv; - BlockDriver *drv; + BlockDriver *target_drv; + BlockDriver *drv = NULL; int flags; const char *device; const char *format; const char *new_image_file = NULL; - bool do_snapshot; + char *new_source_file = NULL; QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states; QSIMPLEQ_INIT(&snap_bdrv_states); @@ -759,11 +760,23 @@ void qmp_blockdev_transaction(BlockdevActionList *dev_list, switch (dev_info->kind) { case BLOCKDEV_ACTION_KIND_SNAPSHOT: device = dev_info->snapshot->device; - new_image_file = dev_info->snapshot->snapshot_file; - do_snapshot = !dev_info->snapshot->has_reuse || !dev_info->snapshot->reuse; + if (!dev_info->snapshot->has_reuse || !dev_info->snapshot->reuse) { + new_image_file = dev_info->snapshot->snapshot_file; + } + new_source_file = g_strdup(dev_info->snapshot->snapshot_file); format = dev_info->snapshot->format; assert(!(dev_info->snapshot->has_format && format)); break; + + case BLOCKDEV_ACTION_KIND_MIRROR: + device = dev_info->mirror->device; + if (!dev_info->mirror->has_reuse || !dev_info->mirror->reuse) { + new_image_file = dev_info->mirror->target; + } + format = dev_info->mirror->format; + assert(!(dev_info->mirror->has_format && format)); + drv = bdrv_find_format("blkmirror"); + break; default: abort(); } @@ -771,12 +784,19 @@ void qmp_blockdev_transaction(BlockdevActionList *dev_list, if (!format) { format = "qcow2"; } - - drv = bdrv_find_format(format); - if (!drv) { + target_drv = bdrv_find_format(format); + if (!target_drv) { error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); goto delete_and_fail; } + if (!drv) { + drv = target_drv; + } + + if (dev_info->kind == BLOCKDEV_ACTION_KIND_MIRROR) { + new_source_file = g_strdup_printf("blkmirror:%s:%s", format, + dev_info->mirror->target); + } states->old_bs = bdrv_find(device); if (!states->old_bs) { @@ -800,32 +820,34 @@ void qmp_blockdev_transaction(BlockdevActionList *dev_list, flags = states->old_bs->open_flags; - proto_drv = bdrv_find_protocol(new_image_file); + proto_drv = bdrv_find_protocol(new_source_file); if (!proto_drv) { error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); goto delete_and_fail; } /* create new image w/backing file */ - if (do_snapshot) { + if (new_image_file) { ret = bdrv_img_create(new_image_file, format, states->old_bs->filename, states->old_bs->drv->format_name, NULL, -1, flags); if (ret) { - error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file); + error_set(errp, QERR_OPEN_FILE_FAILED, new_source_file); goto delete_and_fail; } } /* We will manually add the backing_hd field to the bs later */ states->new_bs = bdrv_new(""); - ret = bdrv_open(states->new_bs, new_image_file, + ret = bdrv_open(states->new_bs, new_source_file, flags | BDRV_O_NO_BACKING, drv); if (ret != 0) { - error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file); + error_set(errp, QERR_OPEN_FILE_FAILED, new_source_file); goto delete_and_fail; } + g_free(new_source_file); + new_source_file = NULL; } @@ -853,6 +875,7 @@ exit: QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) { g_free(states); } + g_free(new_source_file); return; } diff --git a/qapi-schema.json b/qapi-schema.json index 78df122..5f0fcee 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1131,6 +1131,22 @@ '*reuse': 'bool' } } ## +# @BlockdevMirror +# +# @device: the name of the device to start mirroring. +# +# @target: the target of the new image. A new file will be created if +# @reuse is absent or false. +# +# @format: #optional the format of the snapshot image, default is 'qcow2'. +# +# @reuse: #optional whether QEMU should create a new image. +## +{ 'type': 'BlockdevMirror', + 'data': { 'device': 'str', 'target': 'str', '*format': 'str', + '*reuse': 'bool' } } + +## # @BlockdevAction # # A discriminated record of operations that can be performed with @@ -1138,7 +1154,8 @@ ## { 'union': 'BlockdevAction', 'data': { - 'snapshot': 'BlockdevSnapshot' + 'snapshot': 'BlockdevSnapshot', + 'mirror': 'BlockdevMirror', } } ## diff --git a/qmp-commands.hx b/qmp-commands.hx index 6728495..b7a9ada 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -701,7 +701,11 @@ operation for now is snapshotting. If there is any failure performing any of the operations, all snapshots for the group are abandoned, and the original disks pre-snapshot attempt are used. +Mirrored writes keep the previous image file open, and start writing +data also to the new file specified in the command. + A list of dictionaries is accepted, that contains the actions to be performed. + For snapshots this is the device, the file to use for the new snapshot, and the format. The default format, if not specified, is qcow2. Image files can be created by QEMU, or it can be created externally. @@ -713,7 +717,7 @@ Arguments: actions array: - "type": the operation to perform. The only supported - value is "snapshot". (json-string) + values are "snapshot" and "mirror". (json-string) - "data": a dictionary. The contents depend on the value of "type". When "type" is "snapshot": - "device": device name to snapshot (json-string) @@ -721,6 +725,12 @@ actions array: - "format": format of new image (json-string, optional) - "reuse": whether QEMU should look for an existing image file (json-bool, optional, default false) + When "type" is "mirror": + - "device": device name to snapshot (json-string) + - "target": name of destination image file (json-string) + - "format": format of new image (json-string, optional) + - "reuse": whether QEMU should look for an existing image file + (json-bool, optional, default false) Example: -- 1.7.7.6