* [PATCH v4 00/10] mirror: allow switching from background to active mode @ 2023-10-31 13:54 Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 01/10] blockjob: introduce block-job-change QMP command Fiona Ebner ` (10 more replies) 0 siblings, 11 replies; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov Changes in v4: * add an iotest for the new functionality * set actively_synced to false when setting dirty bitmap in bdrv_mirror_top_do_write * add comments describing requirements for accessing copy_mode and actively_synced field * add global state code annotation and comment about assumptions in mirror_change method * add comment that change callback can be called before the job coroutine is running * fix typo in QAPI description Changes in v3: * unlock the job mutex when calling the new block job driver 'query' handler * squash patch adapting iotest output into patch that changes the output * turn accesses to copy_mode and actively_synced atomic * slightly rework error handling in mirror_change Changes in v2: * move bitmap to filter which allows to avoid draining when changing the copy mode * add patch to determine copy_to_target only once * drop patches returning redundant information upon query * update QEMU version in QAPI * update indentation in QAPI * update indentation in QAPI (like in a937b6aa73 ("qapi: Reformat doc comments to conform to current conventions")) * add patch to adapt iotest output Discussion of v3: https://lists.nongnu.org/archive/html/qemu-devel/2023-10/msg04026.html Discussion of v2: https://lists.nongnu.org/archive/html/qemu-devel/2023-10/msg02290.html Discussion of v1: https://lists.nongnu.org/archive/html/qemu-devel/2023-02/msg07216.html With active mode, the guest write speed is limited by the synchronous writes to the mirror target. For this reason, management applications might want to start out in background mode and only switch to active mode later, when certain conditions are met. This series adds a block-job-change QMP command to achieve that, as well as job-type-specific information when querying block jobs, which can be used to decide when the switch should happen. For now, only the direction background -> active is supported. The information added upon querying is whether the target is actively synced, the total data sent, and the remaining dirty bytes. Initially, I tried to go for a more general 'job-change' command, but to avoid mutual inclusion of block-core.json and job.json, more preparation would be required. More details described here: https://lists.nongnu.org/archive/html/qemu-devel/2023-10/msg02993.html Fiona Ebner (10): blockjob: introduce block-job-change QMP command block/mirror: set actively_synced even after the job is ready block/mirror: move dirty bitmap to filter block/mirror: determine copy_to_target only once mirror: implement mirror_change method qapi/block-core: use JobType for BlockJobInfo's type qapi/block-core: turn BlockJobInfo into a union blockjob: query driver-specific info via a new 'query' driver method mirror: return mirror-specific information upon query iotests: add test for changing mirror's copy_mode block/mirror.c | 131 ++++++++---- block/monitor/block-hmp-cmds.c | 4 +- blockdev.c | 14 ++ blockjob.c | 28 ++- include/block/blockjob.h | 11 + include/block/blockjob_int.h | 12 ++ job.c | 1 + qapi/block-core.json | 59 +++++- qapi/job.json | 4 +- tests/qemu-iotests/109.out | 24 +-- .../tests/mirror-change-copy-mode | 192 ++++++++++++++++++ .../tests/mirror-change-copy-mode.out | 5 + 12 files changed, 429 insertions(+), 56 deletions(-) create mode 100755 tests/qemu-iotests/tests/mirror-change-copy-mode create mode 100644 tests/qemu-iotests/tests/mirror-change-copy-mode.out -- 2.39.2 ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 01/10] blockjob: introduce block-job-change QMP command 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 16:05 ` Eric Blake 2023-10-31 13:54 ` [PATCH v4 02/10] block/mirror: set actively_synced even after the job is ready Fiona Ebner ` (9 subsequent siblings) 10 siblings, 1 reply; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov which will allow changing job-type-specific options after job creation. In the JobVerbTable, the same allow bits as for set-speed are used, because set-speed can be considered an existing change command. Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> --- Changes in v4: * Add note that change callback can be invoked before job coroutine is running. blockdev.c | 14 ++++++++++++++ blockjob.c | 20 ++++++++++++++++++++ include/block/blockjob.h | 11 +++++++++++ include/block/blockjob_int.h | 7 +++++++ job.c | 1 + qapi/block-core.json | 26 ++++++++++++++++++++++++++ qapi/job.json | 4 +++- 7 files changed, 82 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index a01c62596b..639000d7d8 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3382,6 +3382,20 @@ void qmp_block_job_dismiss(const char *id, Error **errp) job_dismiss_locked(&job, errp); } +void qmp_block_job_change(BlockJobChangeOptions *opts, Error **errp) +{ + BlockJob *job; + + JOB_LOCK_GUARD(); + job = find_block_job_locked(opts->id, errp); + + if (!job) { + return; + } + + block_job_change_locked(job, opts, errp); +} + void qmp_change_backing_file(const char *device, const char *image_node_name, const char *backing_file, diff --git a/blockjob.c b/blockjob.c index 807f992b59..11bae119bd 100644 --- a/blockjob.c +++ b/blockjob.c @@ -328,6 +328,26 @@ static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) return block_job_set_speed_locked(job, speed, errp); } +void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) +{ + const BlockJobDriver *drv = block_job_driver(job); + + GLOBAL_STATE_CODE(); + + if (job_apply_verb_locked(&job->job, JOB_VERB_CHANGE, errp)) { + return; + } + + if (drv->change) { + job_unlock(); + drv->change(job, opts, errp); + job_lock(); + } else { + error_setg(errp, "Job type does not support change"); + } +} + void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) { IO_CODE(); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 058b0c824c..95854f1477 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -172,6 +172,17 @@ bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs); */ bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp); +/** + * block_job_change_locked: + * @job: The job to change. + * @opts: The new options. + * @errp: Error object. + * + * Change the job according to opts. + */ +void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp); + /** * block_job_query_locked: * @job: The job to get information about. diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 104824040c..a4656d4cb5 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -67,6 +67,13 @@ struct BlockJobDriver { void (*attached_aio_context)(BlockJob *job, AioContext *new_context); void (*set_speed)(BlockJob *job, int64_t speed); + + /* + * Change the @job's options according to @opts. + * + * Note that this can already be called before the job coroutine is running. + */ + void (*change)(BlockJob *job, BlockJobChangeOptions *opts, Error **errp); }; /* diff --git a/job.c b/job.c index 72d57f0934..99a2e54b54 100644 --- a/job.c +++ b/job.c @@ -80,6 +80,7 @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, + [JOB_VERB_CHANGE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, }; /* Transactional group of jobs */ diff --git a/qapi/block-core.json b/qapi/block-core.json index 89751d81f2..c6f31a9399 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3044,6 +3044,32 @@ { 'command': 'block-job-finalize', 'data': { 'id': 'str' }, 'allow-preconfig': true } +## +# @BlockJobChangeOptions: +# +# Block job options that can be changed after job creation. +# +# @id: The job identifier +# +# @type: The job type +# +# Since 8.2 +## +{ 'union': 'BlockJobChangeOptions', + 'base': { 'id': 'str', 'type': 'JobType' }, + 'discriminator': 'type', + 'data': {} } + +## +# @block-job-change: +# +# Change the block job's options. +# +# Since: 8.2 +## +{ 'command': 'block-job-change', + 'data': 'BlockJobChangeOptions', 'boxed': true } + ## # @BlockdevDiscardOptions: # diff --git a/qapi/job.json b/qapi/job.json index 7f0ba090de..b3957207a4 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -105,11 +105,13 @@ # # @finalize: see @job-finalize # +# @change: see @block-job-change (since 8.2) +# # Since: 2.12 ## { 'enum': 'JobVerb', 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', - 'finalize' ] } + 'finalize', 'change' ] } ## # @JOB_STATUS_CHANGE: -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v4 01/10] blockjob: introduce block-job-change QMP command 2023-10-31 13:54 ` [PATCH v4 01/10] blockjob: introduce block-job-change QMP command Fiona Ebner @ 2023-10-31 16:05 ` Eric Blake 0 siblings, 0 replies; 15+ messages in thread From: Eric Blake @ 2023-10-31 16:05 UTC (permalink / raw) To: Fiona Ebner Cc: qemu-devel, qemu-block, armbru, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov On Tue, Oct 31, 2023 at 02:54:22PM +0100, Fiona Ebner wrote: > which will allow changing job-type-specific options after job > creation. > > In the JobVerbTable, the same allow bits as for set-speed are used, > because set-speed can be considered an existing change command. > > Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> > Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> > --- > > Changes in v4: > * Add note that change callback can be invoked before job coroutine > is running. > > blockdev.c | 14 ++++++++++++++ > blockjob.c | 20 ++++++++++++++++++++ > include/block/blockjob.h | 11 +++++++++++ > include/block/blockjob_int.h | 7 +++++++ > job.c | 1 + > qapi/block-core.json | 26 ++++++++++++++++++++++++++ > qapi/job.json | 4 +++- > 7 files changed, 82 insertions(+), 1 deletion(-) > Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake, Principal Software Engineer Red Hat, Inc. Virtualization: qemu.org | libguestfs.org ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 02/10] block/mirror: set actively_synced even after the job is ready 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 01/10] blockjob: introduce block-job-change QMP command Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 18:05 ` Eric Blake 2023-10-31 13:54 ` [PATCH v4 03/10] block/mirror: move dirty bitmap to filter Fiona Ebner ` (8 subsequent siblings) 10 siblings, 1 reply; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov In preparation to allow switching from background to active mode. This ensures that setting actively_synced will not be missed when the switch happens after the job is ready. Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> --- No changes in v4. block/mirror.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index dcd88de2e3..1c2c00ee1d 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1074,9 +1074,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) * the target in a consistent state. */ job_transition_to_ready(&s->common.job); - if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { - s->actively_synced = true; - } + } + if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { + s->actively_synced = true; } should_complete = s->should_complete || -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v4 02/10] block/mirror: set actively_synced even after the job is ready 2023-10-31 13:54 ` [PATCH v4 02/10] block/mirror: set actively_synced even after the job is ready Fiona Ebner @ 2023-10-31 18:05 ` Eric Blake 0 siblings, 0 replies; 15+ messages in thread From: Eric Blake @ 2023-10-31 18:05 UTC (permalink / raw) To: Fiona Ebner Cc: qemu-devel, qemu-block, armbru, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov On Tue, Oct 31, 2023 at 02:54:23PM +0100, Fiona Ebner wrote: > In preparation to allow switching from background to active mode. This > ensures that setting actively_synced will not be missed when the > switch happens after the job is ready. > > Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> > Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> > --- > > No changes in v4. > > block/mirror.c | 6 +++--- > 1 file changed, 3 insertions(+), 3 deletions(-) Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake, Principal Software Engineer Red Hat, Inc. Virtualization: qemu.org | libguestfs.org ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 03/10] block/mirror: move dirty bitmap to filter 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 01/10] blockjob: introduce block-job-change QMP command Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 02/10] block/mirror: set actively_synced even after the job is ready Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 18:08 ` Eric Blake 2023-10-31 13:54 ` [PATCH v4 04/10] block/mirror: determine copy_to_target only once Fiona Ebner ` (7 subsequent siblings) 10 siblings, 1 reply; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov In preparation to allow switching to active mode without draining. Initialization of the bitmap in mirror_dirty_init() still happens with the original/backing BlockDriverState, which should be fine, because the mirror top has the same length. Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> --- Changes in v4: * Also set actively_synced to false when setting the bitmap in bdrv_mirror_top_do_write. block/mirror.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 1c2c00ee1d..914d723446 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1500,6 +1500,11 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, abort(); } + if (!copy_to_target && s->job && s->job->dirty_bitmap) { + s->job->actively_synced = false; + bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); + } + if (ret < 0) { goto out; } @@ -1823,13 +1828,17 @@ static BlockJob *mirror_start_job( s->should_complete = true; } - s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp); + s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity, + NULL, errp); if (!s->dirty_bitmap) { goto fail; } - if (s->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING) { - bdrv_disable_dirty_bitmap(s->dirty_bitmap); - } + + /* + * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in active + * mode. + */ + bdrv_disable_dirty_bitmap(s->dirty_bitmap); ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v4 03/10] block/mirror: move dirty bitmap to filter 2023-10-31 13:54 ` [PATCH v4 03/10] block/mirror: move dirty bitmap to filter Fiona Ebner @ 2023-10-31 18:08 ` Eric Blake 0 siblings, 0 replies; 15+ messages in thread From: Eric Blake @ 2023-10-31 18:08 UTC (permalink / raw) To: Fiona Ebner Cc: qemu-devel, qemu-block, armbru, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov On Tue, Oct 31, 2023 at 02:54:24PM +0100, Fiona Ebner wrote: > In preparation to allow switching to active mode without draining. > Initialization of the bitmap in mirror_dirty_init() still happens with > the original/backing BlockDriverState, which should be fine, because > the mirror top has the same length. > > Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> > Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> > --- > > Changes in v4: > * Also set actively_synced to false when setting the bitmap in > bdrv_mirror_top_do_write. > > block/mirror.c | 17 +++++++++++++---- > 1 file changed, 13 insertions(+), 4 deletions(-) > Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake, Principal Software Engineer Red Hat, Inc. Virtualization: qemu.org | libguestfs.org ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4 04/10] block/mirror: determine copy_to_target only once 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner ` (2 preceding siblings ...) 2023-10-31 13:54 ` [PATCH v4 03/10] block/mirror: move dirty bitmap to filter Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 05/10] mirror: implement mirror_change method Fiona Ebner ` (6 subsequent siblings) 10 siblings, 0 replies; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov In preparation to allow changing the copy_mode via QMP. When running in an iothread, it could be that copy_mode is changed from the main thread in between reading copy_mode in bdrv_mirror_top_pwritev() and reading copy_mode in bdrv_mirror_top_do_write(), so they might end up disagreeing about whether copy_to_target is true or false. Avoid that scenario by determining copy_to_target only once and passing it to bdrv_mirror_top_do_write() as an argument. Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> --- No changes in v4. block/mirror.c | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 914d723446..31da1526eb 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1463,21 +1463,21 @@ bdrv_mirror_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } +static bool should_copy_to_target(MirrorBDSOpaque *s) +{ + return s->job && s->job->ret >= 0 && + !job_is_cancelled(&s->job->common.job) && + s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; +} + static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, - uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, - int flags) + bool copy_to_target, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { MirrorOp *op = NULL; MirrorBDSOpaque *s = bs->opaque; int ret = 0; - bool copy_to_target = false; - - if (s->job) { - copy_to_target = s->job->ret >= 0 && - !job_is_cancelled(&s->job->common.job) && - s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; - } if (copy_to_target) { op = active_write_prepare(s->job, offset, bytes); @@ -1524,17 +1524,10 @@ static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) { - MirrorBDSOpaque *s = bs->opaque; QEMUIOVector bounce_qiov; void *bounce_buf; int ret = 0; - bool copy_to_target = false; - - if (s->job) { - copy_to_target = s->job->ret >= 0 && - !job_is_cancelled(&s->job->common.job) && - s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; - } + bool copy_to_target = should_copy_to_target(bs->opaque); if (copy_to_target) { /* The guest might concurrently modify the data to write; but @@ -1551,8 +1544,8 @@ bdrv_mirror_top_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, flags &= ~BDRV_REQ_REGISTERED_BUF; } - ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, offset, bytes, qiov, - flags); + ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, copy_to_target, + offset, bytes, qiov, flags); if (copy_to_target) { qemu_iovec_destroy(&bounce_qiov); @@ -1575,15 +1568,17 @@ static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, BdrvRequestFlags flags) { - return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, offset, bytes, NULL, - flags); + bool copy_to_target = should_copy_to_target(bs->opaque); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, copy_to_target, + offset, bytes, NULL, flags); } static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) { - return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, offset, bytes, - NULL, 0); + bool copy_to_target = should_copy_to_target(bs->opaque); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, copy_to_target, + offset, bytes, NULL, 0); } static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs) -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 05/10] mirror: implement mirror_change method 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner ` (3 preceding siblings ...) 2023-10-31 13:54 ` [PATCH v4 04/10] block/mirror: determine copy_to_target only once Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 06/10] qapi/block-core: use JobType for BlockJobInfo's type Fiona Ebner ` (5 subsequent siblings) 10 siblings, 0 replies; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov which allows switching the @copy-mode from 'background' to 'write-blocking'. This is useful for management applications, so they can start out in background mode to avoid limiting guest write speed and switch to active mode when certain criteria are fulfilled. In presence of an iothread, the copy_mode member is now shared between the iothread and the main thread, so turn accesses to it atomic. Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> --- Changes in v4: * add comment about how copy_mode should be accessed * add GLOBAL_STATE_CODE annotation for mirror_change * use two spaces in between sentences in QAPI description * fix typo in QAPI description block/mirror.c | 44 +++++++++++++++++++++++++++++++++++++++++--- qapi/block-core.json | 13 ++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 31da1526eb..4016d89253 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -55,6 +55,10 @@ typedef struct MirrorBlockJob { BlockMirrorBackingMode backing_mode; /* Whether the target image requires explicit zero-initialization */ bool zero_target; + /* + * To be accesssed with atomics. Written only under the BQL (required by the + * current implementation of mirror_change()). + */ MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; /* Set when the target is synced (dirty bitmap is clean, nothing @@ -1075,7 +1079,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) */ job_transition_to_ready(&s->common.job); } - if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { + if (qatomic_read(&s->copy_mode) != MIRROR_COPY_MODE_BACKGROUND) { s->actively_synced = true; } @@ -1246,6 +1250,39 @@ static bool commit_active_cancel(Job *job, bool force) return force || !job_is_ready(job); } +static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + BlockJobChangeOptionsMirror *change_opts = &opts->u.mirror; + MirrorCopyMode current; + + /* + * The implementation relies on the fact that copy_mode is only written + * under the BQL. Otherwise, further synchronization would be required. + */ + + GLOBAL_STATE_CODE(); + + if (qatomic_read(&s->copy_mode) == change_opts->copy_mode) { + return; + } + + if (change_opts->copy_mode != MIRROR_COPY_MODE_WRITE_BLOCKING) { + error_setg(errp, "Change to copy mode '%s' is not implemented", + MirrorCopyMode_str(change_opts->copy_mode)); + return; + } + + current = qatomic_cmpxchg(&s->copy_mode, MIRROR_COPY_MODE_BACKGROUND, + change_opts->copy_mode); + if (current != MIRROR_COPY_MODE_BACKGROUND) { + error_setg(errp, "Expected current copy mode '%s', got '%s'", + MirrorCopyMode_str(MIRROR_COPY_MODE_BACKGROUND), + MirrorCopyMode_str(current)); + } +} + static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), @@ -1260,6 +1297,7 @@ static const BlockJobDriver mirror_job_driver = { .cancel = mirror_cancel, }, .drained_poll = mirror_drained_poll, + .change = mirror_change, }; static const BlockJobDriver commit_active_job_driver = { @@ -1467,7 +1505,7 @@ static bool should_copy_to_target(MirrorBDSOpaque *s) { return s->job && s->job->ret >= 0 && !job_is_cancelled(&s->job->common.job) && - s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; + qatomic_read(&s->job->copy_mode) == MIRROR_COPY_MODE_WRITE_BLOCKING; } static int coroutine_fn GRAPH_RDLOCK @@ -1813,7 +1851,7 @@ static BlockJob *mirror_start_job( s->is_none_mode = is_none_mode; s->backing_mode = backing_mode; s->zero_target = zero_target; - s->copy_mode = copy_mode; + qatomic_set(&s->copy_mode, copy_mode); s->base = base; s->base_overlay = bdrv_find_overlay(bs, base); s->granularity = granularity; diff --git a/qapi/block-core.json b/qapi/block-core.json index c6f31a9399..6369207be2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3044,6 +3044,17 @@ { 'command': 'block-job-finalize', 'data': { 'id': 'str' }, 'allow-preconfig': true } +## +# @BlockJobChangeOptionsMirror: +# +# @copy-mode: Switch to this copy mode. Currently, only the switch +# from 'background' to 'write-blocking' is implemented. +# +# Since: 8.2 +## +{ 'struct': 'BlockJobChangeOptionsMirror', + 'data': { 'copy-mode' : 'MirrorCopyMode' } } + ## # @BlockJobChangeOptions: # @@ -3058,7 +3069,7 @@ { 'union': 'BlockJobChangeOptions', 'base': { 'id': 'str', 'type': 'JobType' }, 'discriminator': 'type', - 'data': {} } + 'data': { 'mirror': 'BlockJobChangeOptionsMirror' } } ## # @block-job-change: -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 06/10] qapi/block-core: use JobType for BlockJobInfo's type 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner ` (4 preceding siblings ...) 2023-10-31 13:54 ` [PATCH v4 05/10] mirror: implement mirror_change method Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 07/10] qapi/block-core: turn BlockJobInfo into a union Fiona Ebner ` (4 subsequent siblings) 10 siblings, 0 replies; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov In preparation to turn BlockJobInfo into a union with @type as the discriminator. That requires it to be an enum. Even without that requirement, it's nicer to have an enum instead of a str here. No functional change is intended. Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> Reviewed-by: Markus Armbruster <armbru@redhat.com> --- Changes in v4: * Mention that it's nicer to have an enum regardless in commit message. block/monitor/block-hmp-cmds.c | 4 ++-- blockjob.c | 2 +- qapi/block-core.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 7645c7e5fb..5b2c597e7a 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -846,7 +846,7 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) } while (list) { - if (strcmp(list->value->type, "stream") == 0) { + if (list->value->type == JOB_TYPE_STREAM) { monitor_printf(mon, "Streaming device %s: Completed %" PRId64 " of %" PRId64 " bytes, speed limit %" PRId64 " bytes/s\n", @@ -858,7 +858,7 @@ void hmp_info_block_jobs(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Type %s, device %s: Completed %" PRId64 " of %" PRId64 " bytes, speed limit %" PRId64 " bytes/s\n", - list->value->type, + JobType_str(list->value->type), list->value->device, list->value->offset, list->value->len, diff --git a/blockjob.c b/blockjob.c index 11bae119bd..9665b02627 100644 --- a/blockjob.c +++ b/blockjob.c @@ -388,7 +388,7 @@ BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) &progress_total); info = g_new0(BlockJobInfo, 1); - info->type = g_strdup(job_type_str(&job->job)); + info->type = job_type(&job->job); info->device = g_strdup(job->job.id); info->busy = job->job.busy; info->paused = job->job.pause_count > 0; diff --git a/qapi/block-core.json b/qapi/block-core.json index 6369207be2..9d03210664 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1396,7 +1396,7 @@ # Since: 1.1 ## { 'struct': 'BlockJobInfo', - 'data': {'type': 'str', 'device': 'str', 'len': 'int', + 'data': {'type': 'JobType', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', 'status': 'JobStatus', -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 07/10] qapi/block-core: turn BlockJobInfo into a union 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner ` (5 preceding siblings ...) 2023-10-31 13:54 ` [PATCH v4 06/10] qapi/block-core: use JobType for BlockJobInfo's type Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 08/10] blockjob: query driver-specific info via a new 'query' driver method Fiona Ebner ` (3 subsequent siblings) 10 siblings, 0 replies; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov In preparation to additionally return job-type-specific information. Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru> --- No changes in v4. qapi/block-core.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 9d03210664..dca0e94bb0 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1395,13 +1395,15 @@ # # Since: 1.1 ## -{ 'struct': 'BlockJobInfo', - 'data': {'type': 'JobType', 'device': 'str', 'len': 'int', +{ 'union': 'BlockJobInfo', + 'base': {'type': 'JobType', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', 'status': 'JobStatus', 'auto-finalize': 'bool', 'auto-dismiss': 'bool', - '*error': 'str' } } + '*error': 'str' }, + 'discriminator': 'type', + 'data': {} } ## # @query-block-jobs: -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 08/10] blockjob: query driver-specific info via a new 'query' driver method 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner ` (6 preceding siblings ...) 2023-10-31 13:54 ` [PATCH v4 07/10] qapi/block-core: turn BlockJobInfo into a union Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 09/10] mirror: return mirror-specific information upon query Fiona Ebner ` (2 subsequent siblings) 10 siblings, 0 replies; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> --- No changes in v4. blockjob.c | 6 ++++++ include/block/blockjob_int.h | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/blockjob.c b/blockjob.c index 9665b02627..41719dcf7d 100644 --- a/blockjob.c +++ b/blockjob.c @@ -376,6 +376,7 @@ BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) { BlockJobInfo *info; uint64_t progress_current, progress_total; + const BlockJobDriver *drv = block_job_driver(job); GLOBAL_STATE_CODE(); @@ -405,6 +406,11 @@ BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) g_strdup(error_get_pretty(job->job.err)) : g_strdup(strerror(-job->job.ret)); } + if (drv->query) { + job_unlock(); + drv->query(job, info); + job_lock(); + } return info; } diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index a4656d4cb5..18ee6f7bf0 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -74,6 +74,11 @@ struct BlockJobDriver { * Note that this can already be called before the job coroutine is running. */ void (*change)(BlockJob *job, BlockJobChangeOptions *opts, Error **errp); + + /* + * Query information specific to this kind of block job. + */ + void (*query)(BlockJob *job, BlockJobInfo *info); }; /* -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 09/10] mirror: return mirror-specific information upon query 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner ` (7 preceding siblings ...) 2023-10-31 13:54 ` [PATCH v4 08/10] blockjob: query driver-specific info via a new 'query' driver method Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 10/10] iotests: add test for changing mirror's copy_mode Fiona Ebner 2023-10-31 18:05 ` [PATCH v4 00/10] mirror: allow switching from background to active mode Kevin Wolf 10 siblings, 0 replies; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov To start out, only actively-synced is returned. For example, this is useful for jobs that started out in background mode and switched to active mode. Once actively-synced is true, it's clear that the mode switch has been completed. Note that completion of the switch might happen much earlier, e.g. if the switch happens before the job is ready, once all background operations have finished. It's assumed that whether the disks are actively-synced or not is more interesting than whether the mode switch completed. That information can still be added if required in the future. In presence of an iothread, the actively_synced member is now shared between the iothread and the main thread, so turn accesses to it atomic. Requires to adapt the output for iotest 109. Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> --- Changes in v4: * mention that actively_synced should be accessed with atomics above field declaration block/mirror.c | 31 +++++++++++++++++++++++-------- qapi/block-core.json | 16 +++++++++++++++- tests/qemu-iotests/109.out | 24 ++++++++++++------------ 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 4016d89253..c839542774 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -61,8 +61,12 @@ typedef struct MirrorBlockJob { */ MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; - /* Set when the target is synced (dirty bitmap is clean, nothing - * in flight) and the job is running in active mode */ + /* + * To be accessed with atomics. + * + * Set when the target is synced (dirty bitmap is clean, nothing in flight) + * and the job is running in active mode. + */ bool actively_synced; bool should_complete; int64_t granularity; @@ -126,7 +130,7 @@ typedef enum MirrorMethod { static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { - s->actively_synced = false; + qatomic_set(&s->actively_synced, false); if (read) { return block_job_error_action(&s->common, s->on_source_error, true, error); @@ -966,7 +970,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) if (s->bdev_length == 0) { /* Transition to the READY state and wait for complete. */ job_transition_to_ready(&s->common.job); - s->actively_synced = true; + qatomic_set(&s->actively_synced, true); while (!job_cancel_requested(&s->common.job) && !s->should_complete) { job_yield(&s->common.job); } @@ -1080,7 +1084,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) job_transition_to_ready(&s->common.job); } if (qatomic_read(&s->copy_mode) != MIRROR_COPY_MODE_BACKGROUND) { - s->actively_synced = true; + qatomic_set(&s->actively_synced, true); } should_complete = s->should_complete || @@ -1283,6 +1287,15 @@ static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, } } +static void mirror_query(BlockJob *job, BlockJobInfo *info) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + + info->u.mirror = (BlockJobInfoMirror) { + .actively_synced = qatomic_read(&s->actively_synced), + }; +} + static const BlockJobDriver mirror_job_driver = { .job_driver = { .instance_size = sizeof(MirrorBlockJob), @@ -1298,6 +1311,7 @@ static const BlockJobDriver mirror_job_driver = { }, .drained_poll = mirror_drained_poll, .change = mirror_change, + .query = mirror_query, }; static const BlockJobDriver commit_active_job_driver = { @@ -1416,7 +1430,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset, bitmap_end - bitmap_offset); - job->actively_synced = false; + qatomic_set(&job->actively_synced, false); action = mirror_error_action(job, false, -ret); if (action == BLOCK_ERROR_ACTION_REPORT) { @@ -1475,7 +1489,8 @@ static void coroutine_fn GRAPH_RDLOCK active_write_settle(MirrorOp *op) uint64_t end_chunk = DIV_ROUND_UP(op->offset + op->bytes, op->s->granularity); - if (!--op->s->in_active_write_counter && op->s->actively_synced) { + if (!--op->s->in_active_write_counter && + qatomic_read(&op->s->actively_synced)) { BdrvChild *source = op->s->mirror_top_bs->backing; if (QLIST_FIRST(&source->bs->parents) == source && @@ -1539,7 +1554,7 @@ bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, } if (!copy_to_target && s->job && s->job->dirty_bitmap) { - s->job->actively_synced = false; + qatomic_set(&s->job->actively_synced, false); bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); } diff --git a/qapi/block-core.json b/qapi/block-core.json index dca0e94bb0..99961256f2 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1352,6 +1352,20 @@ { 'enum': 'MirrorCopyMode', 'data': ['background', 'write-blocking'] } +## +# @BlockJobInfoMirror: +# +# Information specific to mirror block jobs. +# +# @actively-synced: Whether the source is actively synced to the +# target, i.e. same data and new writes are done synchronously to +# both. +# +# Since 8.2 +## +{ 'struct': 'BlockJobInfoMirror', + 'data': { 'actively-synced': 'bool' } } + ## # @BlockJobInfo: # @@ -1403,7 +1417,7 @@ 'auto-finalize': 'bool', 'auto-dismiss': 'bool', '*error': 'str' }, 'discriminator': 'type', - 'data': {} } + 'data': { 'mirror': 'BlockJobInfoMirror' } } ## # @query-block-jobs: diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 2611d6a40f..965c9a6a0a 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -38,7 +38,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -90,7 +90,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -142,7 +142,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -194,7 +194,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -246,7 +246,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -298,7 +298,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -349,7 +349,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -400,7 +400,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -451,7 +451,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -502,7 +502,7 @@ read 512/512 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -533,7 +533,7 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} @@ -557,7 +557,7 @@ Images are identical. {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"execute":"query-block-jobs"} -{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} +{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror", "actively-synced": false}]} {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v4 10/10] iotests: add test for changing mirror's copy_mode 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner ` (8 preceding siblings ...) 2023-10-31 13:54 ` [PATCH v4 09/10] mirror: return mirror-specific information upon query Fiona Ebner @ 2023-10-31 13:54 ` Fiona Ebner 2023-10-31 18:05 ` [PATCH v4 00/10] mirror: allow switching from background to active mode Kevin Wolf 10 siblings, 0 replies; 15+ messages in thread From: Fiona Ebner @ 2023-10-31 13:54 UTC (permalink / raw) To: qemu-devel Cc: qemu-block, armbru, eblake, hreitz, kwolf, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov One part of the test is using a throttled source to ensure that there are no obvious issues when changing the copy_mode while there are ongoing requests (source and target images are compared at the very end). The other part of the test is using a throttled target to ensure that the change to active mode actually happened. This is done by hitting the throttling limit, issuing a synchronous write and then immediately verifying the target side. QSD is used, because otherwise, a synchronous write would hang there. Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> --- New in v4. .../tests/mirror-change-copy-mode | 192 ++++++++++++++++++ .../tests/mirror-change-copy-mode.out | 5 + 2 files changed, 197 insertions(+) create mode 100755 tests/qemu-iotests/tests/mirror-change-copy-mode create mode 100644 tests/qemu-iotests/tests/mirror-change-copy-mode.out diff --git a/tests/qemu-iotests/tests/mirror-change-copy-mode b/tests/qemu-iotests/tests/mirror-change-copy-mode new file mode 100755 index 0000000000..e9fef8c5e9 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-change-copy-mode @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +# group: rw +# +# Test for changing mirror copy mode from background to active +# +# Copyright (C) 2023 Proxmox Server Solutions GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import os +import time + +import iotests +from iotests import qemu_img, qemu_io, QemuStorageDaemon + +iops_target = 8 +iops_source = iops_target * 2 +image_size = 1 * 1024 * 1024 +source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) +target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) +nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') + +class TestMirrorChangeCopyMode(iotests.QMPTestCase): + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, source_img, str(image_size)) + qemu_img('create', '-f', iotests.imgfmt, target_img, str(image_size)) + + self.qsd = QemuStorageDaemon('--nbd-server', + f'addr.type=unix,addr.path={nbd_sock}', + qmp=True) + + self.qsd.cmd('object-add', { + 'qom-type': 'throttle-group', + 'id': 'thrgr-target', + 'limits': { + 'iops-write': iops_target, + 'iops-write-max': iops_target + } + }) + + self.qsd.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': 'throttle', + 'throttle-group': 'thrgr-target', + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': target_img + } + } + }) + + self.qsd.cmd('block-export-add', { + 'id': 'exp0', + 'type': 'nbd', + 'node-name': 'target', + 'writable': True + }) + + self.vm = iotests.VM() + self.vm.add_args('-drive', + f'file={source_img},if=none,format={iotests.imgfmt},' + + f'iops_wr={iops_source},' + + f'iops_wr_max={iops_source},' + + 'id=source') + self.vm.launch() + + self.vm.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': 'nbd', + 'export': 'target', + 'server': { + 'type': 'unix', + 'path': nbd_sock + } + }) + + + def tearDown(self): + self.vm.shutdown() + self.qsd.stop() + self.check_qemu_io_errors() + self.check_images_identical() + os.remove(source_img) + os.remove(target_img) + + # Once the VM is shut down we can parse the log and see if qemu-io ran + # without errors. + def check_qemu_io_errors(self): + self.assertFalse(self.vm.is_running()) + log = self.vm.get_log() + for line in log.split("\n"): + assert not line.startswith("Pattern verification failed") + + def check_images_identical(self): + qemu_img('compare', '-f', iotests.imgfmt, source_img, target_img) + + def start_mirror(self): + self.vm.cmd('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + filter_node_name='mirror-top', + sync='full', + copy_mode='background') + + def test_background_to_active(self): + self.vm.hmp_qemu_io('source', f'write 0 {image_size}') + self.vm.hmp_qemu_io('target', f'write 0 {image_size}') + + self.start_mirror() + + result = self.vm.cmd('query-block-jobs') + assert not result[0]['actively-synced'] + + self.vm.event_wait('BLOCK_JOB_READY') + + result = self.vm.cmd('query-block-jobs') + assert not result[0]['actively-synced'] + + # Start some background requests. + reqs = 4 * iops_source + req_size = image_size // reqs + for i in range(0, reqs): + req = f'aio_write -P 7 {req_size * i} {req_size}' + self.vm.hmp_qemu_io('source', req) + + # Wait for the first few requests. + time.sleep(1) + self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + + result = self.vm.cmd('query-block-jobs') + # There should've been new requests. + assert result[0]['len'] > image_size + # To verify later that not all requests were completed at this point. + len_before_change = result[0]['len'] + + # Change the copy mode while requests are happening. + self.vm.cmd('block-job-change', + id='mirror', + type='mirror', + copy_mode='write-blocking') + + # Wait until image is actively synced. + while True: + time.sleep(0.1) + self.vm.qtest(f'clock_step {100 * 1000 * 1000}') + result = self.vm.cmd('query-block-jobs') + if result[0]['actively-synced']: + break + + # Because of throttling, not all request should've been completed above. + result = self.vm.cmd('query-block-jobs') + assert result[0]['len'] > len_before_change + + # Issue enough requests for a few seconds only touching the first half + # of the image. + reqs = 4 * iops_target + req_size = image_size // 2 // reqs + for i in range(0, reqs): + req = f'aio_write -P 19 {req_size * i} {req_size}' + self.vm.hmp_qemu_io('source', req) + + # Now issue a synchronous write in the second half of the image and + # immediately verify that it was written to the target too. This would + # fail without switching the copy mode. Note that this only produces a + # log line and the actual checking happens during tearDown(). + req_args = f'-P 37 {3 * (image_size // 4)} {req_size}' + self.vm.hmp_qemu_io('source', f'write {req_args}') + self.vm.hmp_qemu_io('target', f'read {req_args}') + + self.vm.cmd('block-job-cancel', device='mirror') + while len(self.vm.cmd('query-block-jobs')) > 0: + time.sleep(0.1) + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2', 'raw'], + supported_protocols=['file']) diff --git a/tests/qemu-iotests/tests/mirror-change-copy-mode.out b/tests/qemu-iotests/tests/mirror-change-copy-mode.out new file mode 100644 index 0000000000..ae1213e6f8 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-change-copy-mode.out @@ -0,0 +1,5 @@ +. +---------------------------------------------------------------------- +Ran 1 tests + +OK -- 2.39.2 ^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v4 00/10] mirror: allow switching from background to active mode 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner ` (9 preceding siblings ...) 2023-10-31 13:54 ` [PATCH v4 10/10] iotests: add test for changing mirror's copy_mode Fiona Ebner @ 2023-10-31 18:05 ` Kevin Wolf 10 siblings, 0 replies; 15+ messages in thread From: Kevin Wolf @ 2023-10-31 18:05 UTC (permalink / raw) To: Fiona Ebner Cc: qemu-devel, qemu-block, armbru, eblake, hreitz, vsementsov, jsnow, den, t.lamprecht, alexander.ivanov Am 31.10.2023 um 14:54 hat Fiona Ebner geschrieben: > Changes in v4: > * add an iotest for the new functionality > * set actively_synced to false when setting dirty bitmap in > bdrv_mirror_top_do_write > * add comments describing requirements for accessing copy_mode and > actively_synced field > * add global state code annotation and comment about assumptions > in mirror_change method > * add comment that change callback can be called before the job > coroutine is running > * fix typo in QAPI description > > Changes in v3: > * unlock the job mutex when calling the new block job driver > 'query' handler > * squash patch adapting iotest output into patch that changes the > output > * turn accesses to copy_mode and actively_synced atomic > * slightly rework error handling in mirror_change > > Changes in v2: > * move bitmap to filter which allows to avoid draining when > changing the copy mode > * add patch to determine copy_to_target only once > * drop patches returning redundant information upon query > * update QEMU version in QAPI > * update indentation in QAPI > * update indentation in QAPI (like in a937b6aa73 ("qapi: Reformat > doc comments to conform to current conventions")) > * add patch to adapt iotest output > > Discussion of v3: > https://lists.nongnu.org/archive/html/qemu-devel/2023-10/msg04026.html > > Discussion of v2: > https://lists.nongnu.org/archive/html/qemu-devel/2023-10/msg02290.html > > Discussion of v1: > https://lists.nongnu.org/archive/html/qemu-devel/2023-02/msg07216.html > > With active mode, the guest write speed is limited by the synchronous > writes to the mirror target. For this reason, management applications > might want to start out in background mode and only switch to active > mode later, when certain conditions are met. This series adds a > block-job-change QMP command to achieve that, as well as > job-type-specific information when querying block jobs, which > can be used to decide when the switch should happen. > > For now, only the direction background -> active is supported. > > The information added upon querying is whether the target is actively > synced, the total data sent, and the remaining dirty bytes. > > Initially, I tried to go for a more general 'job-change' command, but > to avoid mutual inclusion of block-core.json and job.json, more > preparation would be required. More details described here: > https://lists.nongnu.org/archive/html/qemu-devel/2023-10/msg02993.html > > Fiona Ebner (10): > blockjob: introduce block-job-change QMP command > block/mirror: set actively_synced even after the job is ready > block/mirror: move dirty bitmap to filter > block/mirror: determine copy_to_target only once > mirror: implement mirror_change method > qapi/block-core: use JobType for BlockJobInfo's type > qapi/block-core: turn BlockJobInfo into a union > blockjob: query driver-specific info via a new 'query' driver method > mirror: return mirror-specific information upon query > iotests: add test for changing mirror's copy_mode Thanks, applied to the block branch. Kevin ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2023-10-31 18:08 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2023-10-31 13:54 [PATCH v4 00/10] mirror: allow switching from background to active mode Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 01/10] blockjob: introduce block-job-change QMP command Fiona Ebner 2023-10-31 16:05 ` Eric Blake 2023-10-31 13:54 ` [PATCH v4 02/10] block/mirror: set actively_synced even after the job is ready Fiona Ebner 2023-10-31 18:05 ` Eric Blake 2023-10-31 13:54 ` [PATCH v4 03/10] block/mirror: move dirty bitmap to filter Fiona Ebner 2023-10-31 18:08 ` Eric Blake 2023-10-31 13:54 ` [PATCH v4 04/10] block/mirror: determine copy_to_target only once Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 05/10] mirror: implement mirror_change method Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 06/10] qapi/block-core: use JobType for BlockJobInfo's type Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 07/10] qapi/block-core: turn BlockJobInfo into a union Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 08/10] blockjob: query driver-specific info via a new 'query' driver method Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 09/10] mirror: return mirror-specific information upon query Fiona Ebner 2023-10-31 13:54 ` [PATCH v4 10/10] iotests: add test for changing mirror's copy_mode Fiona Ebner 2023-10-31 18:05 ` [PATCH v4 00/10] mirror: allow switching from background to active mode Kevin Wolf
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).