You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
9.9 KiB
251 lines
9.9 KiB
From 9bb9cafd736057fd2a8ebfa6f5769668f125fbe6 Mon Sep 17 00:00:00 2001
|
|
From: Kevin Wolf <kwolf@redhat.com>
|
|
Date: Fri, 18 Nov 2022 18:41:06 +0100
|
|
Subject: [PATCH 24/31] block: Call drain callbacks only once
|
|
|
|
RH-Author: Stefano Garzarella <sgarzare@redhat.com>
|
|
RH-MergeRequest: 135: block: Simplify drain to prevent QEMU from crashing during snapshot
|
|
RH-Bugzilla: 2155112
|
|
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
|
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
|
|
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
|
|
RH-Commit: [12/16] ea9a433dc01d1b8539a2d4ea12887f2a3ce830ea (sgarzarella/qemu-kvm-c-9-s)
|
|
|
|
We only need to call both the BlockDriver's callback and the parent
|
|
callbacks when going from undrained to drained or vice versa. A second
|
|
drain section doesn't make a difference for the driver or the parent,
|
|
they weren't supposed to send new requests before and after the second
|
|
drain.
|
|
|
|
One thing that gets in the way is the 'ignore_bds_parents' parameter in
|
|
bdrv_do_drained_begin_quiesce() and bdrv_do_drained_end(): It means that
|
|
bdrv_drain_all_begin() increases bs->quiesce_counter, but does not
|
|
quiesce the parent through BdrvChildClass callbacks. If an additional
|
|
drain section is started now, bs->quiesce_counter will be non-zero, but
|
|
we would still need to quiesce the parent through BdrvChildClass in
|
|
order to keep things consistent (and unquiesce it on the matching
|
|
bdrv_drained_end(), even though the counter would not reach 0 yet as
|
|
long as the bdrv_drain_all() section is still active).
|
|
|
|
Instead of keeping track of this, let's just get rid of the parameter.
|
|
It was introduced in commit 6cd5c9d7b2d as an optimisation so that
|
|
during bdrv_drain_all(), we wouldn't recursively drain all parents up to
|
|
the root for each node, resulting in quadratic complexity. As it happens,
|
|
calling the callbacks only once solves the same problem, so as of this
|
|
patch, we'll still have O(n) complexity and ignore_bds_parents is not
|
|
needed any more.
|
|
|
|
This patch only ignores the 'ignore_bds_parents' parameter. It will be
|
|
removed in a separate patch.
|
|
|
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
|
Reviewed-by: Hanna Reitz <hreitz@redhat.com>
|
|
Message-Id: <20221118174110.55183-12-kwolf@redhat.com>
|
|
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
|
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
|
(cherry picked from commit 57e05be343f33f4e5899a8d8946a8596d68424a1)
|
|
Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
|
|
---
|
|
block.c | 25 +++++++------------------
|
|
block/io.c | 30 ++++++++++++++++++------------
|
|
include/block/block_int-common.h | 8 ++++----
|
|
tests/unit/test-bdrv-drain.c | 16 ++++++++++------
|
|
4 files changed, 39 insertions(+), 40 deletions(-)
|
|
|
|
diff --git a/block.c b/block.c
|
|
index e0e3b21790..5a583e260d 100644
|
|
--- a/block.c
|
|
+++ b/block.c
|
|
@@ -2824,7 +2824,6 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
|
{
|
|
BlockDriverState *old_bs = child->bs;
|
|
int new_bs_quiesce_counter;
|
|
- int drain_saldo;
|
|
|
|
assert(!child->frozen);
|
|
assert(old_bs != new_bs);
|
|
@@ -2834,16 +2833,13 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
|
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
|
|
}
|
|
|
|
- new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
|
|
- drain_saldo = new_bs_quiesce_counter - child->parent_quiesce_counter;
|
|
-
|
|
/*
|
|
* If the new child node is drained but the old one was not, flush
|
|
* all outstanding requests to the old child node.
|
|
*/
|
|
- while (drain_saldo > 0 && child->klass->drained_begin) {
|
|
+ new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
|
|
+ if (new_bs_quiesce_counter && !child->quiesced_parent) {
|
|
bdrv_parent_drained_begin_single(child, true);
|
|
- drain_saldo--;
|
|
}
|
|
|
|
if (old_bs) {
|
|
@@ -2859,16 +2855,6 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
|
if (new_bs) {
|
|
assert_bdrv_graph_writable(new_bs);
|
|
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
|
|
-
|
|
- /*
|
|
- * Polling in bdrv_parent_drained_begin_single() may have led to the new
|
|
- * node's quiesce_counter having been decreased. Not a problem, we just
|
|
- * need to recognize this here and then invoke drained_end appropriately
|
|
- * more often.
|
|
- */
|
|
- assert(new_bs->quiesce_counter <= new_bs_quiesce_counter);
|
|
- drain_saldo += new_bs->quiesce_counter - new_bs_quiesce_counter;
|
|
-
|
|
if (child->klass->attach) {
|
|
child->klass->attach(child);
|
|
}
|
|
@@ -2877,10 +2863,13 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
|
/*
|
|
* If the old child node was drained but the new one is not, allow
|
|
* requests to come in only after the new node has been attached.
|
|
+ *
|
|
+ * Update new_bs_quiesce_counter because bdrv_parent_drained_begin_single()
|
|
+ * polls, which could have changed the value.
|
|
*/
|
|
- while (drain_saldo < 0 && child->klass->drained_end) {
|
|
+ new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0);
|
|
+ if (!new_bs_quiesce_counter && child->quiesced_parent) {
|
|
bdrv_parent_drained_end_single(child);
|
|
- drain_saldo++;
|
|
}
|
|
}
|
|
|
|
diff --git a/block/io.c b/block/io.c
|
|
index 75224480d0..87d6f22ec4 100644
|
|
--- a/block/io.c
|
|
+++ b/block/io.c
|
|
@@ -62,8 +62,9 @@ void bdrv_parent_drained_end_single(BdrvChild *c)
|
|
{
|
|
IO_OR_GS_CODE();
|
|
|
|
- assert(c->parent_quiesce_counter > 0);
|
|
- c->parent_quiesce_counter--;
|
|
+ assert(c->quiesced_parent);
|
|
+ c->quiesced_parent = false;
|
|
+
|
|
if (c->klass->drained_end) {
|
|
c->klass->drained_end(c);
|
|
}
|
|
@@ -110,7 +111,10 @@ void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll)
|
|
{
|
|
AioContext *ctx = bdrv_child_get_parent_aio_context(c);
|
|
IO_OR_GS_CODE();
|
|
- c->parent_quiesce_counter++;
|
|
+
|
|
+ assert(!c->quiesced_parent);
|
|
+ c->quiesced_parent = true;
|
|
+
|
|
if (c->klass->drained_begin) {
|
|
c->klass->drained_begin(c);
|
|
}
|
|
@@ -358,11 +362,12 @@ void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
|
|
/* Stop things in parent-to-child order */
|
|
if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) {
|
|
aio_disable_external(bdrv_get_aio_context(bs));
|
|
- }
|
|
|
|
- bdrv_parent_drained_begin(bs, parent, ignore_bds_parents);
|
|
- if (bs->drv && bs->drv->bdrv_drain_begin) {
|
|
- bs->drv->bdrv_drain_begin(bs);
|
|
+ /* TODO Remove ignore_bds_parents, we don't consider it any more */
|
|
+ bdrv_parent_drained_begin(bs, parent, false);
|
|
+ if (bs->drv && bs->drv->bdrv_drain_begin) {
|
|
+ bs->drv->bdrv_drain_begin(bs);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -413,13 +418,14 @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent,
|
|
assert(bs->quiesce_counter > 0);
|
|
|
|
/* Re-enable things in child-to-parent order */
|
|
- if (bs->drv && bs->drv->bdrv_drain_end) {
|
|
- bs->drv->bdrv_drain_end(bs);
|
|
- }
|
|
- bdrv_parent_drained_end(bs, parent, ignore_bds_parents);
|
|
-
|
|
old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter);
|
|
if (old_quiesce_counter == 1) {
|
|
+ if (bs->drv && bs->drv->bdrv_drain_end) {
|
|
+ bs->drv->bdrv_drain_end(bs);
|
|
+ }
|
|
+ /* TODO Remove ignore_bds_parents, we don't consider it any more */
|
|
+ bdrv_parent_drained_end(bs, parent, false);
|
|
+
|
|
aio_enable_external(bdrv_get_aio_context(bs));
|
|
}
|
|
}
|
|
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
|
|
index 791dddfd7d..a6bc6b7fe9 100644
|
|
--- a/include/block/block_int-common.h
|
|
+++ b/include/block/block_int-common.h
|
|
@@ -980,13 +980,13 @@ struct BdrvChild {
|
|
bool frozen;
|
|
|
|
/*
|
|
- * How many times the parent of this child has been drained
|
|
+ * True if the parent of this child has been drained by this BdrvChild
|
|
* (through klass->drained_*).
|
|
- * Usually, this is equal to bs->quiesce_counter (potentially
|
|
- * reduced by bdrv_drain_all_count). It may differ while the
|
|
+ *
|
|
+ * It is generally true if bs->quiesce_counter > 0. It may differ while the
|
|
* child is entering or leaving a drained section.
|
|
*/
|
|
- int parent_quiesce_counter;
|
|
+ bool quiesced_parent;
|
|
|
|
QLIST_ENTRY(BdrvChild) next;
|
|
QLIST_ENTRY(BdrvChild) next_parent;
|
|
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
|
|
index dda08de8db..172bc6debc 100644
|
|
--- a/tests/unit/test-bdrv-drain.c
|
|
+++ b/tests/unit/test-bdrv-drain.c
|
|
@@ -296,7 +296,11 @@ static void test_quiesce_common(enum drain_type drain_type, bool recursive)
|
|
|
|
do_drain_begin(drain_type, bs);
|
|
|
|
- g_assert_cmpint(bs->quiesce_counter, ==, 1);
|
|
+ if (drain_type == BDRV_DRAIN_ALL) {
|
|
+ g_assert_cmpint(bs->quiesce_counter, ==, 2);
|
|
+ } else {
|
|
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
|
|
+ }
|
|
g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
|
|
|
|
do_drain_end(drain_type, bs);
|
|
@@ -348,8 +352,8 @@ static void test_nested(void)
|
|
|
|
for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
|
|
for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
|
|
- int backing_quiesce = (outer != BDRV_DRAIN) +
|
|
- (inner != BDRV_DRAIN);
|
|
+ int backing_quiesce = (outer == BDRV_DRAIN_ALL) +
|
|
+ (inner == BDRV_DRAIN_ALL);
|
|
|
|
g_assert_cmpint(bs->quiesce_counter, ==, 0);
|
|
g_assert_cmpint(backing->quiesce_counter, ==, 0);
|
|
@@ -359,10 +363,10 @@ static void test_nested(void)
|
|
do_drain_begin(outer, bs);
|
|
do_drain_begin(inner, bs);
|
|
|
|
- g_assert_cmpint(bs->quiesce_counter, ==, 2);
|
|
+ g_assert_cmpint(bs->quiesce_counter, ==, 2 + !!backing_quiesce);
|
|
g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
|
|
- g_assert_cmpint(s->drain_count, ==, 2);
|
|
- g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce);
|
|
+ g_assert_cmpint(s->drain_count, ==, 1);
|
|
+ g_assert_cmpint(backing_s->drain_count, ==, !!backing_quiesce);
|
|
|
|
do_drain_end(inner, bs);
|
|
do_drain_end(outer, bs);
|
|
--
|
|
2.31.1
|
|
|