diff --git a/block.c b/block.c index 2ea9cc110d..68dfd822dd 100644 --- a/block.c +++ b/block.c @@ -5041,7 +5041,6 @@ static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = { * format nodes (always .backing) and filter child for filters (may be .file or * .backing) */ -__attribute__((unused)) static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, Transaction *tran) { @@ -5105,16 +5104,32 @@ static int bdrv_replace_node_noperm(BlockDriverState *from, * * With auto_skip=false the error is returned if from has a parent which should * not be updated. + * + * With @detach_subchain=true @to must be in a backing chain of @from. In this + * case backing link of the cow-parent of @to is removed. */ static int bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to, - bool auto_skip, Error **errp) + bool auto_skip, bool detach_subchain, + Error **errp) { Transaction *tran = tran_new(); g_autoptr(GHashTable) found = NULL; g_autoptr(GSList) refresh_list = NULL; + BlockDriverState *to_cow_parent; int ret; + if (detach_subchain) { + assert(bdrv_chain_contains(from, to)); + assert(from != to); + for (to_cow_parent = from; + bdrv_filter_or_cow_bs(to_cow_parent) != to; + to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent)) + { + ; + } + } + /* Make sure that @from doesn't go away until we have successfully attached * all of its parents to @to. */ bdrv_ref(from); @@ -5134,6 +5149,10 @@ static int bdrv_replace_node_common(BlockDriverState *from, goto out; } + if (detach_subchain) { + bdrv_remove_filter_or_cow_child(to_cow_parent, tran); + } + found = g_hash_table_new(NULL, NULL); refresh_list = bdrv_topological_dfs(refresh_list, found, to); @@ -5158,7 +5177,13 @@ out: int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp) { - return bdrv_replace_node_common(from, to, true, errp); + return bdrv_replace_node_common(from, to, true, false, errp); +} + +int bdrv_drop_filter(BlockDriverState *bs, Error **errp) +{ + return bdrv_replace_node_common(bs, bdrv_filter_or_cow_bs(bs), true, true, + errp); } /* @@ -5493,7 +5518,17 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, updated_children = g_slist_prepend(updated_children, c); } - bdrv_replace_node_common(top, base, false, &local_err); + /* + * It seems correct to pass detach_subchain=true here, but it triggers + * one more yet not fixed bug, when due to nested aio_poll loop we switch to + * another drained section, which modify the graph (for example, removing + * the child, which we keep in updated_children list). So, it's a TODO. + * + * Note, bug triggered if pass detach_subchain=true here and run + * test-bdrv-drain. test_drop_intermediate_poll() test-case will crash. + * That's a FIXME. + */ + bdrv_replace_node_common(top, base, false, false, &local_err); if (local_err) { error_report_err(local_err); goto exit; diff --git a/include/block/block.h b/include/block/block.h index 16e496a5c4..85481a05c6 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -362,6 +362,7 @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp); BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, int flags, Error **errp); +int bdrv_drop_filter(BlockDriverState *bs, Error **errp); int bdrv_parse_aio(const char *mode, int *flags); int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough);