qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] mirror: Freeze backing chain for sync=top
@ 2020-09-02 16:48 Max Reitz
  2020-09-02 16:48 ` [PATCH 1/3] mirror: Set s->base_overlay only if s->base is set Max Reitz
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Max Reitz @ 2020-09-02 16:48 UTC (permalink / raw)
  To: qemu-block; +Cc: Kevin Wolf, John Snow, qemu-devel, Max Reitz

Hi,

On review of the “Deal with filters” series, Kevin noted that mirror
doesn’t freeze its backing chain, even though we keep references to the
base (for sync=top).  That seems like a recipe for disaster, and as the
test added in patch 3 shows, indeed it is.

Patch 1 could also be squashed into “mirror: Deal with filters”.

As for patch 2; maybe it would be sufficient to freeze the backing chain
only down to base_overlay and until mirror_dirty_init() is done.  Then
we bdrv_ref(s->base) so can always be attached to the target when mirror
completes.  But I think the conservative approach I implemented here is
fine until someone complains.


Max Reitz (3):
  mirror: Set s->base_overlay only if s->base is set
  mirror: Freeze backing chain for sync=top
  iotests/041: x-blockdev-reopen during mirror

 block/mirror.c             | 28 +++++++++---
 tests/qemu-iotests/041     | 92 ++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/041.out |  4 +-
 3 files changed, 116 insertions(+), 8 deletions(-)

-- 
2.26.2



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/3] mirror: Set s->base_overlay only if s->base is set
  2020-09-02 16:48 [PATCH 0/3] mirror: Freeze backing chain for sync=top Max Reitz
@ 2020-09-02 16:48 ` Max Reitz
  2020-09-02 16:48 ` [PATCH 2/3] mirror: Freeze backing chain for sync=top Max Reitz
  2020-09-02 16:48 ` [PATCH 3/3] iotests/041: x-blockdev-reopen during mirror Max Reitz
  2 siblings, 0 replies; 5+ messages in thread
From: Max Reitz @ 2020-09-02 16:48 UTC (permalink / raw)
  To: qemu-block; +Cc: Kevin Wolf, John Snow, qemu-devel, Max Reitz

This way, sync=full will not need a reference to any node other than the
source and the target.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/mirror.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/block/mirror.c b/block/mirror.c
index 26acf4af6f..11ebffdf99 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -42,6 +42,7 @@ typedef struct MirrorBlockJob {
     BlockBackend *target;
     BlockDriverState *mirror_top_bs;
     BlockDriverState *base;
+    /* Overlay of @base if @base is non-NULL; NULL otherwise */
     BlockDriverState *base_overlay;
 
     /* The name of the graph node to replace */
@@ -839,8 +840,9 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
             return 0;
         }
 
-        ret = bdrv_is_allocated_above(bs, s->base_overlay, true, offset, bytes,
-                                      &count);
+        ret = bdrv_is_allocated_above(bs, s->base_overlay,
+                                      s->base_overlay != NULL,
+                                      offset, bytes, &count);
         if (ret < 0) {
             return ret;
         }
@@ -1710,7 +1712,7 @@ static BlockJob *mirror_start_job(
     s->zero_target = zero_target;
     s->copy_mode = copy_mode;
     s->base = base;
-    s->base_overlay = bdrv_find_overlay(bs, base);
+    s->base_overlay = base ? bdrv_find_overlay(bs, base) : NULL;
     s->granularity = granularity;
     s->buf_size = ROUND_UP(buf_size, granularity);
     s->unmap = unmap;
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/3] mirror: Freeze backing chain for sync=top
  2020-09-02 16:48 [PATCH 0/3] mirror: Freeze backing chain for sync=top Max Reitz
  2020-09-02 16:48 ` [PATCH 1/3] mirror: Set s->base_overlay only if s->base is set Max Reitz
@ 2020-09-02 16:48 ` Max Reitz
  2020-09-02 17:00   ` Max Reitz
  2020-09-02 16:48 ` [PATCH 3/3] iotests/041: x-blockdev-reopen during mirror Max Reitz
  2 siblings, 1 reply; 5+ messages in thread
From: Max Reitz @ 2020-09-02 16:48 UTC (permalink / raw)
  To: qemu-block; +Cc: Kevin Wolf, John Snow, qemu-devel, Max Reitz

Reported-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/mirror.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/block/mirror.c b/block/mirror.c
index 11ebffdf99..27422ab7a5 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -649,8 +649,8 @@ static int mirror_exit_common(Job *job)
     src = mirror_top_bs->backing->bs;
     target_bs = blk_bs(s->target);
 
