Qemu's attempts to learn whether a destination file starts life with all zero contents are just a hueristic. There may be cases where the caller is aware of information that qemu cannot learn quickly, in which case telling qemu what to assume about the destination can make the mirror operation faster. Given our existing example of "qemu-img convert --target-is-zero", it is time to expose this override in QMP for blockdev-mirror as well.
Signed-off-by: Eric Blake <ebl...@redhat.com> --- qapi/block-core.json | 9 ++++++++- include/block/block_int-global-state.h | 3 ++- block/mirror.c | 19 +++++++++++++------ blockdev.c | 18 +++++++++++------- tests/unit/test-block-iothread.c | 2 +- 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index b1937780e19..6d6185a336a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2538,6 +2538,12 @@ # disappear from the query list without user intervention. # Defaults to true. (Since 3.1) # +# @target-is-zero: Assume the destination read as all zeroes before +# the mirror started, even if qemu is unable to quickly learn that +# from the destination. Default false, since setting this to true +# when the destination is not already zero can lead to a corrupt +# destination. (Since 9.1) +# # Since: 2.6 # # .. qmp-example:: @@ -2557,7 +2563,8 @@ '*on-target-error': 'BlockdevOnError', '*filter-node-name': 'str', '*copy-mode': 'MirrorCopyMode', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + '*auto-finalize': 'bool', '*auto-dismiss': 'bool', + '*target-is-zero': 'bool'}, 'allow-preconfig': true } ## diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index eb2d92a2261..a2b96f90d44 100644 --- a/include/block/block_int-global-state.h +++ b/include/block/block_int-global-state.h @@ -140,6 +140,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, * @mode: Whether to collapse all images in the chain to the target. * @backing_mode: How to establish the target's backing chain after completion. * @zero_target: Whether the target should be explicitly zero-initialized + * @target_is_zero: Whether the target already is zero-initialized * @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. @@ -159,7 +160,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, int creation_flags, int64_t speed, uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool zero_target, bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, const char *filter_node_name, diff --git a/block/mirror.c b/block/mirror.c index 98da5a6dc27..c0936a31028 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -55,6 +55,8 @@ typedef struct MirrorBlockJob { BlockMirrorBackingMode backing_mode; /* Whether the target image requires explicit zero-initialization */ bool zero_target; + /* Whether the target should be assumed to be already zero initialized */ + bool target_is_zero; /* * To be accesssed with atomics. Written only under the BQL (required by the * current implementation of mirror_change()). @@ -877,7 +879,11 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) bdrv_graph_co_rdlock(); bs = s->mirror_top_bs->backing->bs; if (s->zero_target) { - ret = bdrv_co_is_zero_fast(target_bs, 0, s->bdev_length); + if (s->target_is_zero) { + ret = 1; + } else { + ret = bdrv_co_is_zero_fast(target_bs, 0, s->bdev_length); + } } bdrv_graph_co_rdunlock(); @@ -1780,7 +1786,7 @@ static BlockJob *mirror_start_job( const char *replaces, int64_t speed, uint32_t granularity, int64_t buf_size, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool zero_target, bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, @@ -1949,6 +1955,7 @@ static BlockJob *mirror_start_job( s->is_none_mode = is_none_mode; s->backing_mode = backing_mode; s->zero_target = zero_target; + s->target_is_zero = target_is_zero; qatomic_set(&s->copy_mode, copy_mode); s->base = base; s->base_overlay = bdrv_find_overlay(bs, base); @@ -2077,7 +2084,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, int creation_flags, int64_t speed, uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool zero_target, bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, const char *filter_node_name, @@ -2102,8 +2109,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs, mirror_start_job(job_id, bs, creation_flags, target, replaces, speed, granularity, buf_size, backing_mode, zero_target, - on_source_error, on_target_error, unmap, NULL, NULL, - &mirror_job_driver, is_none_mode, base, false, + target_is_zero, on_source_error, on_target_error, unmap, + NULL, NULL, &mirror_job_driver, is_none_mode, base, false, filter_node_name, true, copy_mode, false, errp); } @@ -2129,7 +2136,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, job = mirror_start_job( job_id, bs, creation_flags, base, NULL, speed, 0, 0, - MIRROR_LEAVE_BACKING_CHAIN, false, + MIRROR_LEAVE_BACKING_CHAIN, false, false, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, diff --git a/blockdev.c b/blockdev.c index 1d1f27cfff6..6f5373991c8 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2798,7 +2798,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, const char *replaces, enum MirrorSyncMode sync, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool zero_target, bool target_is_zero, bool has_speed, int64_t speed, bool has_granularity, uint32_t granularity, bool has_buf_size, int64_t buf_size, @@ -2909,11 +2909,10 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, /* pass the node name to replace to mirror start since it's loose coupling * and will allow to check whether the node still exist at mirror completion */ - mirror_start(job_id, bs, target, - replaces, job_flags, + mirror_start(job_id, bs, target, replaces, job_flags, speed, granularity, buf_size, sync, backing_mode, zero_target, - on_source_error, on_target_error, unmap, filter_node_name, - copy_mode, errp); + target_is_zero, on_source_error, on_target_error, unmap, + filter_node_name, copy_mode, errp); } void qmp_drive_mirror(DriveMirror *arg, Error **errp) @@ -2928,6 +2927,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) int64_t size; const char *format = arg->format; bool zero_target; + bool target_is_zero; int ret; bs = qmp_get_root_bs(arg->device, errp); @@ -3044,6 +3044,8 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && (arg->mode == NEW_IMAGE_MODE_EXISTING || !bdrv_has_zero_init(target_bs))); + target_is_zero = (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS && + bdrv_has_zero_init(target_bs)); bdrv_graph_rdunlock_main_loop(); @@ -3055,7 +3057,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) blockdev_mirror_common(arg->job_id, bs, target_bs, arg->replaces, arg->sync, - backing_mode, zero_target, + backing_mode, zero_target, target_is_zero, arg->has_speed, arg->speed, arg->has_granularity, arg->granularity, arg->has_buf_size, arg->buf_size, @@ -3085,6 +3087,7 @@ void qmp_blockdev_mirror(const char *job_id, bool has_copy_mode, MirrorCopyMode copy_mode, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, + bool has_target_is_zero, bool target_is_zero, Error **errp) { BlockDriverState *bs; @@ -3115,7 +3118,8 @@ void qmp_blockdev_mirror(const char *job_id, blockdev_mirror_common(job_id, bs, target_bs, replaces, sync, backing_mode, - zero_target, has_speed, speed, + zero_target, has_target_is_zero && target_is_zero, + has_speed, speed, has_granularity, granularity, has_buf_size, buf_size, has_on_source_error, on_source_error, diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 8189b32fd52..ffc878d401e 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -755,7 +755,7 @@ static void test_propagate_mirror(void) /* Start a mirror job */ mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0, - MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, + MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, &error_abort); -- 2.49.0