@@ -5412,6 +5412,9 @@ bdrv_replace_node_noperm(BlockDriverState *from,
}
/*
+ * Switch all parents of @from to point to @to instead. @from and @to must be in
+ * the same AioContext and both must be drained.
+ *
* With auto_skip=true bdrv_replace_node_common skips updating from parents
* if it creates a parent-child relation loop or if parent is block-job.
*
@@ -5421,10 +5424,9 @@ bdrv_replace_node_noperm(BlockDriverState *from,
* 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, bool detach_subchain,
- Error **errp)
+static int GRAPH_WRLOCK
+bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to,
+ bool auto_skip, bool detach_subchain, Error **errp)
{
Transaction *tran = tran_new();
g_autoptr(GSList) refresh_list = NULL;
@@ -5433,16 +5435,9 @@ static int bdrv_replace_node_common(BlockDriverState *from,
GLOBAL_STATE_CODE();
- /* Make sure that @from doesn't go away until we have successfully attached
- * all of its parents to @to. */
- bdrv_ref(from);
-
- assert(qemu_get_current_aio_context() == qemu_get_aio_context());
+ assert(from->quiesce_counter);
+ assert(to->quiesce_counter);
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
- bdrv_drained_begin(from);
- bdrv_drained_begin(to);
-
- bdrv_graph_wrlock(to);
if (detach_subchain) {
assert(bdrv_chain_contains(from, to));
@@ -5483,33 +5478,51 @@ static int bdrv_replace_node_common(BlockDriverState *from,
out:
tran_finalize(tran, ret);
- bdrv_graph_wrunlock();
-
- bdrv_drained_end(to);
- bdrv_drained_end(from);
- bdrv_unref(from);
-
return ret;
}
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp)
{
+ int ret;
+
GLOBAL_STATE_CODE();
- return bdrv_replace_node_common(from, to, true, false, errp);
+ /* Make sure that @from doesn't go away until we have successfully attached
+ * all of its parents to @to. */
+ bdrv_ref(from);
+ bdrv_drained_begin(from);
+ bdrv_drained_begin(to);
+ bdrv_graph_wrlock(to);
+
+ ret = bdrv_replace_node_common(from, to, true, false, errp);
+
+ bdrv_graph_wrunlock();
+ bdrv_drained_end(to);
+ bdrv_drained_end(from);
+ bdrv_unref(from);
+
+ return ret;
}
int bdrv_drop_filter(BlockDriverState *bs, Error **errp)
{
BlockDriverState *child_bs;
+ int ret;
GLOBAL_STATE_CODE();
+
bdrv_graph_rdlock_main_loop();
child_bs = bdrv_filter_or_cow_bs(bs);
bdrv_graph_rdunlock_main_loop();
- return bdrv_replace_node_common(bs, child_bs, true, true, errp);
+ bdrv_drained_begin(child_bs);
+ bdrv_graph_wrlock(bs);
+ ret = bdrv_replace_node_common(bs, child_bs, true, true, errp);
+ bdrv_graph_wrunlock();
+ bdrv_drained_end(child_bs);
+
+ return ret;
}
/*
@@ -5957,15 +5970,15 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
bdrv_ref(top);
bdrv_drained_begin(base);
- bdrv_graph_rdlock_main_loop();
+ bdrv_graph_wrlock(base);
if (!top->drv || !base->drv) {
- goto exit;
+ goto exit_wrlock;
}
/* Make sure that base is in the backing chain of top */
if (!bdrv_chain_contains(top, base)) {
- goto exit;
+ goto exit_wrlock;
}
/* If 'base' recursively inherits from 'top' then we should set
@@ -5997,6 +6010,8 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
* That's a FIXME.
*/
bdrv_replace_node_common(top, base, false, false, &local_err);
+ bdrv_graph_wrunlock();
+
if (local_err) {
error_report_err(local_err);
goto exit;
@@ -6029,8 +6044,11 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
}
ret = 0;
+ goto exit;
+
+exit_wrlock:
+ bdrv_graph_wrunlock();
exit:
- bdrv_graph_rdunlock_main_loop();
bdrv_drained_end(base);
bdrv_unref(top);
return ret;