Patchwork [RFC,4/4] QAPI: add command for live block commit, 'block-commit'

login
register
mail settings
Submitter Jeff Cody
Date July 31, 2012, 5:16 a.m.
Message ID <ff5bba25bd4fe423857463362ca42f7ede677593.1343710713.git.jcody@redhat.com>
Download mbox | patch
Permalink /patch/174142/
State New
Headers show

Comments

Jeff Cody - July 31, 2012, 5:16 a.m.
The command for live block commit is added, which has the following
arguments:

device: the block device to perform the commit on (mandatory)
base:   the base image to commit into; optional (if not specified,
        it is the underlying original image)
top:    the top image of the commit - all data from inside top down
        to base will be committed into base. optional (if not specified,
        it is the active image) - see note below
speed:  maximum speed, in bytes/sec
on_error: action to take on error (optional - default is report)

note: eventually this will support merging down the active layer,
      but that code is not yet complete.  If the active layer is passed
      in currently as top, or top is left to the default, then the error
      QERR_TOP_NOT_FOUND will be returned.

The is down as a block job, so upon completion a BLOCK_JOB_COMPLETED will
be emitted.

Signed-off-by: Jeff Cody <jcody@redhat.com>
---
 blockdev.c       |   78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qapi-schema.json |   33 +++++++++++++++++++++++
 qmp-commands.hx  |    6 +++++
 3 files changed, 117 insertions(+)
Eric Blake - July 31, 2012, 6:38 p.m.
On 07/30/2012 11:16 PM, Jeff Cody wrote:
> The command for live block commit is added, which has the following
> arguments:
> 
> device: the block device to perform the commit on (mandatory)
> base:   the base image to commit into; optional (if not specified,
>         it is the underlying original image)
> top:    the top image of the commit - all data from inside top down
>         to base will be committed into base. optional (if not specified,
>         it is the active image) - see note below
> speed:  maximum speed, in bytes/sec
> on_error: action to take on error (optional - default is report)
> 
> note: eventually this will support merging down the active layer,
>       but that code is not yet complete.  If the active layer is passed
>       in currently as top, or top is left to the default, then the error
>       QERR_TOP_NOT_FOUND will be returned.
> 
> The is down as a block job, so upon completion a BLOCK_JOB_COMPLETED will
> be emitted.

s/The is down/This is done/ ?

Also, how does block-job-cancel interact with the job started by this
command?  Is it something we can cancel and restart at will, like
block-stream?


> +++ b/qapi-schema.json
> @@ -1365,6 +1365,39 @@
>    'returns': 'str' } 
>  
>  ##
> +# @block-commit
> +#
> +# Live commit of data from child image nodes into parent nodes - i.e.,
> +# writes data between 'top' and 'base' into 'base'.
> +#
> +# @device:  the name of the device
> +#
> +# @base:   #optional The parent image of the device to write data into.
> +#                    If not specified, this is the original parent image.
> +#
> +# @top:    #optional The child image, above which data will not be commited

s/commited/committed/

> +# @on_error: #optional the action to take on an error (default report)
> +#
> +# Returns: Nothing on success
> +#          If commit or stream is already active on this device, DeviceInUse
> +#          If @device does not exist, DeviceNotFound
> +#          If image commit is not supported by this device, NotSupported
> +#          If @base does not exist, BaseNotFound
> +#          If @top does not exist, TopNotFound
> +#          If @speed is invalid, InvalidParameter
> +#
> +# Since: 1.2
> +#
> +##
> +{ 'command': 'block-commit',
> +  'data': { 'device': 'str', '*base': 'str', '*top': 'str',
> +            '*speed': 'int', '*on_error': 'BlockdevOnError' } }

s/on_error/on-error/ (hmm, I guess you were copying from Paolo's series,
so he probably has the same change to make)

Patch

diff --git a/blockdev.c b/blockdev.c
index f39d301..a61ee38 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -831,6 +831,84 @@  exit:
     return;
 }
 
+void qmp_block_commit(const char *device,
+                      bool has_base, const char *base,
+                      bool has_top, const char *top,
+                      bool has_speed, int64_t speed,
+                      bool has_on_error, BlockdevOnError on_error,
+                      Error **errp)
+{
+    BlockDriverState *bs;
+    BlockDriverState *base_bs, *top_bs;
+    Error *local_err = NULL;
+    int orig_base_flags, orig_top_flags;
+
+    if (!has_on_error) {
+        on_error = BLOCKDEV_ON_ERROR_REPORT;
+    }
+
+    /* drain all i/o before commits */
+    bdrv_drain_all();
+
+
+    bs = bdrv_find(device);
+    if (!bs) {
+        error_set(errp, QERR_DEVICE_NOT_FOUND, device);
+        return;
+    }
+    if (base && has_base) {
+        base_bs = bdrv_find_backing_image(bs, base);
+        if (base_bs == NULL) {
+            error_set(errp, QERR_BASE_NOT_FOUND, base);
+            return;
+        }
+    } else {
+        base_bs = bdrv_find_base(bs);
+        if (base_bs == NULL) {
+            error_set(errp, QERR_BASE_NOT_FOUND, "NULL");
+            return;
+        }
+    }
+
+    if (top && has_top) {
+        /* if we want to allow the active layer,
+         * use 'bdrv_find_image()' here */
+        top_bs = bdrv_find_backing_image(bs, top);
+        if (top_bs == NULL) {
+            error_set(errp, QERR_TOP_NOT_FOUND, top);
+            return;
+        }
+    } else {
+        top_bs = bs;
+    }
+
+    orig_base_flags = bdrv_get_flags(base_bs);
+    /* convert base_bs to r/w, if necessary */
+    if (!(orig_base_flags & BDRV_O_RDWR)) {
+        bdrv_reopen(base_bs, orig_base_flags | BDRV_O_RDWR);
+    }
+
+    /* need to also make top r/w, so that when the commit is
+     * finished we can change the backing file */
+    orig_top_flags = bdrv_get_flags(top_bs);
+    /* convert top_bs to r/w, if necessary */
+    if (!(orig_top_flags & BDRV_O_RDWR)) {
+        bdrv_reopen(top_bs, orig_top_flags | BDRV_O_RDWR);
+    }
+
+    commit_start(bs, base_bs, top_bs, speed, on_error,
+                 block_job_cb, bs, orig_base_flags, orig_top_flags,
+                 &local_err);
+    if (local_err != NULL) {
+        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));
+}
+
 void qmp_drive_mirror(const char *device, const char *target,
                       bool has_format, const char *format,
                       enum MirrorSyncMode sync,
diff --git a/qapi-schema.json b/qapi-schema.json
index db6903f..9ab6f90 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -1365,6 +1365,39 @@ 
   'returns': 'str' } 
 
 ##
+# @block-commit
+#
+# Live commit of data from child image nodes into parent nodes - i.e.,
+# writes data between 'top' and 'base' into 'base'.
+#
+# @device:  the name of the device
+#
+# @base:   #optional The parent image of the device to write data into.
+#                    If not specified, this is the original parent image.
+#
+# @top:    #optional The child image, above which data will not be commited
+#                    down.  If not specified, this is the active layer.
+#
+# @speed:  #optional the maximum speed, in bytes per second
+#
+# @on_error: #optional the action to take on an error (default report)
+#
+# Returns: Nothing on success
+#          If commit or stream is already active on this device, DeviceInUse
+#          If @device does not exist, DeviceNotFound
+#          If image commit is not supported by this device, NotSupported
+#          If @base does not exist, BaseNotFound
+#          If @top does not exist, TopNotFound
+#          If @speed is invalid, InvalidParameter
+#
+# Since: 1.2
+#
+##
+{ 'command': 'block-commit',
+  'data': { 'device': 'str', '*base': 'str', '*top': 'str',
+            '*speed': 'int', '*on_error': 'BlockdevOnError' } }
+
+##
 # @drive-mirror
 #
 # Start mirroring a block device's writes to a new destination.
diff --git a/qmp-commands.hx b/qmp-commands.hx
index bde96cf..2f21142 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -723,6 +723,12 @@  EQMP
     },
 
     {
+        .name       = "block-commit",
+        .args_type  = "device:B,base:s?,top:s?,speed:o?,on_error:s?",
+        .mhandler.cmd_new = qmp_marshal_input_block_commit,
+    },
+
+    {
         .name       = "block-job-set-speed",
         .args_type  = "device:B,speed:o",
         .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed,