diff mbox series

[2/2] block: bdrv_reopen() with backing file in different AioContext

Message ID 20200227181804.15299-3-kwolf@redhat.com
State New, archived
Headers show
Series block: bdrv_reopen() with backing file in different AioContext | expand

Commit Message

Kevin Wolf Feb. 27, 2020, 6:18 p.m. UTC
This patch allows bdrv_reopen() (and therefore the x-blockdev-reopen QMP
command) to attach a node as the new backing file even if the node is in
a different AioContext than the parent if one of both nodes can be moved
to the AioContext of the other node.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 block.c                | 36 +++++++++++++++++++++++++++---------
 tests/qemu-iotests/245 |  8 +++-----
 2 files changed, 30 insertions(+), 14 deletions(-)

Comments

Alberto Garcia March 5, 2020, 3:54 p.m. UTC | #1
On Thu 27 Feb 2020 07:18:04 PM CET, Kevin Wolf wrote:
>      /*
> -     * TODO: before removing the x- prefix from x-blockdev-reopen we
> -     * should move the new backing file into the right AioContext
> -     * instead of returning an error.
> +     * Check AioContext compatibility so that the bdrv_set_backing_hd() call in
> +     * bdrv_reopen_commit() won't fail.
>       */
> -    if (new_backing_bs) {
> -        if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
> -            error_setg(errp, "Cannot use a new backing file "
> -                       "with a different AioContext");
> -            return -EINVAL;
> -        }
> +    if (!bdrv_reopen_can_attach(bs->backing, bs, new_backing_bs, errp)) {
> +        return -EINVAL;
>      }

What happens here now if 'new_backing_bs' is NULL ?

It seems that you would be calling bdrv_can_set_aio_context(NULL, ...),
and it looks like that would crash.

Berto
Kevin Wolf March 6, 2020, 2:10 p.m. UTC | #2
Am 05.03.2020 um 16:54 hat Alberto Garcia geschrieben:
> On Thu 27 Feb 2020 07:18:04 PM CET, Kevin Wolf wrote:
> >      /*
> > -     * TODO: before removing the x- prefix from x-blockdev-reopen we
> > -     * should move the new backing file into the right AioContext
> > -     * instead of returning an error.
> > +     * Check AioContext compatibility so that the bdrv_set_backing_hd() call in
> > +     * bdrv_reopen_commit() won't fail.
> >       */
> > -    if (new_backing_bs) {
> > -        if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
> > -            error_setg(errp, "Cannot use a new backing file "
> > -                       "with a different AioContext");
> > -            return -EINVAL;
> > -        }
> > +    if (!bdrv_reopen_can_attach(bs->backing, bs, new_backing_bs, errp)) {
> > +        return -EINVAL;
> >      }
> 
> What happens here now if 'new_backing_bs' is NULL ?
> 
> It seems that you would be calling bdrv_can_set_aio_context(NULL, ...),
> and it looks like that would crash.

Not sure why I thought that this check isn't needed any more...

It actually works as long as everything runs in the main loop context
(because bdrv_get_aio_context(NULL) return the main context, so there is
nothing to do), which is why the test cases didn't fail. But as soon as
you move things to a different AioContext, they will fail.

Maybe even worse, the argument order for bdrv_reopen_can_attach() is
wrong.

Thanks for catching this, I'll send a v2.

Kevin
diff mbox series

Patch

diff --git a/block.c b/block.c
index 202c67e1e8..5dbba6cf31 100644
--- a/block.c
+++ b/block.c
@@ -3781,6 +3781,29 @@  static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs,
     *shared = cumulative_shared_perms;
 }
 
+static bool bdrv_reopen_can_attach(BdrvChild *child,
+                                   BlockDriverState *new_child,
+                                   BlockDriverState *parent,
+                                   Error **errp)
+{
+    AioContext *parent_ctx = bdrv_get_aio_context(parent);
+    AioContext *child_ctx = bdrv_get_aio_context(new_child);
+    GSList *ignore;
+    bool ret;
+
+    ignore = g_slist_prepend(NULL, child);
+    ret = bdrv_can_set_aio_context(new_child, parent_ctx, &ignore, NULL);
+    g_slist_free(ignore);
+    if (ret) {
+        return ret;
+    }
+
+    ignore = g_slist_prepend(NULL, child);
+    ret = bdrv_can_set_aio_context(parent, child_ctx, &ignore, errp);
+    g_slist_free(ignore);
+    return ret;
+}
+
 /*
  * Take a BDRVReopenState and check if the value of 'backing' in the
  * reopen_state->options QDict is valid or not.
@@ -3832,16 +3855,11 @@  static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state,
     }
 
     /*
-     * TODO: before removing the x- prefix from x-blockdev-reopen we
-     * should move the new backing file into the right AioContext
-     * instead of returning an error.
+     * Check AioContext compatibility so that the bdrv_set_backing_hd() call in
+     * bdrv_reopen_commit() won't fail.
      */
-    if (new_backing_bs) {
-        if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) {
-            error_setg(errp, "Cannot use a new backing file "
-                       "with a different AioContext");
-            return -EINVAL;
-        }
+    if (!bdrv_reopen_can_attach(bs->backing, bs, new_backing_bs, errp)) {
+        return -EINVAL;
     }
 
     /*
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
index 5a2cd5ed0e..d6135ec14d 100755
--- a/tests/qemu-iotests/245
+++ b/tests/qemu-iotests/245
@@ -1010,18 +1010,16 @@  class TestBlockdevReopen(iotests.QMPTestCase):
     # neither of them can switch to the other AioContext
     def test_iothreads_error(self):
         self.run_test_iothreads('iothread0', 'iothread1',
-                                "Cannot use a new backing file with a different AioContext")
+                                "Cannot change iothread of active block backend")
 
     def test_iothreads_compatible_users(self):
         self.run_test_iothreads('iothread0', 'iothread0')
 
     def test_iothreads_switch_backing(self):
-        self.run_test_iothreads('iothread0', None,
-                                "Cannot use a new backing file with a different AioContext")
+        self.run_test_iothreads('iothread0', None)
 
     def test_iothreads_switch_overlay(self):
-        self.run_test_iothreads(None, 'iothread0',
-                                "Cannot use a new backing file with a different AioContext")
+        self.run_test_iothreads(None, 'iothread0')
 
 if __name__ == '__main__':
     iotests.main(supported_fmts=["qcow2"],