return 0;
}
+static bool bdrv_child_cb_can_set_aio_ctx(BdrvChild *child, AioContext *ctx,
+ GSList **ignore, Error **errp)
+{
+ BlockDriverState *bs = child->opaque;
+ return bdrv_can_set_aio_context(bs, ctx, ignore, errp);
+}
+
+static void bdrv_child_cb_set_aio_ctx(BdrvChild *child, AioContext *ctx,
+ GSList **ignore)
+{
+ BlockDriverState *bs = child->opaque;
+ return bdrv_set_aio_context_ignore(bs, ctx, ignore);
+}
+
/*
* Returns the options and flags that a temporary snapshot should get, based on
* the originally requested flags (the originally requested image will have
.attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
+ .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
+ .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
};
/*
.attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate,
+ .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
+ .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
};
static void bdrv_backing_attach(BdrvChild *c)
.drained_end = bdrv_child_cb_drained_end,
.inactivate = bdrv_child_cb_inactivate,
.update_filename = bdrv_backing_update_filename,
+ .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
+ .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
};
static int bdrv_open_flags(BlockDriverState *bs, int flags)
GSList *ignore_children, Error **errp);
static void bdrv_child_abort_perm_update(BdrvChild *c);
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
+static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
+ uint64_t *shared_perm);
typedef struct BlockReopenQueueEntry {
bool prepared;
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
!bdrv_is_writable_after_reopen(bs, q))
{
- error_setg(errp, "Block node is read-only");
+ if (!bdrv_is_writable_after_reopen(bs, NULL)) {
+ error_setg(errp, "Block node is read-only");
+ } else {
+ uint64_t current_perms, current_shared;
+ bdrv_get_cumulative_perm(bs, ¤t_perms, ¤t_shared);
+ if (current_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) {
+ error_setg(errp, "Cannot make block node read-only, there is "
+ "a writer on it");
+ } else {
+ error_setg(errp, "Cannot make block node read-only and create "
+ "a writer on it");
+ }
+ }
+
return -EPERM;
}
bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm);
bdrv_check_perm(old_bs, NULL, perm, shared_perm, NULL, &error_abort);
bdrv_set_perm(old_bs, perm, shared_perm);
+
+ /* When the parent requiring a non-default AioContext is removed, the
+ * node moves back to the main AioContext */
+ bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
}
if (new_bs) {
}
}
+/*
+ * This function steals the reference to child_bs from the caller.
+ * That reference is later dropped by bdrv_root_unref_child().
+ *
+ * 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);
if (ret < 0) {
bdrv_abort_perm_update(child_bs);
+ bdrv_unref(child_bs);
return NULL;
}
.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);
return child;
}
+/*
+ * This function transfers the reference to child_bs from the caller
+ * to parent_bs. That reference is later dropped by parent_bs on
+ * bdrv_close() or if someone calls bdrv_unref_child().
+ *
+ * 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,
const char *child_name,
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;
/* If backing_hd was already part of bs's backing chain, and
* inherits_from pointed recursively to bs then let's update it to
* point directly to bs (else it will become NULL). */
- if (update_inherits_from) {
+ if (bs->backing && update_inherits_from) {
backing_hd->inherits_from = bs;
}
- if (!bs->backing) {
- bdrv_unref(backing_hd);
- }
out:
bdrv_refresh_limits(bs, NULL);
ret = -EINVAL;
goto free_exit;
}
- bdrv_set_aio_context(backing_hd, bdrv_get_aio_context(bs));
if (implicit_backing) {
bdrv_refresh_filename(backing_hd);
const BdrvChildRole *child_role,
bool allow_none, Error **errp)
{
- BdrvChild *c;
BlockDriverState *bs;
bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_role,
return NULL;
}
- c = bdrv_attach_child(parent, bs, bdref_key, child_role, errp);
- if (!c) {
- bdrv_unref(bs);
- return NULL;
- }
-
- return c;
+ return bdrv_attach_child(parent, bs, bdref_key, child_role, errp);
}
/* TODO Future callers may need to specify parent/child_role in order for
/* Not requesting BLK_PERM_CONSISTENT_READ because we're only
* looking at the header to guess the image format. This works even
* in cases where a guest would not see a consistent state. */
- file = blk_new(0, BLK_PERM_ALL);
+ file = blk_new(bdrv_get_aio_context(file_bs), 0, BLK_PERM_ALL);
blk_insert_bs(file, file_bs, &local_err);
bdrv_unref(file_bs);
if (local_err) {
bs->drv = NULL;
}
- bdrv_set_backing_hd(bs, NULL, &error_abort);
-
- if (bs->file != NULL) {
- bdrv_unref_child(bs, bs->file);
- bs->file = NULL;
- }
-
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
- /* TODO Remove bdrv_unref() from drivers' close function and use
- * bdrv_unref_child() here */
- if (child->bs->inherits_from == bs) {
- child->bs->inherits_from = NULL;
- }
- bdrv_detach_child(child);
+ bdrv_unref_child(bs, child);
}
+ bs->backing = NULL;
+ bs->file = NULL;
g_free(bs->opaque);
bs->opaque = NULL;
atomic_set(&bs->copy_on_read, 0);
uint64_t perm = 0, shared = BLK_PERM_ALL;
int ret;
- assert(!atomic_read(&from->in_flight));
- assert(!atomic_read(&to->in_flight));
-
/* 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());
+ bdrv_drained_begin(from);
+
/* Put all parents into @list and calculate their cumulative permissions */
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
assert(c->bs == from);
out:
g_slist_free(list);
+ bdrv_drained_end(from);
bdrv_unref(from);
}
g_free(ban);
}
-void bdrv_detach_aio_context(BlockDriverState *bs)
+static void bdrv_detach_aio_context(BlockDriverState *bs)
{
BdrvAioNotifier *baf, *baf_tmp;
- BdrvChild *child;
assert(!bs->walking_aio_notifiers);
bs->walking_aio_notifiers = true;
if (bs->drv && bs->drv->bdrv_detach_aio_context) {
bs->drv->bdrv_detach_aio_context(bs);
}
- QLIST_FOREACH(child, &bs->children, next) {
- bdrv_detach_aio_context(child->bs);
- }
if (bs->quiesce_counter) {
aio_enable_external(bs->aio_context);
bs->aio_context = NULL;
}
-void bdrv_attach_aio_context(BlockDriverState *bs,
- AioContext *new_context)
+static void bdrv_attach_aio_context(BlockDriverState *bs,
+ AioContext *new_context)
{
BdrvAioNotifier *ban, *ban_tmp;
- BdrvChild *child;
if (bs->quiesce_counter) {
aio_disable_external(new_context);
bs->aio_context = new_context;
- QLIST_FOREACH(child, &bs->children, next) {
- bdrv_attach_aio_context(child->bs, new_context);
- }
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
bs->drv->bdrv_attach_aio_context(bs, new_context);
}
bs->walking_aio_notifiers = false;
}
-/* The caller must own the AioContext lock for the old AioContext of bs, but it
- * must not own the AioContext lock for new_context (unless new_context is
- * the same as the current context of bs). */
-void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
+/*
+ * Changes the AioContext used for fd handlers, timers, and BHs by this
+ * BlockDriverState and all its children and parents.
+ *
+ * The caller must own the AioContext lock for the old AioContext of bs, but it
+ * must not own the AioContext lock for new_context (unless new_context is the
+ * same as the current context of bs).
+ *
+ * @ignore will accumulate all visited BdrvChild object. The caller is
+ * responsible for freeing the list afterwards.
+ */
+void bdrv_set_aio_context_ignore(BlockDriverState *bs,
+ AioContext *new_context, GSList **ignore)
{
+ BdrvChild *child;
+
if (bdrv_get_aio_context(bs) == new_context) {
return;
}
bdrv_drained_begin(bs);
+
+ QLIST_FOREACH(child, &bs->children, next) {
+ if (g_slist_find(*ignore, child)) {
+ continue;
+ }
+ *ignore = g_slist_prepend(*ignore, child);
+ bdrv_set_aio_context_ignore(child->bs, new_context, ignore);
+ }
+ QLIST_FOREACH(child, &bs->parents, next_parent) {
+ if (g_slist_find(*ignore, child)) {
+ continue;
+ }
+ assert(child->role->set_aio_ctx);
+ *ignore = g_slist_prepend(*ignore, child);
+ child->role->set_aio_ctx(child, new_context, ignore);
+ }
+
bdrv_detach_aio_context(bs);
/* This function executes in the old AioContext so acquire the new one in
aio_context_release(new_context);
}
+static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx,
+ GSList **ignore, Error **errp)
+{
+ if (g_slist_find(*ignore, c)) {
+ return true;
+ }
+ *ignore = g_slist_prepend(*ignore, c);
+
+ /* A BdrvChildRole that doesn't handle AioContext changes cannot
+ * tolerate any AioContext changes */
+ if (!c->role->can_set_aio_ctx) {
+ char *user = bdrv_child_user_desc(c);
+ error_setg(errp, "Changing iothreads is not supported by %s", user);
+ g_free(user);
+ return false;
+ }
+ if (!c->role->can_set_aio_ctx(c, ctx, ignore, errp)) {
+ assert(!errp || *errp);
+ return false;
+ }
+ return true;
+}
+
+bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
+ GSList **ignore, Error **errp)
+{
+ if (g_slist_find(*ignore, c)) {
+ return true;
+ }
+ *ignore = g_slist_prepend(*ignore, c);
+ return bdrv_can_set_aio_context(c->bs, ctx, ignore, errp);
+}
+
+/* @ignore will accumulate all visited BdrvChild object. The caller is
+ * responsible for freeing the list afterwards. */
+bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx,
+ GSList **ignore, Error **errp)
+{
+ BdrvChild *c;
+
+ if (bdrv_get_aio_context(bs) == ctx) {
+ return true;
+ }
+
+ QLIST_FOREACH(c, &bs->parents, next_parent) {
+ if (!bdrv_parent_can_set_aio_context(c, ctx, ignore, errp)) {
+ return false;
+ }
+ }
+ QLIST_FOREACH(c, &bs->children, next) {
+ if (!bdrv_child_can_set_aio_context(c, ctx, ignore, errp)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx,
+ BdrvChild *ignore_child, Error **errp)
+{
+ GSList *ignore;
+ bool ret;
+
+ ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL;
+ ret = bdrv_can_set_aio_context(bs, ctx, &ignore, errp);
+ g_slist_free(ignore);
+
+ if (!ret) {
+ return -EPERM;
+ }
+
+ ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL;
+ bdrv_set_aio_context_ignore(bs, ctx, &ignore);
+ g_slist_free(ignore);
+
+ return 0;
+}
+
+int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx,
+ Error **errp)
+{
+ return bdrv_child_try_set_aio_context(bs, ctx, NULL, errp);
+}
+
void bdrv_add_aio_context_notifier(BlockDriverState *bs,
void (*attached_aio_context)(AioContext *new_context, void *opaque),
void (*detach_aio_context)(void *opaque), void *opaque)