-    if (bdrv_chain_contains(src, target_bs)) {
-        bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
+    if (s->base) {
+        bdrv_unfreeze_backing_chain(mirror_top_bs, s->base);
     }
 
     bdrv_release_dirty_bitmap(s->dirty_bitmap);
@@ -1780,8 +1780,22 @@ static BlockJob *mirror_start_job(
                 goto fail;
             }
         }
+    }
+
+    if (s->base) {
+        /*
+         * For active commit or mirror with sync=top, we need to
+         * freeze the backing chain down to the base, because we keep
+         * pointers to it and its overlay.  For other cases (mirror
+         * with sync=full or sync=none), we do not, so there is no
+         * need to freeze any part of the chain.
+         * (s->base is non-NULL only in these cases.)
+         */
+        if (target_is_backing) {
+            assert(s->base == target);
+        }
 
-        if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
+        if (bdrv_freeze_backing_chain(mirror_top_bs, s->base, errp) < 0) {
             goto fail;
         }
     }
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 3/3] iotests/041: x-blockdev-reopen during mirror
  2020-09-02 16:48 [PATCH 0/3] mirror: Freeze backing chain for sync=top Max Reitz
  2020-09-02 16:48 ` [PATCH 1/3] mirror: Set s->base_overlay only if s->base is set Max Reitz
  2020-09-02 16:48 ` [PATCH 2/3] mirror: Freeze backing chain for sync=top Max Reitz
@ 2020-09-02 16:48 ` Max Reitz
  2 siblings, 0 replies; 5+ messages in thread
From: Max Reitz @ 2020-09-02 16:48 UTC (permalink / raw)
  To: qemu-block; +Cc: Kevin Wolf, John Snow, qemu-devel, Max Reitz

Test what happens when you remove the backing file during a mirror with
sync=top.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 tests/qemu-iotests/041     | 92 ++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/041.out |  4 +-
 2 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
index cdbef3ba20..4ed7e293ae 100755
--- a/tests/qemu-iotests/041
+++ b/tests/qemu-iotests/041
@@ -1432,6 +1432,98 @@ class TestFilters(iotests.QMPTestCase):
         self.complete_and_wait('mirror')
 
 
+class TestReconfDuringMirror(iotests.QMPTestCase):
+    def setUp(self):
+        qemu_img('create', '-f', iotests.imgfmt, backing_img, '1M')
+        qemu_img('create', '-f', iotests.imgfmt, '-b', backing_img,
+                 '-F', iotests.imgfmt, test_img)
+
+        qemu_io('-c', 'write -P 1 0 1M', test_img)
+
+        self.vm = iotests.VM()
+        self.vm.launch()
+
+        blockdevs = (
+            { 'node-name': 'backing-file',
+              'driver': 'file',
+              'filename': backing_img },
+
+            { 'node-name': 'backing',
+              'driver': iotests.imgfmt,
+              'file': 'backing-file',
+              'read-only': True },
+
+            { 'node-name': 'source-file',
+              'driver': 'file',
+              'filename': test_img },
+
+            { 'node-name': 'source',
+              'driver': iotests.imgfmt,
+              'file': 'source-file',
+              'backing': 'backing' }
+        )
+
+        for blockdev in blockdevs:
+            result = self.vm.qmp('blockdev-add', **blockdev)
+            self.assert_qmp(result, 'return', {})
+
+    def tearDown(self):
+        self.vm.shutdown()
+
+        os.remove(test_img)
+        os.remove(backing_img)
+
+        try:
+            os.remove(target_img)
+        except OSError:
+            pass
+
+    def test_reconf_during_top_mirror(self):
+        result = self.vm.qmp('drive-mirror',
+                             job_id='mirror',
+                             device='source',
+                             sync='top',
+                             target=target_img,
+                             speed=65536)
+        self.assert_qmp(result, 'return', {})
+
+        result = self.vm.qmp('x-blockdev-reopen',
+                             **{ 'node-name': 'source',
+                                 'driver': iotests.imgfmt,
+                                 'file': 'source-file',
+                                 'backing': None })
+        if 'error' in result:
+            self.assert_qmp(result, 'error/desc',
+                            "Cannot change 'backing' link "
+                            "from 'source' to 'backing'")
+
+            result = self.vm.qmp('block-job-set-speed',
+                                 device='mirror', speed=0)
+            self.assert_qmp(result, 'return', {})
+
+            self.complete_and_wait('mirror')
+
+            # Pass
+            return
+
+        # This is not expected.  Let's see what happens when we drop
+        # the backing file altogether.
+        for node in ('backing', 'backing-file'):
+            result = self.vm.qmp('blockdev-del', node_name=node)
+            self.assert_qmp(result, 'return', {})
+
+        result = self.vm.qmp('block-job-set-speed',
+                             device='mirror', speed=0)
+        self.assert_qmp(result, 'return', {})
+
+        # Completing will now probably result in a segfault, because
+        # it tries to assign 'backing' as the target's backing node
+        self.complete_and_wait('mirror')
+
+        # Either way, this is a failure.
+        self.fail('x-blockdev-reopen should not have succeeded')
+
+
 if __name__ == '__main__':
     iotests.main(supported_fmts=['qcow2', 'qed'],
                  supported_protocols=['file'],
diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out
index 46651953e8..5273ce86c3 100644
--- a/tests/qemu-iotests/041.out
+++ b/tests/qemu-iotests/041.out
@@ -1,5 +1,5 @@
-...........................................................................................................
+............................................................................................................
 ----------------------------------------------------------------------
-Ran 107 tests
+Ran 108 tests
 
 OK
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/3] mirror: Freeze backing chain for sync=top
  2020-09-02 16:48 ` [PATCH 2/3] mirror: Freeze backing chain for sync=top Max Reitz
