7f1c5b
From 5defda06ec4c24818a34126c5048be5e274b63f5 Mon Sep 17 00:00:00 2001
7f1c5b
From: Kevin Wolf <kwolf@redhat.com>
7f1c5b
Date: Fri, 18 Nov 2022 18:41:04 +0100
7f1c5b
Subject: [PATCH 22/31] stream: Replace subtree drain with a single node drain
7f1c5b
7f1c5b
RH-Author: Stefano Garzarella <sgarzare@redhat.com>
7f1c5b
RH-MergeRequest: 135: block: Simplify drain to prevent QEMU from crashing during snapshot
7f1c5b
RH-Bugzilla: 2155112
7f1c5b
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7f1c5b
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
7f1c5b
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
7f1c5b
RH-Commit: [10/16] a93250b1f6ef296e903df0ba5d8b29bc2ed540a8 (sgarzarella/qemu-kvm-c-9-s)
7f1c5b
7f1c5b
The subtree drain was introduced in commit b1e1af394d9 as a way to avoid
7f1c5b
graph changes between finding the base node and changing the block graph
7f1c5b
as necessary on completion of the image streaming job.
7f1c5b
7f1c5b
The block graph could change between these two points because
7f1c5b
bdrv_set_backing_hd() first drains the parent node, which involved
7f1c5b
polling and can do anything.
7f1c5b
7f1c5b
Subtree draining was an imperfect way to make this less likely (because
7f1c5b
with it, fewer callbacks are called during this window). Everyone agreed
7f1c5b
that it's not really the right solution, and it was only committed as a
7f1c5b
stopgap solution.
7f1c5b
7f1c5b
This replaces the subtree drain with a solution that simply drains the
7f1c5b
parent node before we try to find the base node, and then call a version
7f1c5b
of bdrv_set_backing_hd() that doesn't drain, but just asserts that the
7f1c5b
parent node is already drained.
7f1c5b
7f1c5b
This way, any graph changes caused by draining happen before we start
7f1c5b
looking at the graph and things stay consistent between finding the base
7f1c5b
node and changing the graph.
7f1c5b
7f1c5b
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7f1c5b
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
7f1c5b
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
7f1c5b
Message-Id: <20221118174110.55183-10-kwolf@redhat.com>
7f1c5b
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7f1c5b
(cherry picked from commit 92140b9f3f07d80e2c27edcc6e32f392be2135e6)
7f1c5b
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
7f1c5b
---
7f1c5b
 block.c                            | 17 ++++++++++++++---
7f1c5b
 block/stream.c                     | 26 ++++++++++++++++----------
7f1c5b
 include/block/block-global-state.h |  3 +++
7f1c5b
 3 files changed, 33 insertions(+), 13 deletions(-)
7f1c5b
7f1c5b
diff --git a/block.c b/block.c
7f1c5b
index b3449a312e..5330e89903 100644
7f1c5b
--- a/block.c
7f1c5b
+++ b/block.c
7f1c5b
@@ -3403,14 +3403,15 @@ static int bdrv_set_backing_noperm(BlockDriverState *bs,
7f1c5b
     return bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp);
7f1c5b
 }
7f1c5b
 
7f1c5b
-int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
7f1c5b
-                        Error **errp)
7f1c5b
+int bdrv_set_backing_hd_drained(BlockDriverState *bs,
7f1c5b
+                                BlockDriverState *backing_hd,
7f1c5b
+                                Error **errp)
7f1c5b
 {
7f1c5b
     int ret;
7f1c5b
     Transaction *tran = tran_new();
7f1c5b
 
7f1c5b
     GLOBAL_STATE_CODE();
7f1c5b
-    bdrv_drained_begin(bs);
7f1c5b
+    assert(bs->quiesce_counter > 0);
7f1c5b
 
7f1c5b
     ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp);
7f1c5b
     if (ret < 0) {
7f1c5b
@@ -3420,7 +3421,17 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
7f1c5b
     ret = bdrv_refresh_perms(bs, errp);
7f1c5b
 out:
7f1c5b
     tran_finalize(tran, ret);
7f1c5b
+    return ret;
7f1c5b
+}
7f1c5b
 
