On 09/26/2012 11:56 AM, Paolo Bonzini wrote: > This adds the monitor commands that start the mirroring job. > > Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> > --- > blockdev.c | 125 > ++++++++++++++++++++++++++++++++++++++++++++++++++++++- > hmp-commands.hx | 21 ++++++++++ > hmp.c | 28 +++++++++++++ > hmp.h | 1 + > qapi-schema.json | 33 +++++++++++++++ > qmp-commands.hx | 42 +++++++++++++++++++ > 6 file modificati, 249 inserzioni(+). 1 rimozione(-) > > diff --git a/blockdev.c b/blockdev.c > index 9069ca1..722aab5 100644 > --- a/blockdev.c > +++ b/blockdev.c > @@ -1118,6 +1117,130 @@ void qmp_block_stream(const char *device, bool > has_base, > trace_qmp_block_stream(bs, bs->job); > } > > +void qmp_drive_mirror(const char *device, const char *target, > + bool has_format, const char *format, > + enum MirrorSyncMode sync, > + bool has_mode, enum NewImageMode mode, > + bool has_speed, int64_t speed, Error **errp) > +{ > + BlockDriverInfo bdi; > + BlockDriverState *bs; > + BlockDriverState *source, *target_bs; > + BlockDriver *proto_drv; > + BlockDriver *drv = NULL; > + Error *local_err = NULL; > + int flags; > + uint64_t size; > + int ret; > + > + if (!has_speed) { > + speed = 0; > + } > + if (!has_mode) { > + mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; > + } > + > + bs = bdrv_find(device); > + if (!bs) { > + error_set(errp, QERR_DEVICE_NOT_FOUND, device); > + return; > + } > + > + if (!has_format) { > + format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : > bs->drv->format_name; > + } > + if (format) { > + drv = bdrv_find_format(format); > + if (!drv) { > + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); > + return; > + } > + } > + > + if (!bdrv_is_inserted(bs)) { > + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); > + return; > + } > + > + if (bdrv_in_use(bs)) { > + error_set(errp, QERR_DEVICE_IN_USE, device); > + return; > + } > + > + flags = bs->open_flags | BDRV_O_RDWR; > + source = bs->backing_hd; > + if (!source && sync == MIRROR_SYNC_MODE_TOP) { > + sync = MIRROR_SYNC_MODE_FULL; > + } > + > + proto_drv = bdrv_find_protocol(target); > + if (!proto_drv) { > + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); > + return; > + } > + > + if (sync == MIRROR_SYNC_MODE_FULL && mode != NEW_IMAGE_MODE_EXISTING) { > + /* create new image w/o backing file */ > + assert(format && drv); > + bdrv_get_geometry(bs, &size); > + size *= 512; > + ret = bdrv_img_create(target, format, > + NULL, NULL, NULL, size, flags); > + } else { > + switch (mode) { > + case NEW_IMAGE_MODE_EXISTING: > + ret = 0; > + break; > + case NEW_IMAGE_MODE_ABSOLUTE_PATHS: > + /* create new image with backing file */ > + ret = bdrv_img_create(target, format, > + source->filename, > + source->drv->format_name,
Should we assert(source->drv != NULL)? Or, alternatively, use bdrv_get_format_name(source) here. > + NULL, -1, flags); > + break; > + default: > + abort(); > + } > + } > + > + if (ret) { > + error_set(errp, QERR_OPEN_FILE_FAILED, target); > + return; > + } > + > + target_bs = bdrv_new(""); > + ret = bdrv_open(target_bs, target, flags | BDRV_O_NO_BACKING, drv); > + > + if (ret < 0) { > + bdrv_delete(target_bs); > + error_set(errp, QERR_OPEN_FILE_FAILED, target); > + return; > + } > + > + /* We need a backing file if we will copy parts of a cluster. */ > + if (bdrv_get_info(target_bs, &bdi) >= 0 && bdi.cluster_size != 0 && > + bdi.cluster_size >= BDRV_SECTORS_PER_DIRTY_CHUNK * 512) { > + ret = bdrv_open_backing_file(target_bs); > + if (ret < 0) { > + bdrv_delete(target_bs); > + error_set(errp, QERR_OPEN_FILE_FAILED, target); > + return; > + } > + } > + > + mirror_start(bs, target_bs, speed, sync, block_job_cb, bs, &local_err); > + if (local_err != NULL) { > + bdrv_delete(target_bs); > + error_propagate(errp, local_err); > + return; > + } > + > + /* Grab a reference so hotplug does not delete the BlockDriverState from > + * underneath us. > + */ > + drive_get_ref(drive_get_by_blockdev(bs)); > +} > + > static BlockJob *find_block_job(const char *device) > { > BlockDriverState *bs; > diff --git a/hmp-commands.hx b/hmp-commands.hx > index 4e52436..9ac4cf6 100644 > --- a/hmp-commands.hx > +++ b/hmp-commands.hx > @@ -1006,6 +1006,27 @@ Snapshot device, using snapshot file as target if > provided > ETEXI > > { > + .name = "drive_mirror", > + .args_type = "reuse:-n,full:-f,device:B,target:s,format:s?", > + .params = "[-n] [-f] device target [format]", > + .help = "initiates live storage\n\t\t\t" > + "migration for a device. The device's contents > are\n\t\t\t" > + "copied to the new image file, including data > that\n\t\t\t" > + "is written after the command is started.\n\t\t\t" > + "The -n flag requests QEMU to reuse the image > found\n\t\t\t" > + "in new-image-file, instead of recreating it from > scratch.\n\t\t\t" > + "The -f flag requests QEMU to copy the whole > disk,\n\t\t\t" > + "so that the result does not need a backing > file.\n\t\t\t", > + .mhandler.cmd = hmp_drive_mirror, > + }, > +STEXI > +@item drive_mirror > +@findex drive_mirror > +Start mirroring a block device's writes to a new destination, > +using the specified target. > +ETEXI > + > + { > .name = "drive_add", > .args_type = "pci_addr:s,opts:s", > .params = "[[<domain>:]<bus>:]<slot>\n" > diff --git a/hmp.c b/hmp.c > index 7819110..94d4d41 100644 > --- a/hmp.c > +++ b/hmp.c > @@ -759,6 +759,34 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict) > hmp_handle_error(mon, &errp); > } > > +void hmp_drive_mirror(Monitor *mon, const QDict *qdict) > +{ > + const char *device = qdict_get_str(qdict, "device"); > + const char *filename = qdict_get_str(qdict, "target"); > + const char *format = qdict_get_try_str(qdict, "format"); > + int reuse = qdict_get_try_bool(qdict, "reuse", 0); > + int full = qdict_get_try_bool(qdict, "full", 0); > + enum NewImageMode mode; > + Error *errp = NULL; > + > + if (!filename) { > + error_set(&errp, QERR_MISSING_PARAMETER, "target"); > + hmp_handle_error(mon, &errp); > + return; > + } > + > + if (reuse) { > + mode = NEW_IMAGE_MODE_EXISTING; > + } else { > + mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; > + } > + > + qmp_drive_mirror(device, filename, !!format, format, > + full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, > + true, mode, false, 0, &errp); > + hmp_handle_error(mon, &errp); > +} > + > void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict) > { > const char *device = qdict_get_str(qdict, "device"); > diff --git a/hmp.h b/hmp.h > index 7bdd23c..34eb2b3 100644 > --- a/hmp.h > +++ b/hmp.h > @@ -51,6 +51,7 @@ void hmp_block_passwd(Monitor *mon, const QDict *qdict); > void hmp_balloon(Monitor *mon, const QDict *qdict); > void hmp_block_resize(Monitor *mon, const QDict *qdict); > void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict); > +void hmp_drive_mirror(Monitor *mon, const QDict *qdict); > void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); > void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict); > void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict); > diff --git a/qapi-schema.json b/qapi-schema.json > index 9ba2f86..4827ed3 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -1529,6 +1529,39 @@ > 'returns': 'str' } > > ## > +# @drive-mirror > +# > +# Start mirroring a block device's writes to a new destination. > +# > +# @device: the name of the device whose writes should be mirrored. > +# > +# @target: the target of the new image. If the file exists, or if it > +# is a device, the existing file/device will be used as the new > +# destination. If it does not exist, a new file will be created. > +# > +# @format: #optional the format of the new destination, default is to > +# probe is @mode is 'existing', else the format of the source > +# > +# @mode: #optional whether and how QEMU should create a new image, default is > +# 'absolute-paths'. > +# > +# @speed: #optional the maximum speed, in bytes per second > +# > +# @sync: what parts of the disk image should be copied to the destination > +# (all the disk, only the sectors allocated in the topmost image, or > +# only new I/O). > +# > +# Returns: nothing on success > +# If @device is not a valid block device, DeviceNotFound > +# > +# Since 1.3 > +## > +{ 'command': 'drive-mirror', > + 'data': { 'device': 'str', 'target': 'str', '*format': 'str', > + 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', > + '*speed': 'int' } } > + > +## > # @migrate_cancel > # > # Cancel the current executing migration process. > diff --git a/qmp-commands.hx b/qmp-commands.hx > index 017544e..25800a8 100644 > --- a/qmp-commands.hx > +++ b/qmp-commands.hx > @@ -906,6 +906,48 @@ Example: > EQMP > > { > + .name = "drive-mirror", > + .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?", > + .mhandler.cmd_new = qmp_marshal_input_drive_mirror, > + }, > + > +SQMP > +drive-mirror > +------------ > + > +Start mirroring a block device's writes to a new destination. target > +specifies the target of the new image. If the file exists, or if it is > +a device, it will be used as the new destination for writes. If does not > +exist, a new file will be created. format specifies the format of the > +mirror image, default is to probe if mode='existing', else the format > +of the source. > + > +Arguments: > + > +- "device": device name to operate on (json-string) > +- "target": name of new image file (json-string) > +- "format": format of new image (json-string, optional) > +- "mode": how an image file should be created into the target > + file/device (NewImageMode, optional, default 'absolute-paths') > +- "speed": maximum speed of the streaming job, in bytes per second > + (json-int) > +- "sync": what parts of the disk image should be copied to the destination; > + possibilities include "full" for all the disk, "top" for only the sectors > + allocated in the topmost image, or "none" to only replicate new I/O > + (MirrorSyncMode). > + > + > +Example: > + > +-> { "execute": "drive-mirror", "arguments": { "device": "ide-hd0", > + "target": > "/some/place/my-image", > + "sync": "full", > + "format": "qcow2" } } > +<- { "return": {} } > + > +EQMP > + > + { > .name = "balloon", > .args_type = "value:M", > .mhandler.cmd_new = qmp_marshal_input_balloon, >