summaryrefslogtreecommitdiff
path: root/block.c
diff options
context:
space:
mode:
Diffstat (limited to 'block.c')
-rw-r--r--block.c35
1 files changed, 34 insertions, 1 deletions
diff --git a/block.c b/block.c
index 69ceac6377..7d9ed1a724 100644
--- a/block.c
+++ b/block.c
@@ -2249,14 +2249,19 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
*
* On failure NULL is returned, errp is set and the reference to
* child_bs is also dropped.
+ *
+ * The caller must hold the AioContext lock @child_bs, but not that of @ctx
+ * (unless @child_bs is already in @ctx).
*/
BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
const char *child_name,
const BdrvChildRole *child_role,
+ AioContext *ctx,
uint64_t perm, uint64_t shared_perm,
void *opaque, Error **errp)
{
BdrvChild *child;
+ Error *local_err = NULL;
int ret;
ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
@@ -2276,6 +2281,31 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
.opaque = opaque,
};
+ /* If the AioContexts don't match, first try to move the subtree of
+ * child_bs into the AioContext of the new parent. If this doesn't work,
+ * try moving the parent into the AioContext of child_bs instead. */
+ if (bdrv_get_aio_context(child_bs) != ctx) {
+ ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err);
+ if (ret < 0 && child_role->can_set_aio_ctx) {
+ GSList *ignore = g_slist_prepend(NULL, child);;
+ ctx = bdrv_get_aio_context(child_bs);
+ if (child_role->can_set_aio_ctx(child, ctx, &ignore, NULL)) {
+ error_free(local_err);
+ ret = 0;
+ g_slist_free(ignore);
+ ignore = g_slist_prepend(NULL, child);;
+ child_role->set_aio_ctx(child, ctx, &ignore);
+ }
+ g_slist_free(ignore);
+ }
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ g_free(child);
+ bdrv_abort_perm_update(child_bs);
+ return NULL;
+ }
+ }
+
/* This performs the matching bdrv_set_perm() for the above check. */
bdrv_replace_child(child, child_bs);
@@ -2289,6 +2319,9 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
*
* On failure NULL is returned, errp is set and the reference to
* child_bs is also dropped.
+ *
+ * If @parent_bs and @child_bs are in different AioContexts, the caller must
+ * hold the AioContext lock for @child_bs, but not for @parent_bs.
*/
BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
BlockDriverState *child_bs,
@@ -2302,11 +2335,11 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
assert(parent_bs->drv);
- assert(bdrv_get_aio_context(parent_bs) == bdrv_get_aio_context(child_bs));
bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
perm, shared_perm, &perm, &shared_perm);
child = bdrv_root_attach_child(child_bs, child_name, child_role,
+ bdrv_get_aio_context(parent_bs),
perm, shared_perm, parent_bs, errp);
if (child == NULL) {
return NULL;