7f1c5b
+int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
7f1c5b
+                        Error **errp)
7f1c5b
+{
7f1c5b
+    int ret;
7f1c5b
+    GLOBAL_STATE_CODE();
7f1c5b
+
7f1c5b
+    bdrv_drained_begin(bs);
7f1c5b
+    ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp);
7f1c5b
     bdrv_drained_end(bs);
7f1c5b
 
7f1c5b
     return ret;
7f1c5b
diff --git a/block/stream.c b/block/stream.c
7f1c5b
index 694709bd25..8744ad103f 100644
7f1c5b
--- a/block/stream.c
7f1c5b
+++ b/block/stream.c
7f1c5b
@@ -64,13 +64,16 @@ static int stream_prepare(Job *job)
7f1c5b
     bdrv_cor_filter_drop(s->cor_filter_bs);
7f1c5b
     s->cor_filter_bs = NULL;
7f1c5b
 
7f1c5b
-    bdrv_subtree_drained_begin(s->above_base);
7f1c5b
+    /*
7f1c5b
+     * bdrv_set_backing_hd() requires that unfiltered_bs is drained. Drain
7f1c5b
+     * already here and use bdrv_set_backing_hd_drained() instead because
7f1c5b
+     * the polling during drained_begin() might change the graph, and if we do
7f1c5b
+     * this only later, we may end up working with the wrong base node (or it
7f1c5b
+     * might even have gone away by the time we want to use it).
7f1c5b
+     */
7f1c5b
+    bdrv_drained_begin(unfiltered_bs);
7f1c5b
 
7f1c5b
     base = bdrv_filter_or_cow_bs(s->above_base);
7f1c5b
-    if (base) {
7f1c5b
-        bdrv_ref(base);
7f1c5b
-    }
7f1c5b
-
7f1c5b
     unfiltered_base = bdrv_skip_filters(base);
7f1c5b
 
7f1c5b
     if (bdrv_cow_child(unfiltered_bs)) {
7f1c5b
@@ -82,7 +85,13 @@ static int stream_prepare(Job *job)
7f1c5b
             }
7f1c5b
         }
7f1c5b
 
7f1c5b
-        bdrv_set_backing_hd(unfiltered_bs, base, &local_err);
7f1c5b
+        bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err);
7f1c5b
+
7f1c5b
+        /*
7f1c5b
+         * This call will do I/O, so the graph can change again from here on.
7f1c5b
+         * We have already completed the graph change, so we are not in danger
7f1c5b
+         * of operating on the wrong node any more if this happens.
7f1c5b
+         */
7f1c5b
         ret = bdrv_change_backing_file(unfiltered_bs, base_id, base_fmt, false);
7f1c5b
         if (local_err) {
7f1c5b
             error_report_err(local_err);
7f1c5b
@@ -92,10 +101,7 @@ static int stream_prepare(Job *job)
7f1c5b
     }
7f1c5b
 
7f1c5b
 out:
7f1c5b
-    if (base) {
7f1c5b
-        bdrv_unref(base);
7f1c5b
-    }
7f1c5b
-    bdrv_subtree_drained_end(s->above_base);
7f1c5b
+    bdrv_drained_end(unfiltered_bs);
7f1c5b
     return ret;
7f1c5b
 }
7f1c5b
 
7f1c5b
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
7f1c5b
index c7bd4a2088..00e0cf8aea 100644
7f1c5b
--- a/include/block/block-global-state.h
7f1c5b
+++ b/include/block/block-global-state.h
7f1c5b
@@ -82,6 +82,9 @@ int bdrv_open_file_child(const char *filename,
7f1c5b
 BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp);
7f1c5b
 int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
7f1c5b
                         Error **errp);
7f1c5b
+int bdrv_set_backing_hd_drained(BlockDriverState *bs,
7f1c5b
+                                BlockDriverState *backing_hd,
7f1c5b
+                                Error **errp);
7f1c5b
 int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
7f1c5b
                            const char *bdref_key, Error **errp);
7f1c5b
 BlockDriverState *bdrv_open(const char *filename, const char *reference,
7f1c5b
-- 
7f1c5b
2.31.1
7f1c5b