diff mbox series

[3/9] mirror: implement mirror_change method

Message ID 20230224144825.466375-4-f.ebner@proxmox.com
State New
Headers show
Series mirror: allow switching from background to active mode | expand

Commit Message

Fiona Ebner Feb. 24, 2023, 2:48 p.m. UTC
which allows switching the @copy-mode from 'background' to
'write-blocking'.

Once the job is in active mode, no new writes need to be registered in
the dirty bitmap, because they are synchronously written to the
target. But since the method is called from the monitor and IO might
be happening in an iothread at the same time, a drained section is
needed.

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 fullfilled. Currently, the
amount of information that can be used for those criteria is a bit
limited, so the plan is to extend quering of block jobs to return more
information relevant for mirror.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
---

Sorry, I still haven't fully grasped the drained logic. Is my
rationale for the drained section correct? There also are some yield
points in block/io.c, for example when the driver implements
bdrv_aio_pwritev (file-win32 and null), and the bitmap is only updated
after that. Is that another reason it's required?

 block/mirror.c       | 38 ++++++++++++++++++++++++++++++++++++++
 qapi/block-core.json | 13 ++++++++++++-
 2 files changed, 50 insertions(+), 1 deletion(-)

Comments

Vladimir Sementsov-Ogievskiy March 1, 2023, 3:18 p.m. UTC | #1
On 24.02.23 17:48, Fiona Ebner wrote:
> which allows switching the @copy-mode from 'background' to
> 'write-blocking'.
> 
> Once the job is in active mode, no new writes need to be registered in
> the dirty bitmap, because they are synchronously written to the
> target. But since the method is called from the monitor and IO might
> be happening in an iothread at the same time, a drained section is
> needed.
> 
> 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 fullfilled. Currently, the
> amount of information that can be used for those criteria is a bit
> limited, so the plan is to extend quering of block jobs to return more
> information relevant for mirror.
> 
> Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
> ---
> 
> Sorry, I still haven't fully grasped the drained logic. Is my
> rationale for the drained section correct? There also are some yield
> points in block/io.c, for example when the driver implements
> bdrv_aio_pwritev (file-win32 and null), and the bitmap is only updated
> after that. Is that another reason it's required?
> 

I think your logic is correct. But draining is quite expensive.

Could we avoid it? For example, just count separately in_flight_non_active writes (the writes that are started with copy_mode = BACKGROUND), and disable dirty bitmap when this counter becomes 0.

Or better idea: move the dirty bitmap to the filter, and make it always disabled. Then, we can set it by hand in bdrv_mirror_top_do_write() only when copy_to_target = false.
Fiona Ebner March 2, 2023, 10 a.m. UTC | #2
Am 01.03.23 um 16:18 schrieb Vladimir Sementsov-Ogievskiy:
> On 24.02.23 17:48, Fiona Ebner wrote:
>> which allows switching the @copy-mode from 'background' to
>> 'write-blocking'.
>>
>> Once the job is in active mode, no new writes need to be registered in
>> the dirty bitmap, because they are synchronously written to the
>> target. But since the method is called from the monitor and IO might
>> be happening in an iothread at the same time, a drained section is
>> needed.
>>
>> 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 fullfilled. Currently, the
>> amount of information that can be used for those criteria is a bit
>> limited, so the plan is to extend quering of block jobs to return more
>> information relevant for mirror.
>>
>> Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
>> ---
>>
>> Sorry, I still haven't fully grasped the drained logic. Is my
>> rationale for the drained section correct? There also are some yield
>> points in block/io.c, for example when the driver implements
>> bdrv_aio_pwritev (file-win32 and null), and the bitmap is only updated
>> after that. Is that another reason it's required?
>>
> 
> I think your logic is correct. But draining is quite expensive.
> 
> Could we avoid it? For example, just count separately
> in_flight_non_active writes (the writes that are started with copy_mode
> = BACKGROUND), and disable dirty bitmap when this counter becomes 0.
> 
> Or better idea: move the dirty bitmap to the filter, and make it always
> disabled. Then, we can set it by hand in bdrv_mirror_top_do_write() only
> when copy_to_target = false.
> 
Sounds good to me! I'll try to go for the second approach in the next
version.

Best Regards,
Fiona
diff mbox series

Patch

diff --git a/block/mirror.c b/block/mirror.c
index ca87492fcc..961aaa5cd6 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -1216,6 +1216,43 @@  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;
+    BlockDriverState *bs = s->mirror_top_bs->backing->bs;
+    AioContext *ctx = bdrv_get_aio_context(bs);
+
+    if (s->copy_mode == change_opts->copy_mode) {
+        return;
+    }
+
+    if (s->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING) {
+        error_setg(errp, "Cannot switch away from copy mode 'write-blocking'");
+        return;
+    }
+
+    assert(s->copy_mode == MIRROR_COPY_MODE_BACKGROUND &&
+           change_opts->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING);
+
+    /*
+     * Once the job is in active mode, no new writes need to be registered in
+     * the dirty bitmap, because they are synchronously written to the target.
+     * Ensure the bitmap is up-to-date first, using a drained section.
+     */
+    s->in_drain = true;
+    aio_context_acquire(ctx);
+    bdrv_drained_begin(bs);
+
+    s->copy_mode = MIRROR_COPY_MODE_WRITE_BLOCKING;
+    bdrv_disable_dirty_bitmap(s->dirty_bitmap);
+
+    bdrv_drained_end(bs);
+    aio_context_release(ctx);
+    s->in_drain = false;
+}
+
 static const BlockJobDriver mirror_job_driver = {
     .job_driver = {
         .instance_size          = sizeof(MirrorBlockJob),
@@ -1230,6 +1267,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 = {
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 714cabd49d..f9f464b25a 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2964,6 +2964,17 @@ 
 { 'command': 'block-job-finalize', 'data': { 'id': 'str' },
   'allow-preconfig': true }
 
+##
+# @BlockJobChangeOptionsMirror:
+#
+# @copy-mode: Switch to this copy mode. Currenlty, only the switch from
+#             'background' to 'write-blocking' is implemented.
+#
+# Since: 8.0
+##
+{ 'struct': 'BlockJobChangeOptionsMirror',
+  'data': { 'copy-mode' : 'MirrorCopyMode' } }
+
 ##
 # @BlockJobChangeOptions:
 #
@@ -2978,7 +2989,7 @@ 
 { 'union': 'BlockJobChangeOptions',
   'base': { 'id': 'str', 'type': 'JobType' },
   'discriminator': 'type',
-  'data': {} }
+  'data': { 'mirror': 'BlockJobChangeOptionsMirror' } }
 
 ##
 # @block-job-change: