block: Don't poll in parent drain callbacks

bdrv_do_drained_begin() is only safe if we have a single
BDRV_POLL_WHILE() after quiescing all affected nodes. We cannot allow
that parent callbacks introduce a nested polling loop that could cause
graph changes while we're traversing the graph.

Split off bdrv_do_drained_begin_quiesce(), which only quiesces a single
node without waiting for its requests to complete. These requests will
be waited for in the BDRV_POLL_WHILE() call down the call chain.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Kevin Wolf 2018-03-23 15:57:20 +01:00
parent ebd3183761
commit dcf94a23b1
3 changed files with 26 additions and 9 deletions

View File

@ -818,7 +818,7 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c)
static void bdrv_child_cb_drained_begin(BdrvChild *child) static void bdrv_child_cb_drained_begin(BdrvChild *child)
{ {
BlockDriverState *bs = child->opaque; BlockDriverState *bs = child->opaque;
bdrv_drained_begin(bs); bdrv_do_drained_begin_quiesce(bs, NULL);
} }
static bool bdrv_child_cb_drained_poll(BdrvChild *child) static bool bdrv_child_cb_drained_poll(BdrvChild *child)

View File

@ -286,15 +286,10 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
assert(data.done); assert(data.done);
} }
void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
BdrvChild *parent, bool poll) BdrvChild *parent)
{ {
BdrvChild *child, *next; assert(!qemu_in_coroutine());
if (qemu_in_coroutine()) {
bdrv_co_yield_to_drain(bs, true, recursive, parent, poll);
return;
}
/* Stop things in parent-to-child order */ /* Stop things in parent-to-child order */
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
@ -303,6 +298,19 @@ void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
bdrv_parent_drained_begin(bs, parent); bdrv_parent_drained_begin(bs, parent);
bdrv_drain_invoke(bs, true); bdrv_drain_invoke(bs, true);
}
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
BdrvChild *parent, bool poll)
{
BdrvChild *child, *next;
if (qemu_in_coroutine()) {
bdrv_co_yield_to_drain(bs, true, recursive, parent, poll);
return;
}
bdrv_do_drained_begin_quiesce(bs, parent);
if (recursive) { if (recursive) {
bs->recursive_quiesce_counter++; bs->recursive_quiesce_counter++;

View File

@ -590,6 +590,15 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
*/ */
void bdrv_drained_begin(BlockDriverState *bs); void bdrv_drained_begin(BlockDriverState *bs);
/**
* bdrv_do_drained_begin_quiesce:
*
* Quiesces a BDS like bdrv_drained_begin(), but does not wait for already
* running requests to complete.
*/
void bdrv_do_drained_begin_quiesce(BlockDriverState *bs,
BdrvChild *parent);
/** /**
* Like bdrv_drained_begin, but recursively begins a quiesced section for * Like bdrv_drained_begin, but recursively begins a quiesced section for
* exclusive access to all child nodes as well. * exclusive access to all child nodes as well.