thebeanogamer / rpms / qemu-kvm

Forked from rpms/qemu-kvm 6 months ago
Clone
7f1c5b
From 150ef3356cc6732fede7ca059168fc0565ed0b76 Mon Sep 17 00:00:00 2001
7f1c5b
From: Kevin Wolf <kwolf@redhat.com>
7f1c5b
Date: Fri, 18 Nov 2022 18:41:09 +0100
7f1c5b
Subject: [PATCH 27/31] block: Don't poll in bdrv_replace_child_noperm()
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: [15/16] 5fc7d6b703a2d6c1118d875056f0afbd6ba5cca9 (sgarzarella/qemu-kvm-c-9-s)
7f1c5b
7f1c5b
In order to make sure that bdrv_replace_child_noperm() doesn't have to
7f1c5b
poll any more, get rid of the bdrv_parent_drained_begin_single() call.
7f1c5b
7f1c5b
This is possible now because we can require that the parent is already
7f1c5b
drained through the child in question when the function is called and we
7f1c5b
don't call the parent drain callbacks more than once.
7f1c5b
7f1c5b
The additional drain calls needed in callers cause the test case to run
7f1c5b
its code in the drain handler too early (bdrv_attach_child() drains
7f1c5b
now), so modify it to only enable the code after the test setup has
7f1c5b
completed.
7f1c5b
7f1c5b
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7f1c5b
Message-Id: <20221118174110.55183-15-kwolf@redhat.com>
7f1c5b
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
7f1c5b
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
7f1c5b
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7f1c5b
(cherry picked from commit 23987471285a26397e3152a9244b652445fd36c4)
7f1c5b
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
7f1c5b
---
7f1c5b
 block.c                      | 103 ++++++++++++++++++++++++++++++-----
7f1c5b
 block/io.c                   |   2 +-
7f1c5b
 include/block/block-io.h     |   8 +++
7f1c5b
 tests/unit/test-bdrv-drain.c |  10 ++++
7f1c5b
 4 files changed, 108 insertions(+), 15 deletions(-)
7f1c5b
7f1c5b
diff --git a/block.c b/block.c
7f1c5b
index af31a94863..65588d313a 100644
7f1c5b
--- a/block.c
7f1c5b
+++ b/block.c
7f1c5b
@@ -2407,6 +2407,20 @@ static void bdrv_replace_child_abort(void *opaque)
7f1c5b
 
7f1c5b
     GLOBAL_STATE_CODE();
7f1c5b
     /* old_bs reference is transparently moved from @s to @s->child */
7f1c5b
+    if (!s->child->bs) {
7f1c5b
+        /*
7f1c5b
+         * The parents were undrained when removing old_bs from the child. New
7f1c5b
+         * requests can't have been made, though, because the child was empty.
7f1c5b
+         *
7f1c5b
+         * TODO Make bdrv_replace_child_noperm() transactionable to avoid
7f1c5b
+         * undraining the parent in the first place. Once this is done, having
7f1c5b
+         * new_bs drained when calling bdrv_replace_child_tran() is not a
7f1c5b
+         * requirement any more.
7f1c5b
+         */
7f1c5b
+        bdrv_parent_drained_begin_single(s->child, false);
7f1c5b
+        assert(!bdrv_parent_drained_poll_single(s->child));
7f1c5b
+    }
7f1c5b
+    assert(s->child->quiesced_parent);
7f1c5b
     bdrv_replace_child_noperm(s->child, s->old_bs);
7f1c5b
     bdrv_unref(new_bs);
7f1c5b
 }
7f1c5b
@@ -2422,12 +2436,19 @@ static TransactionActionDrv bdrv_replace_child_drv = {
7f1c5b
  *
7f1c5b
  * Note: real unref of old_bs is done only on commit.
7f1c5b
  *
7f1c5b
+ * Both @child->bs and @new_bs (if non-NULL) must be drained. @new_bs must be
7f1c5b
+ * kept drained until the transaction is completed.
7f1c5b
+ *
7f1c5b
  * The function doesn't update permissions, caller is responsible for this.
7f1c5b
  */
7f1c5b
 static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
7f1c5b
                                     Transaction *tran)
