diff mbox

[v6,2/8] qmp: Add optional bool "unmap" to drive-mirror

Message ID 1432790990-25383-3-git-send-email-famz@redhat.com
State New
Headers show

Commit Message

Fam Zheng May 28, 2015, 5:29 a.m. UTC
If specified as "true", it allows discarding on target sectors where source is
not allocated.

Signed-off-by: Fam Zheng <famz@redhat.com>
---
 block/mirror.c            | 7 +++++--
 blockdev.c                | 5 +++++
 hmp.c                     | 2 +-
 include/block/block_int.h | 2 ++
 qapi/block-core.json      | 8 +++++++-
 qmp-commands.hx           | 3 +++
 6 files changed, 23 insertions(+), 4 deletions(-)

Comments

Paolo Bonzini May 28, 2015, 8:28 a.m. UTC | #1
On 28/05/2015 07:29, Fam Zheng wrote:
> If specified as "true", it allows discarding on target sectors where source is
> not allocated.
> 
> Signed-off-by: Fam Zheng <famz@redhat.com>

Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>

> ---
>  block/mirror.c            | 7 +++++--
>  blockdev.c                | 5 +++++
>  hmp.c                     | 2 +-
>  include/block/block_int.h | 2 ++
>  qapi/block-core.json      | 8 +++++++-
>  qmp-commands.hx           | 3 +++
>  6 files changed, 23 insertions(+), 4 deletions(-)
> 
> diff --git a/block/mirror.c b/block/mirror.c
> index 58f391a..85995b2 100644
> --- a/block/mirror.c
> +++ b/block/mirror.c
> @@ -57,6 +57,7 @@ typedef struct MirrorBlockJob {
>      int in_flight;
>      int sectors_in_flight;
>      int ret;
> +    bool unmap;
>  } MirrorBlockJob;
>  
>  typedef struct MirrorOp {
> @@ -651,6 +652,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
>                               int64_t buf_size,
>                               BlockdevOnError on_source_error,
>                               BlockdevOnError on_target_error,
> +                             bool unmap,
>                               BlockCompletionFunc *cb,
>                               void *opaque, Error **errp,
>                               const BlockJobDriver *driver,
> @@ -703,6 +705,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
>                    int64_t speed, uint32_t granularity, int64_t buf_size,
>                    MirrorSyncMode mode, BlockdevOnError on_source_error,
>                    BlockdevOnError on_target_error,
> +                  bool unmap,
>                    BlockCompletionFunc *cb,
>                    void *opaque, Error **errp)
>  {
> @@ -717,7 +720,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
>      base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
>      mirror_start_job(bs, target, replaces,
>                       speed, granularity, buf_size,
> -                     on_source_error, on_target_error, cb, opaque, errp,
> +                     on_source_error, on_target_error, unmap, cb, opaque, errp,
>                       &mirror_job_driver, is_none_mode, base);
>  }
>  
> @@ -765,7 +768,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
>  
>      bdrv_ref(base);
>      mirror_start_job(bs, base, NULL, speed, 0, 0,
> -                     on_error, on_error, cb, opaque, &local_err,
> +                     on_error, on_error, false, cb, opaque, &local_err,
>                       &commit_active_job_driver, false, base);
>      if (local_err) {
>          error_propagate(errp, local_err);
> diff --git a/blockdev.c b/blockdev.c
> index 5eaf77e..4387e14 100644
> --- a/blockdev.c
> +++ b/blockdev.c
> @@ -2632,6 +2632,7 @@ void qmp_drive_mirror(const char *device, const char *target,
>                        bool has_buf_size, int64_t buf_size,
>                        bool has_on_source_error, BlockdevOnError on_source_error,
>                        bool has_on_target_error, BlockdevOnError on_target_error,
> +                      bool has_unmap, bool unmap,
>                        Error **errp)
>  {
>      BlockBackend *blk;
> @@ -2663,6 +2664,9 @@ void qmp_drive_mirror(const char *device, const char *target,
>      if (!has_buf_size) {
>          buf_size = DEFAULT_MIRROR_BUF_SIZE;
>      }
> +    if (!has_unmap) {
> +        unmap = true;
> +    }
>  
>      if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
>          error_set(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
> @@ -2802,6 +2806,7 @@ void qmp_drive_mirror(const char *device, const char *target,
>                   has_replaces ? replaces : NULL,
>                   speed, granularity, buf_size, sync,
>                   on_source_error, on_target_error,
> +                 unmap,
>                   block_job_cb, bs, &local_err);
>      if (local_err != NULL) {
>          bdrv_unref(target_bs);
> diff --git a/hmp.c b/hmp.c
> index e17852d..62c53e0 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -1056,7 +1056,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
>                       false, NULL, false, NULL,
>                       full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
>                       true, mode, false, 0, false, 0, false, 0,
> -                     false, 0, false, 0, &err);
> +                     false, 0, false, 0, false, true, &err);
>      hmp_handle_error(mon, &err);
>  }
>  
> diff --git a/include/block/block_int.h b/include/block/block_int.h
> index f004378..4e0d700 100644
> --- a/include/block/block_int.h
> +++ b/include/block/block_int.h
> @@ -590,6 +590,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
>   * @mode: Whether to collapse all images in the chain to the target.
>   * @on_source_error: The action to take upon error reading from the source.
>   * @on_target_error: The action to take upon error writing to the target.
> + * @unmap: Whether to unmap target where source sectors only contain zeroes.
>   * @cb: Completion function for the job.
>   * @opaque: Opaque pointer value passed to @cb.
>   * @errp: Error object.
> @@ -604,6 +605,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
>                    int64_t speed, uint32_t granularity, int64_t buf_size,
>                    MirrorSyncMode mode, BlockdevOnError on_source_error,
>                    BlockdevOnError on_target_error,
> +                  bool unmap,
>                    BlockCompletionFunc *cb,
>                    void *opaque, Error **errp);
>  
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 863ffea..a59063d 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -954,6 +954,11 @@
>  # @on-target-error: #optional the action to take on an error on the target,
>  #                   default 'report' (no limitations, since this applies to
>  #                   a different block device than @device).
> +# @unmap: #optional Whether to try to unmap target sectors where source has
> +#         only zero. If true, and target unallocated sectors will read as zero,
> +#         target image sectors will be unmapped; otherwise, zeroes will be
> +#         written. Both will result in identical contents.
> +#         Default is true. (Since 2.4)
>  #
>  # Returns: nothing on success
>  #          If @device is not a valid block device, DeviceNotFound
> @@ -966,7 +971,8 @@
>              'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
>              '*speed': 'int', '*granularity': 'uint32',
>              '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
> -            '*on-target-error': 'BlockdevOnError' } }
> +            '*on-target-error': 'BlockdevOnError',
> +            '*unmap': 'bool' } }
>  
>  ##
>  # @BlockDirtyBitmap
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 14e109e..63c86fc 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -1503,6 +1503,7 @@ EQMP
>          .args_type  = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
>                        "node-name:s?,replaces:s?,"
>                        "on-source-error:s?,on-target-error:s?,"
> +                      "unmap:b?,"
>                        "granularity:i?,buf-size:i?",
>          .mhandler.cmd_new = qmp_marshal_input_drive_mirror,
>      },
> @@ -1542,6 +1543,8 @@ Arguments:
>    (BlockdevOnError, default 'report')
>  - "on-target-error": the action to take on an error on the target
>    (BlockdevOnError, default 'report')
> +- "unmap": whether the target sectors should be discarded where source has only
> +  zeroes. (json-bool, optional, default true)
>  
>  The default value of the granularity is the image cluster size clamped
>  between 4096 and 65536, if the image format defines one.  If the format
>
Eric Blake June 2, 2015, 5:28 p.m. UTC | #2
On 05/27/2015 11:29 PM, Fam Zheng wrote:
> If specified as "true", it allows discarding on target sectors where source is
> not allocated.
> 
> Signed-off-by: Fam Zheng <famz@redhat.com>
> ---