@ 2020-09-02 17:00   ` Max Reitz
  0 siblings, 0 replies; 5+ messages in thread
From: Max Reitz @ 2020-09-02 17:00 UTC (permalink / raw)
  To: qemu-block; +Cc: Kevin Wolf, John Snow, qemu-devel


[-- Attachment #1.1: Type: text/plain, Size: 2181 bytes --]

On 02.09.20 18:48, Max Reitz wrote:
> Reported-by: Kevin Wolf <kwolf@redhat.com>
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/mirror.c | 20 +++++++++++++++++---
>  1 file changed, 17 insertions(+), 3 deletions(-)
> 
> diff --git a/block/mirror.c b/block/mirror.c
> index 11ebffdf99..27422ab7a5 100644
> --- a/block/mirror.c
> +++ b/block/mirror.c
> @@ -649,8 +649,8 @@ static int mirror_exit_common(Job *job)
>      src = mirror_top_bs->backing->bs;
>      target_bs = blk_bs(s->target);
>  
> -    if (bdrv_chain_contains(src, target_bs)) {
> -        bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs);
> +    if (s->base) {
> +        bdrv_unfreeze_backing_chain(mirror_top_bs, s->base);
>      }
>  
>      bdrv_release_dirty_bitmap(s->dirty_bitmap);
> @@ -1780,8 +1780,22 @@ static BlockJob *mirror_start_job(
>                  goto fail;
>              }
>          }
> +    }
> +
> +    if (s->base) {
> +        /*
> +         * For active commit or mirror with sync=top, we need to
> +         * freeze the backing chain down to the base, because we keep
> +         * pointers to it and its overlay.  For other cases (mirror
> +         * with sync=full or sync=none), we do not, so there is no
> +         * need to freeze any part of the chain.
> +         * (s->base is non-NULL only in these cases.)
> +         */
> +        if (target_is_backing) {
> +            assert(s->base == target);
> +        }

On second thought, this assertion doesn’t hold true when mirroring to an
image in the backing chain (i.e., specifically not using commit).  Now,
this mostly doesn’t work thanks to op blockers, but after “Deal with
filters”, it’s possible.  I don’t know whether it produces any sensible
results, though.

Should we leave it possible, or should we make mirroring to a node in
the backing chain (i.e. target_is_backing && s->base != target) an error?

Max

>  
> -        if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) {
> +        if (bdrv_freeze_backing_chain(mirror_top_bs, s->base, errp) < 0) {
>              goto fail;
>          }
>      }
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-09-02 17:02 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-09-02 16:48 [PATCH 0/3] mirror: Freeze backing chain for sync=top Max Reitz
2020-09-02 16:48 ` [PATCH 1/3] mirror: Set s->base_overlay only if s->base is set Max Reitz
2020-09-02 16:48 ` [PATCH 2/3] mirror: Freeze backing chain for sync=top Max Reitz
2020-09-02 17:00   ` Max Reitz
2020-09-02 16:48 ` [PATCH 3/3] iotests/041: x-blockdev-reopen during mirror Max Reitz

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).