aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block.c43
-rw-r--r--include/block/block.h1
2 files changed, 40 insertions, 4 deletions
diff --git a/block.c b/block.c
index 2ea9cc1..68dfd82 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 16e496a..85481a0 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);