> +++ b/qapi/block-core.json
> @@ -954,6 +954,11 @@
>  # @on-target-error: #optional the action to take on an error on the target,
>  #                   default 'report' (no limitations, since this applies to
>  #                   a different block device than @device).
> +# @unmap: #optional Whether to try to unmap target sectors where source has
> +#         only zero. If true, and target unallocated sectors will read as zero,
> +#         target image sectors will be unmapped; otherwise, zeroes will be
> +#         written. Both will result in identical contents.
> +#         Default is true. (Since 2.4)

Just making sure I understand:

The guest sees identical contents, but with "unmap":true, the host file
is potentially sparse, while with "unmap":false, the host file is
fully-allocated.

Also, while the default is now true, this doesn't tell me what the
behavior was in 2.3.  Is this a new default behavior (where in 2.3 you
could not preserve sparseness), or a new knob (previously you always got
a sparse copy, and could not request full allocation)?  I'm okay either
way, but I'm trying to understand whether libvirt should advertise this
knob to higher-level apps, and if so, what libvirt should do when it
detects qemu 2.3 (that is, should it tell upper-level apps that
sparseness cannot be preserved, or that full allocation cannot be
guaranteed, when the "unmap" parameter is not detected).
Fam Zheng June 3, 2015, 7:13 a.m. UTC | #3
On Tue, 06/02 11:28, Eric Blake wrote:
> On 05/27/2015 11:29 PM, Fam Zheng wrote:
> > If specified as "true", it allows discarding on target sectors where source is
> > not allocated.
> > 
> > Signed-off-by: Fam Zheng <famz@redhat.com>
> > ---
> 
> > +++ b/qapi/block-core.json
> > @@ -954,6 +954,11 @@
> >  # @on-target-error: #optional the action to take on an error on the target,
> >  #                   default 'report' (no limitations, since this applies to
> >  #                   a different block device than @device).
> > +# @unmap: #optional Whether to try to unmap target sectors where source has
> > +#         only zero. If true, and target unallocated sectors will read as zero,
> > +#         target image sectors will be unmapped; otherwise, zeroes will be
> > +#         written. Both will result in identical contents.
> > +#         Default is true. (Since 2.4)
> 
> Just making sure I understand:
> 
> The guest sees identical contents, but with "unmap":true, the host file
> is potentially sparse, while with "unmap":false, the host file is
> fully-allocated.
> 
> Also, while the default is now true, this doesn't tell me what the
> behavior was in 2.3.  Is this a new default behavior (where in 2.3 you
> could not preserve sparseness), or a new knob (previously you always got
> a sparse copy, and could not request full allocation)?  I'm okay either
> way, but I'm trying to understand whether libvirt should advertise this
> knob to higher-level apps, and if so, what libvirt should do when it
> detects qemu 2.3 (that is, should it tell upper-level apps that
> sparseness cannot be preserved, or that full allocation cannot be
> guaranteed, when the "unmap" parameter is not detected).

