This makes sense when the next commit starts using the extra buffer space to perform many I/O operations asynchronously.
Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- v1->v2: new testcases block/mirror.c | 6 +++--- block_int.h | 5 +++-- blockdev.c | 9 ++++++++- hmp.c | 2 +- qapi-schema.json | 5 ++++- qmp-commands.hx | 4 +++- tests/qemu-iotests/040 | 31 +++++++++++++++++++++++++++++++ tests/qemu-iotests/040.out | 4 ++-- 8 file modificati, 55 inserzioni(+), 11 rimozioni(-) diff --git a/block/mirror.c b/block/mirror.c index fc39621..e6426bb 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -404,8 +404,8 @@ static BlockJobType mirror_job_type = { }; void mirror_start(BlockDriverState *bs, BlockDriverState *target, - int64_t speed, int64_t granularity, MirrorSyncMode mode, - BlockdevOnError on_source_error, + int64_t speed, int64_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp) @@ -443,7 +443,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target, s->target = target; s->mode = mode; s->granularity = granularity; - s->buf_size = granularity; + s->buf_size = MAX(buf_size, granularity); bdrv_set_dirty_tracking(bs, granularity); bdrv_set_enable_write_cache(s->target, true); diff --git a/block_int.h b/block_int.h index 032db74..78d87fa 100644 --- a/block_int.h +++ b/block_int.h @@ -321,6 +321,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base, * @target: Block device to write to. * @speed: The maximum speed, in bytes per second, or 0 for unlimited. * @granularity: The chosen granularity for the dirty bitmap. + * @buf_size: The amount of data that can be in flight at one time. * @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. @@ -334,8 +335,8 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base, * @bs will be switched to read from @target. */ void mirror_start(BlockDriverState *bs, BlockDriverState *target, - int64_t speed, int64_t granularity, MirrorSyncMode mode, - BlockdevOnError on_source_error, + int64_t speed, int64_t granularity, int64_t buf_size, + MirrorSyncMode mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp); diff --git a/blockdev.c b/blockdev.c index c53e861..f001bba 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1117,12 +1117,15 @@ void qmp_block_stream(const char *device, bool has_base, trace_qmp_block_stream(bs, bs->job); } +#define DEFAULT_MIRROR_BUF_SIZE (10 << 20) + void qmp_drive_mirror(const char *device, const char *target, bool has_format, const char *format, enum MirrorSyncMode sync, bool has_mode, enum NewImageMode mode, bool has_speed, int64_t speed, bool has_granularity, uint32_t granularity, + 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, Error **errp) @@ -1151,6 +1154,10 @@ void qmp_drive_mirror(const char *device, const char *target, if (!has_granularity) { granularity = 0; } + if (!has_buf_size) { + buf_size = DEFAULT_MIRROR_BUF_SIZE; + } + if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { error_set(errp, QERR_INVALID_PARAMETER, device); return; @@ -1240,7 +1247,7 @@ void qmp_drive_mirror(const char *device, const char *target, return; } - mirror_start(bs, target_bs, speed, granularity, sync, + mirror_start(bs, target_bs, speed, granularity, buf_size, sync, on_source_error, on_target_error, block_job_cb, bs, &local_err); if (local_err != NULL) { diff --git a/hmp.c b/hmp.c index 12d1413..b518045 100644 --- a/hmp.c +++ b/hmp.c @@ -783,7 +783,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict) qmp_drive_mirror(device, filename, !!format, format, full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP, - true, mode, false, 0, false, 0, + true, mode, false, 0, false, 0, false, 0, false, 0, false, 0, &errp); hmp_handle_error(mon, &errp); } diff --git a/qapi-schema.json b/qapi-schema.json index 1b9a962..653ce1e 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1558,6 +1558,9 @@ # are smaller than that, else the cluster size. Must be a # power of 2 between 512 and 64M. # +# @buf-size: #optional maximum amount of data in flight from source to +# target. +# # @on-source-error: #optional the action to take on an error on the source, # default 'report'. 'stop' and 'enospc' can only be used # if the block device supports io-status (see BlockInfo). @@ -1575,7 +1578,7 @@ 'data': { 'device': 'str', 'target': 'str', '*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', '*speed': 'int', '*granularity': 'uint32', - '*on-source-error': 'BlockdevOnError', + '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError' } } ## diff --git a/qmp-commands.hx b/qmp-commands.hx index e48ef5e..55f5b90 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -909,7 +909,7 @@ EQMP .name = "drive-mirror", .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," "on-source-error:s?,on-target-error:s?," - "granularity:i?", + "buf-size:i?,granularity:i?", .mhandler.cmd_new = qmp_marshal_input_drive_mirror, }, @@ -934,6 +934,8 @@ Arguments: - "speed": maximum speed of the streaming job, in bytes per second (json-int) - "granularity": granularity of the dirty bitmap (json-int, optional) +- "buf_size": maximum amount of data in flight from source to target + (json-int, default 10M) - "sync": what parts of the disk image should be copied to the destination; possibilities include "full" for all the disk, "top" for only the sectors allocated in the topmost image, or "none" to only replicate new I/O diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 39d07a6..9d06365 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -195,6 +195,37 @@ class TestSingleDrive(ImageMirroringTestCase): self.assertTrue(self.compare_images(test_img, target_img), 'target image does not match source after mirroring') + def test_small_buffer(self): + self.assert_no_active_mirrors() + + # A small buffer is rounded up automatically + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + buf_size=4096, target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + + def test_small_buffer2(self): + self.assert_no_active_mirrors() + + qemu_img('create', '-f', iotests.imgfmt, '-o', 'cluster_size=%d,size=%d' + % (TestSingleDrive.image_len, TestSingleDrive.image_len), target_img) + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', + buf_size=65536, mode='existing', target=target_img) + self.assert_qmp(result, 'return', {}) + + self.complete_and_wait() + result = self.vm.qmp('query-block') + self.assert_qmp(result, 'return[0]/inserted/file', target_img) + self.vm.shutdown() + self.assertTrue(self.compare_images(test_img, target_img), + 'target image does not match source after mirroring') + def test_large_cluster(self): self.assert_no_active_mirrors() diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out index 52d796e..4176bb9 100644 --- a/tests/qemu-iotests/040.out +++ b/tests/qemu-iotests/040.out @@ -1,5 +1,5 @@ -................. +................... ---------------------------------------------------------------------- -Ran 17 tests +Ran 19 tests OK -- 1.7.12