Patchwork [20/20] monitor: add commands to start/stop dirty bitmap

login
register
mail settings
Submitter Paolo Bonzini
Date Dec. 12, 2012, 1:46 p.m.
Message ID <1355319999-30627-21-git-send-email-pbonzini@redhat.com>
Download mbox | patch
Permalink /patch/205550/
State New
Headers show

Comments

Paolo Bonzini - Dec. 12, 2012, 1:46 p.m.
Signed-off-by: Paolo Bonzini <pbonzini@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(+)
Eric Blake - Dec. 21, 2012, 6:30 p.m.
On 12/12/2012 06:46 AM, Paolo Bonzini wrote:
> Signed-off-by: Paolo Bonzini <pbonzini@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(+)
> 

A bit light on the commit message.  What is the proper sequence of QMP
commands for using this feature in relation to driver-mirror and such?
For example, let's say I want to start a mirror, then shutdown qemu,
then start a new qemu -S; what is the right sequence for when to call
this to re-initialize the dirty bitmap and resume the mirror at the same
point, all prior to issuing 'cont' to the new qemu instance?  Once I
know that, then I can fix libvirt to allow disk-copy across qemu
restarts (right now, libvirt limits disk-copy to transient domains,
since those don't have to worry about restarts).

> +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.

4k to 64M here...

> +++ b/qapi-schema.json
> @@ -3028,3 +3028,59 @@
>  # Since: 1.3.0
>  ##
>  { 'command': 'nbd-server-stop' }
> +
> +##
> +# @blockdev-dirty-enable:
> +#

> +# @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.

but 512 here.  Which is right?

> +#
> +# Returns: Nothing on success
> +#
> +# Since: 1.3

1.4, now

> +##
> +{ '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

You named it 'drive-mirror', not 'blockdev-drive-mirror'.

> +# 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

Again, 1.4.

Patch

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,