7f1c5b
 {
7f1c5b
     BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
7f1c5b
+
7f1c5b
+    assert(child->quiesced_parent);
7f1c5b
+    assert(!new_bs || new_bs->quiesce_counter);
7f1c5b
+
7f1c5b
     *s = (BdrvReplaceChildState) {
7f1c5b
         .child = child,
7f1c5b
         .old_bs = child->bs,
7f1c5b
@@ -2819,6 +2840,14 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
7f1c5b
     return permissions[qapi_perm];
7f1c5b
 }
7f1c5b
 
7f1c5b
+/*
7f1c5b
+ * Replaces the node that a BdrvChild points to without updating permissions.
7f1c5b
+ *
7f1c5b
+ * If @new_bs is non-NULL, the parent of @child must already be drained through
7f1c5b
+ * @child.
7f1c5b
+ *
7f1c5b
+ * This function does not poll.
7f1c5b
+ */
7f1c5b
 static void bdrv_replace_child_noperm(BdrvChild *child,
7f1c5b
                                       BlockDriverState *new_bs)
7f1c5b
 {
7f1c5b
@@ -2826,6 +2855,28 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
7f1c5b
     int new_bs_quiesce_counter;
7f1c5b
 
7f1c5b
     assert(!child->frozen);
7f1c5b
+
7f1c5b
+    /*
7f1c5b
+     * If we want to change the BdrvChild to point to a drained node as its new
7f1c5b
+     * child->bs, we need to make sure that its new parent is drained, too. In
7f1c5b
+     * other words, either child->quiesce_parent must already be true or we must
7f1c5b
+     * be able to set it and keep the parent's quiesce_counter consistent with
7f1c5b
+     * that, but without polling or starting new requests (this function
7f1c5b
+     * guarantees that it doesn't poll, and starting new requests would be
7f1c5b
+     * against the invariants of drain sections).
7f1c5b
+     *
7f1c5b
+     * To keep things simple, we pick the first option (child->quiesce_parent
7f1c5b
+     * must already be true). We also generalise the rule a bit to make it
7f1c5b
+     * easier to verify in callers and more likely to be covered in test cases:
7f1c5b
+     * The parent must be quiesced through this child even if new_bs isn't
7f1c5b
+     * currently drained.
7f1c5b
+     *
7f1c5b
+     * The only exception is for callers that always pass new_bs == NULL. In
7f1c5b
+     * this case, we obviously never need to consider the case of a drained
7f1c5b
+     * new_bs, so we can keep the callers simpler by allowing them not to drain
7f1c5b
+     * the parent.
7f1c5b
+     */
7f1c5b
+    assert(!new_bs || child->quiesced_parent);
7f1c5b
     assert(old_bs != new_bs);
7f1c5b
     GLOBAL_STATE_CODE();
7f1c5b
 
7f1c5b
@@ -2833,15 +2884,6 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
7f1c5b
         assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
7f1c5b
     }
7f1c5b
 
7f1c5b
-    /*
7f1c5b
-     * If the new child node is drained but the old one was not, flush
7f1c5b
-     * all outstanding requests to the old child node.
7f1c5b
-     */
7f1c5b
-    new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
7f1c5b
-    if (new_bs_quiesce_counter && !child->quiesced_parent) {
7f1c5b
-        bdrv_parent_drained_begin_single(child, true);
7f1c5b
-    }
7f1c5b
-
7f1c5b
     if (old_bs) {
7f1c5b
         if (child->klass->detach) {
7f1c5b
             child->klass->detach(child);
7f1c5b
@@ -2861,11 +2903,9 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
7f1c5b
     }
7f1c5b
 
7f1c5b
     /*
7f1c5b
-     * If the old child node was drained but the new one is not, allow
7f1c5b
-     * requests to come in only after the new node has been attached.
7f1c5b
-     *
7f1c5b
-     * Update new_bs_quiesce_counter because bdrv_parent_drained_begin_single()
7f1c5b
-     * polls, which could have changed the value.
7f1c5b
+     * If the parent was drained through this BdrvChild previously, but new_bs
7f1c5b
+     * is not drained, allow requests to come in only after the new node has
7f1c5b
+     * been attached.
7f1c5b
      */
7f1c5b
     new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
7f1c5b
     if (!new_bs_quiesce_counter && child->quiesced_parent) {
7f1c5b
@@ -3002,6 +3042,24 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs,
7f1c5b
     }
7f1c5b
 
7f1c5b
     bdrv_ref(child_bs);
7f1c5b
+    /*
7f1c5b
+     * Let every new BdrvChild start with a drained parent. Inserting the child
7f1c5b
+     * in the graph with bdrv_replace_child_noperm() will undrain it if
7f1c5b
+     * @child_bs is not drained.
7f1c5b
+     *
7f1c5b
+     * The child was only just created and is not yet visible in global state
7f1c5b
+     * until bdrv_replace_child_noperm() inserts it into the graph, so nobody
7f1c5b
+     * could have sent requests and polling is not necessary.
7f1c5b
+     *
7f1c5b
+     * Note that this means that the parent isn't fully drained yet, we only
7f1c5b
+     * stop new requests from coming in. This is fine, we don't care about the
7f1c5b
+     * old requests here, they are not for this child. If another place enters a
7f1c5b
+     * drain section for the same parent, but wants it to be fully quiesced, it
7f1c5b
+     * will not run most of the the code in .drained_begin() again (which is not
7f1c5b
+     * a problem, we already did this), but it will still poll until the parent
7f1c5b
+     * is fully quiesced, so it will not be negatively affected either.
7f1c5b
+     */
7f1c5b
+    bdrv_parent_drained_begin_single(new_child, false);
7f1c5b
     bdrv_replace_child_noperm(new_child, child_bs);
7f1c5b
 
7f1c5b
     BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1);
7f1c5b
@@ -5059,12 +5117,24 @@ static void bdrv_remove_child(BdrvChild *child, Transaction *tran)
7f1c5b
     }
7f1c5b
 
7f1c5b
     if (child->bs) {
7f1c5b
+        BlockDriverState *bs = child->bs;
7f1c5b
+        bdrv_drained_begin(bs);
7f1c5b
         bdrv_replace_child_tran(child, NULL, tran);
7f1c5b
+        bdrv_drained_end(bs);
7f1c5b
     }
7f1c5b
 
7f1c5b
     tran_add(tran, &bdrv_remove_child_drv, child);
7f1c5b
 }
7f1c5b
 
7f1c5b
+static void undrain_on_clean_cb(void *opaque)
7f1c5b
+{
7f1c5b
+    bdrv_drained_end(opaque);
7f1c5b
+}
7f1c5b
+
7f1c5b
+static TransactionActionDrv undrain_on_clean = {
7f1c5b
+    .clean = undrain_on_clean_cb,
7f1c5b
+};
7f1c5b
+
7f1c5b
 static int bdrv_replace_node_noperm(BlockDriverState *from,
7f1c5b
                                     BlockDriverState *to,
7f1c5b
                                     bool auto_skip, Transaction *tran,
7f1c5b
@@ -5074,6 +5144,11 @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
7f1c5b
 
7f1c5b
     GLOBAL_STATE_CODE();
7f1c5b
 
7f1c5b
+    bdrv_drained_begin(from);
7f1c5b
+    bdrv_drained_begin(to);
7f1c5b
+    tran_add(tran, &undrain_on_clean, from);
7f1c5b
+    tran_add(tran, &undrain_on_clean, to);
7f1c5b
+
7f1c5b
     QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
7f1c5b
         assert(c->bs == from);
7f1c5b
         if (!should_update_child(c, to)) {
7f1c5b
diff --git a/block/io.c b/block/io.c
7f1c5b
index 5e9150d92c..ae64830eac 100644
7f1c5b
--- a/block/io.c
7f1c5b
+++ b/block/io.c
7f1c5b
@@ -81,7 +81,7 @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
7f1c5b
     }
7f1c5b
 }
7f1c5b
 
7f1c5b
-static bool bdrv_parent_drained_poll_single(BdrvChild *c)
7f1c5b
+bool bdrv_parent_drained_poll_single(BdrvChild *c)
7f1c5b
 {
7f1c5b
     if (c->klass->drained_poll) {
7f1c5b
         return c->klass->drained_poll(c);
7f1c5b
diff --git a/include/block/block-io.h b/include/block/block-io.h
7f1c5b
index 8f5e75756a..65e6d2569b 100644
7f1c5b
--- a/include/block/block-io.h
7f1c5b
+++ b/include/block/block-io.h
7f1c5b
@@ -292,6 +292,14 @@ bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
7f1c5b
  */
7f1c5b
 void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll);
7f1c5b
 
7f1c5b
+/**
7f1c5b
+ * bdrv_parent_drained_poll_single:
7f1c5b
+ *
7f1c5b
+ * Returns true if there is any pending activity to cease before @c can be
7f1c5b
+ * called quiesced, false otherwise.
7f1c5b
+ */
7f1c5b
+bool bdrv_parent_drained_poll_single(BdrvChild *c);
7f1c5b
+
7f1c5b
 /**
7f1c5b
  * bdrv_parent_drained_end_single:
7f1c5b
  *
7f1c5b
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
7f1c5b
index 172bc6debc..2686a8acee 100644
7f1c5b
--- a/tests/unit/test-bdrv-drain.c
7f1c5b
+++ b/tests/unit/test-bdrv-drain.c
7f1c5b
@@ -1654,6 +1654,7 @@ static void test_drop_intermediate_poll(void)
7f1c5b
 
7f1c5b
 
7f1c5b
 typedef struct BDRVReplaceTestState {
7f1c5b
+    bool setup_completed;
7f1c5b
     bool was_drained;
7f1c5b
     bool was_undrained;
7f1c5b
     bool has_read;
7f1c5b
@@ -1738,6 +1739,10 @@ static void bdrv_replace_test_drain_begin(BlockDriverState *bs)
7f1c5b
 {
7f1c5b
     BDRVReplaceTestState *s = bs->opaque;
7f1c5b
 
7f1c5b
+    if (!s->setup_completed) {
7f1c5b
+        return;
7f1c5b
+    }
7f1c5b
+
7f1c5b
     if (!s->drain_count) {
7f1c5b
         s->drain_co = qemu_coroutine_create(bdrv_replace_test_drain_co, bs);
7f1c5b
         bdrv_inc_in_flight(bs);
7f1c5b
@@ -1769,6 +1774,10 @@ static void bdrv_replace_test_drain_end(BlockDriverState *bs)
7f1c5b
 {
7f1c5b
     BDRVReplaceTestState *s = bs->opaque;
7f1c5b
 
7f1c5b
+    if (!s->setup_completed) {
7f1c5b
+        return;
7f1c5b
+    }
7f1c5b
+
7f1c5b
     g_assert(s->drain_count > 0);
7f1c5b
     if (!--s->drain_count) {
7f1c5b
         s->was_undrained = true;
7f1c5b
@@ -1867,6 +1876,7 @@ static void do_test_replace_child_mid_drain(int old_drain_count,
7f1c5b
     bdrv_ref(old_child_bs);
7f1c5b
     bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds,
7f1c5b
                       BDRV_CHILD_COW, &error_abort);
7f1c5b
+    parent_s->setup_completed = true;
7f1c5b
 
7f1c5b
     for (i = 0; i < old_drain_count; i++) {
7f1c5b
         bdrv_drained_begin(old_child_bs);
7f1c5b
-- 
7f1c5b
2.31.1
7f1c5b