bdrv_child_cb_inactivate() asserts that parents are already inactive when children get inactivated. This precondition is necessary because parents could still issue requests in their inactivation code.
When block nodes are created individually with -blockdev, all of them are monitor owned and will be returned by bdrv_next() in an undefined order (in practice, in the order of their creation, which is usually children before parents), which obviously fails the assertion. This patch fixes the ordering by skipping nodes with still active parents in bdrv_inactivate_recurse() because we know that they will be covered by recursion when the last active parent becomes inactive. Signed-off-by: Kevin Wolf <kw...@redhat.com> --- block.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/block.c b/block.c index 5ba3435f8f..0569275e31 100644 --- a/block.c +++ b/block.c @@ -4612,6 +4612,22 @@ void bdrv_invalidate_cache_all(Error **errp) } } +static bool bdrv_has_active_bds_parent(BlockDriverState *bs) +{ + BdrvChild *parent; + + QLIST_FOREACH(parent, &bs->parents, next_parent) { + if (parent->role->parent_is_bds) { + BlockDriverState *parent_bs = parent->opaque; + if (!(parent_bs->open_flags & BDRV_O_INACTIVE)) { + return true; + } + } + } + + return false; +} + static int bdrv_inactivate_recurse(BlockDriverState *bs, bool setting_flag) { @@ -4622,6 +4638,12 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs, return -ENOMEDIUM; } + /* Make sure that we don't inactivate a child before its parent. + * It will be covered by recursion from the yet active parent. */ + if (bdrv_has_active_bds_parent(bs)) { + return 0; + } + if (!setting_flag && bs->drv->bdrv_inactivate) { ret = bs->drv->bdrv_inactivate(bs); if (ret < 0) { -- 2.19.1