We always skip sectors which are initially unallocated (at the time of mirror
job starting), actually this even stays true for both unmap=true and
unmap=false now.

The difference is how we handle sectors discarded *after* mirror job starts:

Before, we ignore the *discard*, which means the target remains populated, with
old data before discard.

After, we honor the discard, depending on two factors:

    source read as zero    unmap                 RESULT
==========================================================================

           Y               true       write zero with BDRV_REQ_MAY_UNMAP

           Y               false      write zero without BDRV_REQ_MAY_UNMAP

           N               both       discard (note that on some protocols
                                      this may be nop)

In other words, the unmap option only affects sector X if:

1) in the beginning, sector X was allocated
2) drive-mirror starts
3) sector X got discarded at source side

All in all, this is quite advisory.

Fam
diff mbox

Patch

diff --git a/block/mirror.c b/block/mirror.c
index 58f391a..85995b2 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -57,6 +57,7 @@  typedef struct MirrorBlockJob {
     int in_flight;
     int sectors_in_flight;
     int ret;
+    bool unmap;
 } MirrorBlockJob;
 
 typedef struct MirrorOp {
@@ -651,6 +652,7 @@  static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
                              int64_t buf_size,
                              BlockdevOnError on_source_error,
                              BlockdevOnError on_target_error,
+                             bool unmap,
                              BlockCompletionFunc *cb,
                              void *opaque, Error **errp,
                              const BlockJobDriver *driver,
@@ -703,6 +705,7 @@  void mirror_start(BlockDriverState *bs, BlockDriverState *target,
                   int64_t speed, uint32_t granularity, int64_t buf_size,
                   MirrorSyncMode mode, BlockdevOnError on_source_error,
                   BlockdevOnError on_target_error,
+                  bool unmap,
                   BlockCompletionFunc *cb,
                   void *opaque, Error **errp)
 {
@@ -717,7 +720,7 @@  void mirror_start(BlockDriverState *bs, BlockDriverState *target,
     base = mode == MIRROR_SYNC_MODE_TOP ? bs->backing_hd : NULL;
     mirror_start_job(bs, target, replaces,
                      speed, granularity, buf_size,
-                     on_source_error, on_target_error, cb, opaque, errp,
+                     on_source_error, on_target_error, unmap, cb, opaque, errp,
                      &mirror_job_driver, is_none_mode, base);
 }
 
@@ -765,7 +768,7 @@  void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
 
     bdrv_ref(base);
     mirror_start_job(bs, base, NULL, speed, 0, 0,
-                     on_error, on_error, cb, opaque, &local_err,
+                     on_error, on_error, false, cb, opaque, &local_err,
                      &commit_active_job_driver, false, base);
     if (local_err) {
         error_propagate(errp, local_err);
diff --git a/blockdev.c b/blockdev.c
index 5eaf77e..4387e14 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2632,6 +2632,7 @@  void qmp_drive_mirror(const char *device, const char *target,
                       bool has_buf_size, int64_t buf_size,
                       bool has_on_source_error, BlockdevOnError on_source_error,
                       bool has_on_target_error, BlockdevOnError on_target_error,
+                      bool has_unmap, bool unmap,
                       Error **errp)
 {
     BlockBackend *blk;
@@ -2663,6 +2664,9 @@  void qmp_drive_mirror(const char *device, const char *target,
     if (!has_buf_size) {
         buf_size = DEFAULT_MIRROR_BUF_SIZE;
     }
+    if (!has_unmap) {
+        unmap = true;
+    }
 
     if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
         error_set(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
@@ -2802,6 +2806,7 @@  void qmp_drive_mirror(const char *device, const char *target,
                  has_replaces ? replaces : NULL,
                  speed, granularity, buf_size, sync,
                  on_source_error, on_target_error,
+                 unmap,
                  block_job_cb, bs, &local_err);
     if (local_err != NULL) {
         bdrv_unref(target_bs);
diff --git a/hmp.c b/hmp.c
index e17852d..62c53e0 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1056,7 +1056,7 @@  void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
                      false, NULL, false, NULL,
                      full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
                      true, mode, false, 0, false, 0, false, 0,
-                     false, 0, false, 0, &err);
+                     false, 0, false, 0, false, true, &err);
     hmp_handle_error(mon, &err);
 }
 
diff --git a/include/block/block_int.h b/include/block/block_int.h
index f004378..4e0d700 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -590,6 +590,7 @@  void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
  * @mode: Whether to collapse all images in the chain to the target.
  * @on_source_error: The action to take upon error reading from the source.
  * @on_target_error: The action to take upon error writing to the target.
+ * @unmap: Whether to unmap target where source sectors only contain zeroes.
  * @cb: Completion function for the job.
  * @opaque: Opaque pointer value passed to @cb.
  * @errp: Error object.
@@ -604,6 +605,7 @@  void mirror_start(BlockDriverState *bs, BlockDriverState *target,
                   int64_t speed, uint32_t granularity, int64_t buf_size,
                   MirrorSyncMode mode, BlockdevOnError on_source_error,
                   BlockdevOnError on_target_error,
+                  bool unmap,
                   BlockCompletionFunc *cb,
                   void *opaque, Error **errp);
 
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 863ffea..a59063d 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -954,6 +954,11 @@ 
 # @on-target-error: #optional the action to take on an error on the target,
 #                   default 'report' (no limitations, since this applies to
 #                   a different block device than @device).
+# @unmap: #optional Whether to try to unmap target sectors where source has
+#         only zero. If true, and target unallocated sectors will read as zero,
+#         target image sectors will be unmapped; otherwise, zeroes will be
+#         written. Both will result in identical contents.
+#         Default is true. (Since 2.4)
 #
 # Returns: nothing on success
 #          If @device is not a valid block device, DeviceNotFound
@@ -966,7 +971,8 @@ 
             'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
             '*speed': 'int', '*granularity': 'uint32',
             '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
-            '*on-target-error': 'BlockdevOnError' } }
+            '*on-target-error': 'BlockdevOnError',
+            '*unmap': 'bool' } }
 
 ##
 # @BlockDirtyBitmap
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 14e109e..63c86fc 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -1503,6 +1503,7 @@  EQMP
         .args_type  = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
                       "node-name:s?,replaces:s?,"
                       "on-source-error:s?,on-target-error:s?,"
+                      "unmap:b?,"
                       "granularity:i?,buf-size:i?",
         .mhandler.cmd_new = qmp_marshal_input_drive_mirror,
     },
@@ -1542,6 +1543,8 @@  Arguments:
   (BlockdevOnError, default 'report')
 - "on-target-error": the action to take on an error on the target
   (BlockdevOnError, default 'report')
+- "unmap": whether the target sectors should be discarded where source has only
+  zeroes. (json-bool, optional, default true)
 
 The default value of the granularity is the image cluster size clamped
 between 4096 and 65536, if the image format defines one.  If the format