Skip to content

Commit

Permalink
block: Call drain callbacks only once
Browse files Browse the repository at this point in the history
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 6cd5c9d 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>
  • Loading branch information
kevmw committed Dec 14, 2022
1 parent a442c7b commit c49c309
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 40 deletions.
25 changes: 7 additions & 18 deletions block.c
Expand Up @@ -2855,7 +2855,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);
Expand All @@ -2865,16 +2864,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) {
Expand All @@ -2890,16 +2886,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);
}
Expand All @@ -2908,10 +2894,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++;
}
}

Expand Down
30 changes: 18 additions & 12 deletions block/io.c
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
}
}

Expand Down Expand Up @@ -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));
}
}
Expand Down
8 changes: 4 additions & 4 deletions include/block/block_int-common.h
Expand Up @@ -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;
Expand Down
16 changes: 10 additions & 6 deletions tests/unit/test-bdrv-drain.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit c49c309

Please sign in to comment.