Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- blockdev.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++ blockdev.h | 1 + hmp-commands.hx | 39 ++++++++++++++++++++++++++++++++++ hmp.c | 27 +++++++++++++++++++++++ hmp.h | 2 ++ qapi-schema.json | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ qmp-commands.hx | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 245 insertions(+)
diff --git a/blockdev.c b/blockdev.c index 37e6743..96e1b4e 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1323,6 +1323,61 @@ void qmp_drive_mirror(const char *device, const char *target, drive_get_ref(drive_get_by_blockdev(bs)); } +void qmp_blockdev_dirty_enable(const char *device, const char *file, + bool has_granularity, uint32_t granularity, + Error **errp) +{ + BlockDriverState *bs; + DriveInfo *drv; + Error *local_err = NULL; + + bs = bdrv_find(device); + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + drv = drive_get_by_blockdev(bs); + bdrv_enable_dirty_tracking(bs, granularity, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Release previous usage of dirty bitmap. */ + if (drv->dirty_use) { + bdrv_disable_persistent_dirty_tracking(bs); + bdrv_disable_dirty_tracking(bs); + } + drv->dirty_use = true; + + bdrv_enable_persistent_dirty_tracking(bs, file, errp); +} + +void qmp_blockdev_dirty_disable(const char *device, bool has_force, bool force, Error **errp) +{ + BlockDriverState *bs = bdrv_find(device); + DriveInfo *drv; + + if (!bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + return; + } + + drv = drive_get_by_blockdev(bs); + if (!drv->dirty_use) { + error_setg(errp, "dirty tracking not enabled on device '%s'", device); + return; + } + + if (has_force && force) { + bdrv_disable_persistent_dirty_tracking(bs); + } + + bdrv_disable_dirty_tracking(bs); + drv->dirty_use = false; +} + static BlockJob *find_block_job(const char *device) { BlockDriverState *bs; diff --git a/blockdev.h b/blockdev.h index 5f27b64..deb5bbf 100644 --- a/blockdev.h +++ b/blockdev.h @@ -38,6 +38,7 @@ struct DriveInfo { const char *serial; QTAILQ_ENTRY(DriveInfo) next; int refcount; + int dirty_use; }; DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); diff --git a/hmp-commands.hx b/hmp-commands.hx index 010b8c9..349cd0d 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1025,6 +1025,45 @@ using the specified target. ETEXI { + .name = "dirty_enable", + .args_type = "device:B,file:s,granularity:i?", + .params = "device file [granularity]", + "Defaults to MB if no size suffix is specified, ie. B/K/M/G/T", + .help = "initiates tracking of\n\t\t\t" + "dirty blocks for a block device.", + .mhandler.cmd = hmp_dirty_enable, + }, +STEXI +@item dirty_enable +@findex dirty_enable +Start tracking dirty blocks for a block device. Dirty blocks will +be written to an on-disk file, with one bit per block and an arbitrary +granularity. + +If the dirty bitmap is already active, or used by something else (for +example @command{drive_mirror}), the granularity argument must be absent +or equal to the active granularity. The granularity must be a power-of-two +comprised between 4,096 and 67,108,864. +ETEXI + + { + .name = "dirty_disable", + .args_type = "force:-f,device:B", + .params = "[-f] device", + .help = "prepares to disable tracking\n\t\t\t" + "dirty blocks of a block device.", + .mhandler.cmd = hmp_dirty_disable, + }, +STEXI +@item dirty_disable +@findex dirty_disable +Prepare QEMU to stop tracking dirty blocks for a block device. The +actual end of dirty tracking could be delayed while the dirty bitmap +is in use by another command such as @command{drive_mirror}, unless +the @option{-f} option is used. +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 428c563..e3d2c47 100644 --- a/hmp.c +++ b/hmp.c @@ -1335,3 +1335,30 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict) qmp_nbd_server_stop(&errp); hmp_handle_error(mon, &errp); } + +void hmp_dirty_enable(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *file = qdict_get_str(qdict, "file"); + bool has_granularity = qdict_haskey(qdict, "granularity"); + int granularity = -1; + Error *errp = NULL; + + if (has_granularity) { + granularity = qdict_get_int(qdict, "granularity"); + } + + qmp_blockdev_dirty_enable(device, file, + has_granularity, granularity, &errp); + hmp_handle_error(mon, &errp); +} + +void hmp_dirty_disable(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + bool force = qdict_get_try_bool(qdict, "force", false); + Error *errp = NULL; + + qmp_blockdev_dirty_disable(device, true, force, &errp); + hmp_handle_error(mon, &errp); +} diff --git a/hmp.h b/hmp.h index 0ab03be..7408e45 100644 --- a/hmp.h +++ b/hmp.h @@ -80,5 +80,7 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict); void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); void hmp_nbd_server_add(Monitor *mon, const QDict *qdict); void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict); +void hmp_dirty_enable(Monitor *mon, const QDict *qdict); +void hmp_dirty_disable(Monitor *mon, const QDict *qdict); #endif diff --git a/qapi-schema.json b/qapi-schema.json index fb38106..cdf3772 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3028,3 +3028,59 @@ # Since: 1.3.0 ## { 'command': 'nbd-server-stop' } + +## +# @blockdev-dirty-enable: +# +# Start tracking dirty blocks for a block device. Dirty blocks will +# be written to an on-disk file, with one bit per block and an arbitrary +# granularity. If the file already exists, the dirty bitmap is loaded +# from the file. +# +# It is not an error to use this command if the dirty bitmap is already +# active; the dirty blocks will simply be written to a new file from this +# point on. +# +# If the dirty bitmap is already active, or used by something else (for +# example drive-mirror), the granularity argument must be absent or equal +# to the active granularity. Also, in this case the file must not exist. +# +# @device: the name of the device to track dirty blocks of +# +# @filename: the name of the file to write the bitmap to +# +# @granularity: #optional granularity of the dirty bitmap, default is 64K +# if the image format doesn't have clusters, 4K if the clusters +# are smaller than that, else the cluster size. Must be a +# power of 2 between 512 and 64M. +# +# Returns: Nothing on success +# +# Since: 1.3 +## +{ 'command': 'blockdev-dirty-enable', + 'data': {'device': 'str', 'filename': 'str', '*granularity': 'uint32' } } + +## +# @blockdev-dirty-disable: +# +# Stop tracking dirty blocks for a block device. Dirty blocks will +# be written to an on-disk file, with one bit per block and an arbitrary +# granularity. +# +# If the dirty bitmap is already active, or used by something else (for +# example blockdev-drive-mirror), the granularity argument must be absent +# or equal to the active granularity. +# +# @device: the name of the device to track dirty blocks of +# +# @force: #optional true to immediately stop writing to the dirty +# bitmap file; false to do so only when the last user of the +# dirty bitmap stops using it (default false) +# +# Returns: Nothing on success +# +# Since: 1.3 +## +{ 'command': 'blockdev-dirty-disable', + 'data': {'device': 'str', '*force': 'bool'} } diff --git a/qmp-commands.hx b/qmp-commands.hx index 97c52c9..e0fde11 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -2589,6 +2589,71 @@ EQMP }, { + .name = "blockdev-dirty-enable", + .args_type = "device:s,file:s,granularity:i?", + .mhandler.cmd_new = qmp_marshal_input_blockdev_dirty_enable, + }, +SQMP +blockdev-dirty-enable +--------------------- + +Start tracking dirty blocks for a block device. Dirty blocks will +be written to an on-disk file, with one bit per block and an arbitrary +granularity. If the file already exists, the dirty bitmap is loaded +from the file. + +It is not an error to use this command if the dirty bitmap is already +active; the dirty blocks will simply be written to a new file from this +point on. + +If the dirty bitmap is already active, or used by something else (for +example blockdev-drive-mirror), the granularity argument must be absent +or equal to the active granularity. Also, in this case the file must +not exist. + +Arguments: + +- device: device name (json-string) +- file: path to the dirty tracking file (json-string) +- granularity: granularity of the dirty bitmap (json-int, optional, + must be a power of two between 512 and 64M. + +The default value of the granularity is, if the image format defines +a cluster size, the cluster size or 4096, whichever is larger. If it +does not define a cluster size, the default value of the granularity +is 65536. + +Example: + +-> { "execute": "blockdev-dirty-enable", "arguments": { + "device": "ide0-hd0", "path": "/var/lib/libvirt/dirty/image.dbmp" } } +<- { "return": {} } +EQMP + + { + .name = "blockdev-dirty-disable", + .args_type = "device:s,force:b", + .mhandler.cmd_new = qmp_marshal_input_blockdev_dirty_disable, + }, +SQMP +blockdev-dirty-disable +---------------------- + +Arguments: + +- device: device name (json-string) +- force: true to immediately stop writing to the dirty bitmap file; + false to do so only when the last user of the dirty bitmap stops using + it (json-boolean). + +Example: + +-> { "execute": "blockdev-dirty-disable", "arguments": { + "device": "ide0-hd0", "force": false } } +<- { "return": {} } +EQMP + + { .name = "query-block-jobs", .args_type = "", .mhandler.cmd_new = qmp_marshal_input_query_block_jobs, -- 1.8.0.1