static bool bdrv_recurse_has_child(BlockDriverState *bs,
BlockDriverState *child);
-static void bdrv_child_free(BdrvChild *child);
-static void bdrv_replace_child_noperm(BdrvChild **child,
- BlockDriverState *new_bs,
- bool free_empty_child);
-static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
- BdrvChild *child,
- Transaction *tran);
+static void bdrv_replace_child_noperm(BdrvChild *child,
+ BlockDriverState *new_bs);
+static void bdrv_remove_child(BdrvChild *child, Transaction *tran);
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
Transaction *tran);
static bool bdrv_backing_overridden(BlockDriverState *bs);
+static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp);
+
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
{
if (!bs || !bs->drv) {
/* page size or 4k (hdd sector size) should be on the safe side */
- return MAX(4096, qemu_real_host_page_size);
+ return MAX(4096, qemu_real_host_page_size());
}
IO_CODE();
{
if (!bs || !bs->drv) {
/* page size or 4k (hdd sector size) should be on the safe side */
- return MAX(4096, qemu_real_host_page_size);
+ return MAX(4096, qemu_real_host_page_size());
}
IO_CODE();
/* The driver isn't registered, maybe we need to load a module */
for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
if (!strcmp(block_driver_modules[i].format_name, format_name)) {
- block_module_load_one(block_driver_modules[i].library_name);
+ Error *local_err = NULL;
+ int rv = block_module_load(block_driver_modules[i].library_name,
+ &local_err);
+ if (rv > 0) {
+ return bdrv_do_find_format(format_name);
+ } else if (rv < 0) {
+ error_report_err(local_err);
+ }
break;
}
}
-
- return bdrv_do_find_format(format_name);
+ return NULL;
}
static int bdrv_format_is_whitelisted(const char *format_name, bool read_only)
CreateCo *cco = opaque;
assert(cco->drv);
+ GLOBAL_STATE_CODE();
ret = cco->drv->bdrv_co_create_opts(cco->drv,
cco->filename, cco->opts, &local_err);
* Helper function for bdrv_create_file_fallback(): Zero the first
* sector to remove any potentially pre-existing image header.
*/
-static int create_file_fallback_zero_first_sector(BlockBackend *blk,
- int64_t current_size,
- Error **errp)
+static int coroutine_fn
+create_file_fallback_zero_first_sector(BlockBackend *blk,
+ int64_t current_size,
+ Error **errp)
{
int64_t bytes_to_clear;
int ret;
bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE);
if (bytes_to_clear) {
- ret = blk_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
+ ret = blk_co_pwrite_zeroes(blk, 0, bytes_to_clear, BDRV_REQ_MAY_UNMAP);
if (ret < 0) {
error_setg_errno(errp, -ret,
"Failed to clear the new image's first sector");
/*
* Create a uniquely-named empty temporary file.
- * Return 0 upon success, otherwise a negative errno value.
+ * Return the actual file name used upon success, otherwise NULL.
+ * This string should be freed with g_free() when not needed any longer.
+ *
+ * Note: creating a temporary file for the caller to (re)open is
+ * inherently racy. Use g_file_open_tmp() instead whenever practical.
*/
-int get_tmp_filename(char *filename, int size)
+char *create_tmp_file(Error **errp)
{
-#ifdef _WIN32
- char temp_dir[MAX_PATH];
- /* GetTempFileName requires that its output buffer (4th param)
- have length MAX_PATH or greater. */
- assert(size >= MAX_PATH);
- return (GetTempPath(MAX_PATH, temp_dir)
- && GetTempFileName(temp_dir, "qem", 0, filename)
- ? 0 : -GetLastError());
-#else
int fd;
const char *tmpdir;
- tmpdir = getenv("TMPDIR");
- if (!tmpdir) {
+ g_autofree char *filename = NULL;
+
+ tmpdir = g_get_tmp_dir();
+#ifndef _WIN32
+ /*
+ * See commit 69bef79 ("block: use /var/tmp instead of /tmp for -snapshot")
+ *
+ * This function is used to create temporary disk images (like -snapshot),
+ * so the files can become very large. /tmp is often a tmpfs where as
+ * /var/tmp is usually on a disk, so more appropriate for disk images.
+ */
+ if (!g_strcmp0(tmpdir, "/tmp")) {
tmpdir = "/var/tmp";
}
- if (snprintf(filename, size, "%s/vl.XXXXXX", tmpdir) >= size) {
- return -EOVERFLOW;
- }
- fd = mkstemp(filename);
+#endif
+
+ filename = g_strdup_printf("%s/vl.XXXXXX", tmpdir);
+ fd = g_mkstemp(filename);
if (fd < 0) {
- return -errno;
- }
- if (close(fd) != 0) {
- unlink(filename);
- return -errno;
+ error_setg_errno(errp, errno, "Could not open temporary file '%s'",
+ filename);
+ return NULL;
}
- return 0;
-#endif
+ close(fd);
+
+ return g_steal_pointer(&filename);
}
/*
for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) {
if (block_driver_modules[i].protocol_name &&
!strcmp(block_driver_modules[i].protocol_name, protocol)) {
- block_module_load_one(block_driver_modules[i].library_name);
+ int rv = block_module_load(block_driver_modules[i].library_name, errp);
+ if (rv > 0) {
+ drv1 = bdrv_do_find_protocol(protocol);
+ } else if (rv < 0) {
+ return NULL;
+ }
break;
}
}
- drv1 = bdrv_do_find_protocol(protocol);
if (!drv1) {
error_setg(errp, "Unknown protocol '%s'", protocol);
}
return ret;
}
- ret = blk_pread(file, 0, buf, sizeof(buf));
+ ret = blk_pread(file, 0, sizeof(buf), buf, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read image for determining its "
"format");
return ret;
}
- drv = bdrv_probe_all(buf, ret, filename);
+ drv = bdrv_probe_all(buf, sizeof(buf), filename);
if (!drv) {
error_setg(errp, "Could not determine image format: No compatible "
"driver found");
- ret = -ENOENT;
+ *pdrv = NULL;
+ return -ENOENT;
}
+
*pdrv = drv;
- return ret;
+ return 0;
}
/**
static void bdrv_join_options(BlockDriverState *bs, QDict *options,
QDict *old_options)
{
+ GLOBAL_STATE_CODE();
if (bs->drv && bs->drv->bdrv_join_options) {
bs->drv->bdrv_join_options(options, old_options);
} else {
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)
+static bool bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp)
{
BlockDriverState *bs = child->opaque;
- return bdrv_set_aio_context_ignore(bs, ctx, ignore);
+ return bdrv_change_aio_context(bs, ctx, visited, tran, errp);
}
/*
assert_bdrv_graph_writable(bs);
QLIST_INSERT_HEAD(&bs->children, child, next);
-
- if (child->role & BDRV_CHILD_COW) {
+ if (bs->drv->is_filter || (child->role & BDRV_CHILD_FILTERED)) {
+ /*
+ * Here we handle filters and block/raw-format.c when it behave like
+ * filter. They generally have a single PRIMARY child, which is also the
+ * FILTERED child, and that they may have multiple more children, which
+ * are neither PRIMARY nor FILTERED. And never we have a COW child here.
+ * So bs->file will be the PRIMARY child, unless the PRIMARY child goes
+ * into bs->backing on exceptional cases; and bs->backing will be
+ * nothing else.
+ */
+ assert(!(child->role & BDRV_CHILD_COW));
+ if (child->role & BDRV_CHILD_PRIMARY) {
+ assert(child->role & BDRV_CHILD_FILTERED);
+ assert(!bs->backing);
+ assert(!bs->file);
+
+ if (bs->drv->filtered_child_is_backing) {
+ bs->backing = child;
+ } else {
+ bs->file = child;
+ }
+ } else {
+ assert(!(child->role & BDRV_CHILD_FILTERED));
+ }
+ } else if (child->role & BDRV_CHILD_COW) {
+ assert(bs->drv->supports_backing);
+ assert(!(child->role & BDRV_CHILD_PRIMARY));
+ assert(!bs->backing);
+ bs->backing = child;
bdrv_backing_attach(child);
+ } else if (child->role & BDRV_CHILD_PRIMARY) {
+ assert(!bs->file);
+ bs->file = child;
}
bdrv_apply_subtree_drain(child, bs);
assert_bdrv_graph_writable(bs);
QLIST_REMOVE(child, next);
+ if (child == bs->backing) {
+ assert(child != bs->file);
+ bs->backing = NULL;
+ } else if (child == bs->file) {
+ bs->file = NULL;
+ }
}
static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
.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,
+ .change_aio_ctx = bdrv_child_cb_change_aio_ctx,
.update_filename = bdrv_child_cb_update_filename,
.get_parent_aio_context = child_of_bds_get_parent_aio_context,
};
{
Error *local_err = NULL;
int i, ret;
+ GLOBAL_STATE_CODE();
bdrv_assign_node_name(bs, node_name, &local_err);
if (local_err) {
goto open_failed;
}
+ assert(!(bs->supported_read_flags & ~BDRV_REQ_MASK));
+ assert(!(bs->supported_write_flags & ~BDRV_REQ_MASK));
+
+ /*
+ * Always allow the BDRV_REQ_REGISTERED_BUF optimization hint. This saves
+ * drivers that pass read/write requests through to a child the trouble of
+ * declaring support explicitly.
+ *
+ * Drivers must not propagate this flag accidentally when they initiate I/O
+ * to a bounce buffer. That case should be rare though.
+ */
+ bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF;
+ bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF;
+
ret = refresh_total_sectors(bs, bs->total_sectors);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not refresh total sector count");
bs->drv = NULL;
if (bs->file != NULL) {
bdrv_unref_child(bs, bs->file);
- bs->file = NULL;
+ assert(!bs->file);
}
g_free(bs->opaque);
bs->opaque = NULL;
BlockDriver *drv = NULL;
Error *local_err = NULL;
+ GLOBAL_STATE_CODE();
+
/*
* Caution: while qdict_get_try_str() is fine, getting non-string
* types would require more care. When @options come from
static char *bdrv_child_user_desc(BdrvChild *c)
{
+ GLOBAL_STATE_CODE();
return c->klass->get_parent_desc(c);
}
uint64_t *nperm, uint64_t *nshared)
{
assert(bs->drv && bs->drv->bdrv_child_perm);
+ GLOBAL_STATE_CODE();
bs->drv->bdrv_child_perm(bs, c, role, reopen_queue,
parent_perm, parent_shared,
nperm, nshared);
{
BlockDriverState *bs = opaque;
uint64_t cumulative_perms, cumulative_shared_perms;
+ GLOBAL_STATE_CODE();
if (bs->drv->bdrv_set_perm) {
bdrv_get_cumulative_perm(bs, &cumulative_perms,
static void bdrv_drv_set_perm_abort(void *opaque)
{
BlockDriverState *bs = opaque;
+ GLOBAL_STATE_CODE();
if (bs->drv->bdrv_abort_perm_update) {
bs->drv->bdrv_abort_perm_update(bs);
uint64_t shared_perm, Transaction *tran,
Error **errp)
{
+ GLOBAL_STATE_CODE();
if (!bs->drv) {
return 0;
}
typedef struct BdrvReplaceChildState {
BdrvChild *child;
- BdrvChild **childp;
BlockDriverState *old_bs;
- bool free_empty_child;
} BdrvReplaceChildState;
static void bdrv_replace_child_commit(void *opaque)
BdrvReplaceChildState *s = opaque;
GLOBAL_STATE_CODE();
- if (s->free_empty_child && !s->child->bs) {
- bdrv_child_free(s->child);
- }
bdrv_unref(s->old_bs);
}
BlockDriverState *new_bs = s->child->bs;
GLOBAL_STATE_CODE();
- /*
- * old_bs reference is transparently moved from @s to s->child.
- *
- * Pass &s->child here instead of s->childp, because:
- * (1) s->old_bs must be non-NULL, so bdrv_replace_child_noperm() will not
- * modify the BdrvChild * pointer we indirectly pass to it, i.e. it
- * will not modify s->child. From that perspective, it does not matter
- * whether we pass s->childp or &s->child.
- * (2) If new_bs is not NULL, s->childp will be NULL. We then cannot use
- * it here.
- * (3) If new_bs is NULL, *s->childp will have been NULLed by
- * bdrv_replace_child_tran()'s bdrv_replace_child_noperm() call, and we
- * must not pass a NULL *s->childp here.
- *
- * So whether new_bs was NULL or not, we cannot pass s->childp here; and in
- * any case, there is no reason to pass it anyway.
- */
- bdrv_replace_child_noperm(&s->child, s->old_bs, true);
- /*
- * The child was pre-existing, so s->old_bs must be non-NULL, and
- * s->child thus must not have been freed
- */
- assert(s->child != NULL);
- if (!new_bs) {
- /* As described above, *s->childp was cleared, so restore it */
- assert(s->childp != NULL);
- *s->childp = s->child;
- }
+ /* old_bs reference is transparently moved from @s to @s->child */
+ bdrv_replace_child_noperm(s->child, s->old_bs);
bdrv_unref(new_bs);
}
* Note: real unref of old_bs is done only on commit.
*
* The function doesn't update permissions, caller is responsible for this.
- *
- * (*childp)->bs must not be NULL.
- *
- * Note that if new_bs == NULL, @childp is stored in a state object attached
- * to @tran, so that the old child can be reinstated in the abort handler.
- * Therefore, if @new_bs can be NULL, @childp must stay valid until the
- * transaction is committed or aborted.
- *
- * If @free_empty_child is true and @new_bs is NULL, the BdrvChild is
- * freed (on commit). @free_empty_child should only be false if the
- * caller will free the BDrvChild themselves (which may be important
- * if this is in turn called in another transactional context).
*/
-static void bdrv_replace_child_tran(BdrvChild **childp,
- BlockDriverState *new_bs,
- Transaction *tran,
- bool free_empty_child)
+static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs,
+ Transaction *tran)
{
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
*s = (BdrvReplaceChildState) {
- .child = *childp,
- .childp = new_bs == NULL ? childp : NULL,
- .old_bs = (*childp)->bs,
- .free_empty_child = free_empty_child,
+ .child = child,
+ .old_bs = child->bs,
};
tran_add(tran, &bdrv_replace_child_drv, s);
- /* The abort handler relies on this */
- assert(s->old_bs != NULL);
-
if (new_bs) {
bdrv_ref(new_bs);
}
- /*
- * Pass free_empty_child=false, we will free the child (if
- * necessary) in bdrv_replace_child_commit() (if our
- * @free_empty_child parameter was true).
- */
- bdrv_replace_child_noperm(childp, new_bs, false);
- /* old_bs reference is transparently moved from *childp to @s */
+ bdrv_replace_child_noperm(child, new_bs);
+ /* old_bs reference is transparently moved from @child to @s */
}
/*
return permissions[qapi_perm];
}
-/**
- * Replace (*childp)->bs by @new_bs.
- *
- * If @new_bs is NULL, *childp will be set to NULL, too: BDS parents
- * generally cannot handle a BdrvChild with .bs == NULL, so clearing
- * BdrvChild.bs should generally immediately be followed by the
- * BdrvChild pointer being cleared as well.
- *
- * If @free_empty_child is true and @new_bs is NULL, the BdrvChild is
- * freed. @free_empty_child should only be false if the caller will
- * free the BdrvChild themselves (this may be important in a
- * transactional context, where it may only be freed on commit).
- */
-static void bdrv_replace_child_noperm(BdrvChild **childp,
- BlockDriverState *new_bs,
- bool free_empty_child)
+static void bdrv_replace_child_noperm(BdrvChild *child,
+ BlockDriverState *new_bs)
{
- BdrvChild *child = *childp;
BlockDriverState *old_bs = child->bs;
int new_bs_quiesce_counter;
int drain_saldo;
assert(!child->frozen);
assert(old_bs != new_bs);
+ GLOBAL_STATE_CODE();
if (old_bs && new_bs) {
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
}
child->bs = new_bs;
- if (!new_bs) {
- *childp = NULL;
- }
if (new_bs) {
assert_bdrv_graph_writable(new_bs);
bdrv_parent_drained_end_single(child);
drain_saldo++;
}
-
- if (free_empty_child && !child->bs) {
- bdrv_child_free(child);
- }
}
/**
}
typedef struct BdrvAttachChildCommonState {
- BdrvChild **child;
+ BdrvChild *child;
AioContext *old_parent_ctx;
AioContext *old_child_ctx;
} BdrvAttachChildCommonState;
static void bdrv_attach_child_common_abort(void *opaque)
{
BdrvAttachChildCommonState *s = opaque;
- BdrvChild *child = *s->child;
- BlockDriverState *bs = child->bs;
+ BlockDriverState *bs = s->child->bs;
- /*
- * Pass free_empty_child=false, because we still need the child
- * for the AioContext operations on the parent below; those
- * BdrvChildClass methods all work on a BdrvChild object, so we
- * need to keep it as an empty shell (after this function, it will
- * not be attached to any parent, and it will not have a .bs).
- */
- bdrv_replace_child_noperm(s->child, NULL, false);
+ GLOBAL_STATE_CODE();
+ bdrv_replace_child_noperm(s->child, NULL);
if (bdrv_get_aio_context(bs) != s->old_child_ctx) {
- bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort);
+ bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort);
}
- if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) {
- GSList *ignore;
+ if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) {
+ Transaction *tran;
+ GHashTable *visited;
+ bool ret;
- /* No need to ignore `child`, because it has been detached already */
- ignore = NULL;
- child->klass->can_set_aio_ctx(child, s->old_parent_ctx, &ignore,
- &error_abort);
- g_slist_free(ignore);
+ tran = tran_new();
- ignore = NULL;
- child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore);
- g_slist_free(ignore);
+ /* No need to visit `child`, because it has been detached already */
+ visited = g_hash_table_new(NULL, NULL);
+ ret = s->child->klass->change_aio_ctx(s->child, s->old_parent_ctx,
+ visited, tran, &error_abort);
+ g_hash_table_destroy(visited);
+
+ /* transaction is supposed to always succeed */
+ assert(ret == true);
+ tran_commit(tran);
}
bdrv_unref(bs);
- bdrv_child_free(child);
+ bdrv_child_free(s->child);
}
static TransactionActionDrv bdrv_attach_child_common_drv = {
/*
* Common part of attaching bdrv child to bs or to blk or to job
*
- * Resulting new child is returned through @child.
- * At start *@child must be NULL.
- * @child is saved to a new entry of @tran, so that *@child could be reverted to
- * NULL on abort(). So referenced variable must live at least until transaction
- * end.
- *
* Function doesn't update permissions, caller is responsible for this.
+ *
+ * Returns new created child.
*/
-static int bdrv_attach_child_common(BlockDriverState *child_bs,
- const char *child_name,
- const BdrvChildClass *child_class,
- BdrvChildRole child_role,
- uint64_t perm, uint64_t shared_perm,
- void *opaque, BdrvChild **child,
- Transaction *tran, Error **errp)
+static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs,
+ const char *child_name,
+ const BdrvChildClass *child_class,
+ BdrvChildRole child_role,
+ uint64_t perm, uint64_t shared_perm,
+ void *opaque,
+ Transaction *tran, Error **errp)
{
BdrvChild *new_child;
AioContext *parent_ctx;
AioContext *child_ctx = bdrv_get_aio_context(child_bs);
- assert(child);
- assert(*child == NULL);
assert(child_class->get_parent_desc);
GLOBAL_STATE_CODE();
parent_ctx = bdrv_child_get_parent_aio_context(new_child);
if (child_ctx != parent_ctx) {
Error *local_err = NULL;
- int ret = bdrv_try_set_aio_context(child_bs, parent_ctx, &local_err);
-
- if (ret < 0 && child_class->can_set_aio_ctx) {
- GSList *ignore = g_slist_prepend(NULL, new_child);
- if (child_class->can_set_aio_ctx(new_child, child_ctx, &ignore,
- NULL))
- {
+ int ret = bdrv_try_change_aio_context(child_bs, parent_ctx, NULL,
+ &local_err);
+
+ if (ret < 0 && child_class->change_aio_ctx) {
+ Transaction *tran = tran_new();
+ GHashTable *visited = g_hash_table_new(NULL, NULL);
+ bool ret_child;
+
+ g_hash_table_add(visited, new_child);
+ ret_child = child_class->change_aio_ctx(new_child, child_ctx,
+ visited, tran, NULL);
+ if (ret_child == true) {
error_free(local_err);
ret = 0;
- g_slist_free(ignore);
- ignore = g_slist_prepend(NULL, new_child);
- child_class->set_aio_ctx(new_child, child_ctx, &ignore);
}
- g_slist_free(ignore);
+ tran_finalize(tran, ret_child == true ? 0 : -1);
+ g_hash_table_destroy(visited);
}
if (ret < 0) {
error_propagate(errp, local_err);
bdrv_child_free(new_child);
- return ret;
+ return NULL;
}
}
bdrv_ref(child_bs);
- bdrv_replace_child_noperm(&new_child, child_bs, true);
- /* child_bs was non-NULL, so new_child must not have been freed */
- assert(new_child != NULL);
-
- *child = new_child;
+ bdrv_replace_child_noperm(new_child, child_bs);
BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1);
*s = (BdrvAttachChildCommonState) {
- .child = child,
+ .child = new_child,
.old_parent_ctx = parent_ctx,
.old_child_ctx = child_ctx,
};
tran_add(tran, &bdrv_attach_child_common_drv, s);
- return 0;
+ return new_child;
}
/*
- * Variable referenced by @child must live at least until transaction end.
- * (see bdrv_attach_child_common() doc for details)
- *
* Function doesn't update permissions, caller is responsible for this.
*/
-static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
- BlockDriverState *child_bs,
- const char *child_name,
- const BdrvChildClass *child_class,
- BdrvChildRole child_role,
- BdrvChild **child,
- Transaction *tran,
- Error **errp)
+static BdrvChild *bdrv_attach_child_noperm(BlockDriverState *parent_bs,
+ BlockDriverState *child_bs,
+ const char *child_name,
+ const BdrvChildClass *child_class,
+ BdrvChildRole child_role,
+ Transaction *tran,
+ Error **errp)
{
- int ret;
uint64_t perm, shared_perm;
assert(parent_bs->drv);
if (bdrv_recurse_has_child(child_bs, parent_bs)) {
error_setg(errp, "Making '%s' a %s child of '%s' would create a cycle",
child_bs->node_name, child_name, parent_bs->node_name);
- return -EINVAL;
+ return NULL;
}
bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
perm, shared_perm, &perm, &shared_perm);
- ret = bdrv_attach_child_common(child_bs, child_name, child_class,
- child_role, perm, shared_perm, parent_bs,
- child, tran, errp);
- if (ret < 0) {
- return ret;
- }
-
- return 0;
+ return bdrv_attach_child_common(child_bs, child_name, child_class,
+ child_role, perm, shared_perm, parent_bs,
+ tran, errp);
}
-static void bdrv_detach_child(BdrvChild **childp)
+static void bdrv_detach_child(BdrvChild *child)
{
- BlockDriverState *old_bs = (*childp)->bs;
+ BlockDriverState *old_bs = child->bs;
GLOBAL_STATE_CODE();
- bdrv_replace_child_noperm(childp, NULL, true);
+ bdrv_replace_child_noperm(child, NULL);
+ bdrv_child_free(child);
if (old_bs) {
/*
* 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);
+ bdrv_try_change_aio_context(old_bs, qemu_get_aio_context(), NULL, NULL);
}
}
void *opaque, Error **errp)
{
int ret;
- BdrvChild *child = NULL;
+ BdrvChild *child;
Transaction *tran = tran_new();
GLOBAL_STATE_CODE();
- ret = bdrv_attach_child_common(child_bs, child_name, child_class,
+ child = bdrv_attach_child_common(child_bs, child_name, child_class,
child_role, perm, shared_perm, opaque,
- &child, tran, errp);
- if (ret < 0) {
+ tran, errp);
+ if (!child) {
+ ret = -EINVAL;
goto out;
}
out:
tran_finalize(tran, ret);
- /* child is unset on failure by bdrv_attach_child_common_abort() */
- assert((ret < 0) == !child);
bdrv_unref(child_bs);
- return child;
+
+ return ret < 0 ? NULL : child;
}
/*
Error **errp)
{
int ret;
- BdrvChild *child = NULL;
+ BdrvChild *child;
Transaction *tran = tran_new();
GLOBAL_STATE_CODE();
- ret = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class,
- child_role, &child, tran, errp);
- if (ret < 0) {
+ child = bdrv_attach_child_noperm(parent_bs, child_bs, child_name,
+ child_class, child_role, tran, errp);
+ if (!child) {
+ ret = -EINVAL;
goto out;
}
out:
tran_finalize(tran, ret);
- /* child is unset on failure by bdrv_attach_child_common_abort() */
- assert((ret < 0) == !child);
bdrv_unref(child_bs);
- return child;
+ return ret < 0 ? NULL : child;
}
/* Callers must ensure that child->frozen is false. */
GLOBAL_STATE_CODE();
child_bs = child->bs;
- bdrv_detach_child(&child);
+ bdrv_detach_child(child);
bdrv_unref(child_bs);
}
static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load)
{
BdrvChild *c;
+ GLOBAL_STATE_CODE();
QLIST_FOREACH(c, &bs->parents, next_parent) {
if (c->klass->change_media) {
c->klass->change_media(c, load);
bool is_backing,
Transaction *tran, Error **errp)
{
- int ret = 0;
bool update_inherits_from =
bdrv_inherits_from_recursive(child_bs, parent_bs);
BdrvChild *child = is_backing ? parent_bs->backing : parent_bs->file;
if (child) {
bdrv_unset_inherits_from(parent_bs, child, tran);
- bdrv_remove_file_or_backing_child(parent_bs, child, tran);
+ bdrv_remove_child(child, tran);
}
if (!child_bs) {
goto out;
}
- ret = bdrv_attach_child_noperm(parent_bs, child_bs,
- is_backing ? "backing" : "file",
- &child_of_bds, role,
- is_backing ? &parent_bs->backing :
- &parent_bs->file,
- tran, errp);
- if (ret < 0) {
- return ret;
+ child = bdrv_attach_child_noperm(parent_bs, child_bs,
+ is_backing ? "backing" : "file",
+ &child_of_bds, role,
+ tran, errp);
+ if (!child) {
+ return -EINVAL;
}
errp);
}
+/*
+ * Wrapper on bdrv_open_child() for most popular case: open primary child of bs.
+ */
+int bdrv_open_file_child(const char *filename,
+ QDict *options, const char *bdref_key,
+ BlockDriverState *parent, Error **errp)
+{
+ BdrvChildRole role;
+
+ /* commit_top and mirror_top don't use this function */
+ assert(!parent->drv->filtered_child_is_backing);
+ role = parent->drv->is_filter ?
+ (BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY) : BDRV_CHILD_IMAGE;
+
+ if (!bdrv_open_child(filename, options, bdref_key, parent,
+ &child_of_bds, role, false, errp))
+ {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/*
* TODO Future callers may need to specify parent/child_class in order for
* option inheritance to work. Existing callers use it for the root node.
QDict *snapshot_options,
Error **errp)
{
- /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
- char *tmp_filename = g_malloc0(PATH_MAX + 1);
+ g_autofree char *tmp_filename = NULL;
int64_t total_size;
QemuOpts *opts = NULL;
BlockDriverState *bs_snapshot = NULL;
}
/* Create the temporary image */
- ret = get_tmp_filename(tmp_filename, PATH_MAX + 1);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "Could not get temporary filename");
+ tmp_filename = create_tmp_file(errp);
+ if (!tmp_filename) {
goto out;
}
out:
qobject_unref(snapshot_options);
- g_free(tmp_filename);
return bs_snapshot;
}
assert(!child_class || !flags);
assert(!child_class == !parent);
+ GLOBAL_STATE_CODE();
if (reference) {
bool options_non_empty = options ? qdict_size(options) : false;
* important to avoid graph changes between the recursive queuing here and
* bdrv_reopen_multiple(). */
assert(bs->quiesce_counter > 0);
+ GLOBAL_STATE_CODE();
if (bs_queue == NULL) {
bs_queue = g_new0(BlockReopenQueue, 1);
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
assert(bs_queue != NULL);
+ GLOBAL_STATE_CODE();
QTAILQ_FOREACH(bs_entry, bs_queue, entry) {
ctx = bdrv_get_aio_context(bs_entry->state.bs);
assert(reopen_state != NULL);
assert(reopen_state->bs->drv != NULL);
+ GLOBAL_STATE_CODE();
drv = reopen_state->bs->drv;
/* This function and each driver's bdrv_reopen_prepare() remove
bs = reopen_state->bs;
drv = bs->drv;
assert(drv != NULL);
+ GLOBAL_STATE_CODE();
/* If there are any driver level actions to take */
if (drv->bdrv_reopen_commit) {
assert(reopen_state != NULL);
drv = reopen_state->bs->drv;
assert(drv != NULL);
+ GLOBAL_STATE_CODE();
if (drv->bdrv_reopen_abort) {
drv->bdrv_reopen_abort(reopen_state);
bdrv_unref_child(bs, child);
}
- bs->backing = NULL;
- bs->file = NULL;
+ assert(!bs->backing);
+ assert(!bs->file);
g_free(bs->opaque);
bs->opaque = NULL;
qatomic_set(&bs->copy_on_read, 0);
void bdrv_close_all(void)
{
- assert(job_next(NULL) == NULL);
GLOBAL_STATE_CODE();
+ assert(job_next(NULL) == NULL);
/* Drop references from requests still in flight, such as canceled block
* jobs whose AIO context has not been polled yet */
return ret;
}
-typedef struct BdrvRemoveFilterOrCowChild {
- BdrvChild *child;
- BlockDriverState *bs;
- bool is_backing;
-} BdrvRemoveFilterOrCowChild;
-
-static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
-{
- BdrvRemoveFilterOrCowChild *s = opaque;
- BlockDriverState *parent_bs = s->child->opaque;
-
- if (s->is_backing) {
- parent_bs->backing = s->child;
- } else {
- parent_bs->file = s->child;
- }
-
- /*
- * We don't have to restore child->bs here to undo bdrv_replace_child_tran()
- * because that function is transactionable and it registered own completion
- * entries in @tran, so .abort() for bdrv_replace_child_safe() will be
- * called automatically.
- */
-}
-
-static void bdrv_remove_filter_or_cow_child_commit(void *opaque)
+static void bdrv_remove_child_commit(void *opaque)
{
- BdrvRemoveFilterOrCowChild *s = opaque;
GLOBAL_STATE_CODE();
- bdrv_child_free(s->child);
+ bdrv_child_free(opaque);
}
-static void bdrv_remove_filter_or_cow_child_clean(void *opaque)
-{
- BdrvRemoveFilterOrCowChild *s = opaque;
-
- /* Drop the bs reference after the transaction is done */
- bdrv_unref(s->bs);
- g_free(s);
-}
-
-static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = {
- .abort = bdrv_remove_filter_or_cow_child_abort,
- .commit = bdrv_remove_filter_or_cow_child_commit,
- .clean = bdrv_remove_filter_or_cow_child_clean,
+static TransactionActionDrv bdrv_remove_child_drv = {
+ .commit = bdrv_remove_child_commit,
};
-/*
- * A function to remove backing or file child of @bs.
- * Function doesn't update permissions, caller is responsible for this.
- */
-static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
- BdrvChild *child,
- Transaction *tran)
+/* Function doesn't update permissions, caller is responsible for this. */
+static void bdrv_remove_child(BdrvChild *child, Transaction *tran)
{
- BdrvChild **childp;
- BdrvRemoveFilterOrCowChild *s;
-
if (!child) {
return;
}
- /*
- * Keep a reference to @bs so @childp will stay valid throughout the
- * transaction (required by bdrv_replace_child_tran())
- */
- bdrv_ref(bs);
- if (child == bs->backing) {
- childp = &bs->backing;
- } else if (child == bs->file) {
- childp = &bs->file;
- } else {
- g_assert_not_reached();
- }
-
if (child->bs) {
- /*
- * Pass free_empty_child=false, we will free the child in
- * bdrv_remove_filter_or_cow_child_commit()
- */
- bdrv_replace_child_tran(childp, NULL, tran, false);
+ bdrv_replace_child_tran(child, NULL, tran);
}
- s = g_new(BdrvRemoveFilterOrCowChild, 1);
- *s = (BdrvRemoveFilterOrCowChild) {
- .child = child,
- .bs = bs,
- .is_backing = (childp == &bs->backing),
- };
- tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s);
+ tran_add(tran, &bdrv_remove_child_drv, child);
}
/*
static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs,
Transaction *tran)
{
- bdrv_remove_file_or_backing_child(bs, bdrv_filter_or_cow_child(bs), tran);
+ bdrv_remove_child(bdrv_filter_or_cow_child(bs), tran);
}
static int bdrv_replace_node_noperm(BlockDriverState *from,
{
BdrvChild *c, *next;
- assert(to != NULL);
GLOBAL_STATE_CODE();
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
c->name, from->node_name);
return -EPERM;
}
-
- /*
- * Passing a pointer to the local variable @c is fine here, because
- * @to is not NULL, and so &c will not be attached to the transaction.
- */
- bdrv_replace_child_tran(&c, to, tran, true);
+ bdrv_replace_child_tran(c, to, tran);
}
return 0;
*
* 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.
- *
- * @to must not be NULL.
*/
static int bdrv_replace_node_common(BlockDriverState *from,
BlockDriverState *to,
int ret;
GLOBAL_STATE_CODE();
- assert(to != NULL);
if (detach_subchain) {
assert(bdrv_chain_contains(from, to));
return ret;
}
-/**
- * Replace node @from by @to (where neither may be NULL).
- */
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp)
{
Error **errp)
{
int ret;
+ BdrvChild *child;
Transaction *tran = tran_new();
GLOBAL_STATE_CODE();
assert(!bs_new->backing);
- ret = bdrv_attach_child_noperm(bs_new, bs_top, "backing",
- &child_of_bds, bdrv_backing_role(bs_new),
- &bs_new->backing, tran, errp);
- if (ret < 0) {
+ child = bdrv_attach_child_noperm(bs_new, bs_top, "backing",
+ &child_of_bds, bdrv_backing_role(bs_new),
+ tran, errp);
+ if (!child) {
+ ret = -EINVAL;
goto out;
}
bdrv_drained_begin(old_bs);
bdrv_drained_begin(new_bs);
- bdrv_replace_child_tran(&child, new_bs, tran, true);
- /* @new_bs must have been non-NULL, so @child must not have been freed */
- assert(child != NULL);
+ bdrv_replace_child_tran(child, new_bs, tran);
found = g_hash_table_new(NULL, NULL);
refresh_list = bdrv_topological_dfs(refresh_list, found, old_bs);
}
}
- for (job = block_job_next(NULL); job; job = block_job_next(job)) {
- GSList *el;
+ WITH_JOB_LOCK_GUARD() {
+ for (job = block_job_next_locked(NULL); job;
+ job = block_job_next_locked(job)) {
+ GSList *el;
- xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB,
- job->job.id);
- for (el = job->nodes; el; el = el->next) {
- xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data);
+ xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB,
+ job->job.id);
+ for (el = job->nodes; el; el = el->next) {
+ xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data);
+ }
}
}
int bdrv_get_flags(BlockDriverState *bs)
{
- GLOBAL_STATE_CODE();
+ IO_CODE();
return bs->open_flags;
}
int ret;
uint64_t cumulative_perms, cumulative_shared_perms;
+ GLOBAL_STATE_CODE();
+
if (!bs->drv) {
return -ENOMEDIUM;
}
BdrvAioNotifier *baf, *baf_tmp;
assert(!bs->walking_aio_notifiers);
+ GLOBAL_STATE_CODE();
bs->walking_aio_notifiers = true;
QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) {
if (baf->deleted) {
if (bs->quiesce_counter) {
aio_enable_external(bs->aio_context);
}
+ assert_bdrv_graph_writable(bs);
bs->aio_context = NULL;
}
AioContext *new_context)
{
BdrvAioNotifier *ban, *ban_tmp;
+ GLOBAL_STATE_CODE();
if (bs->quiesce_counter) {
aio_disable_external(new_context);
}
+ assert_bdrv_graph_writable(bs);
bs->aio_context = new_context;
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
bs->walking_aio_notifiers = false;
}
-/*
- * Changes the AioContext used for fd handlers, timers, and BHs by this
- * BlockDriverState and all its children and parents.
- *
- * Must be called from the main AioContext.
- *
- * 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)
-{
- AioContext *old_context = bdrv_get_aio_context(bs);
- GSList *children_to_process = NULL;
- GSList *parents_to_process = NULL;
- GSList *entry;
- BdrvChild *child, *parent;
-
- g_assert(qemu_get_current_aio_context() == qemu_get_aio_context());
-
- if (old_context == 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);
- children_to_process = g_slist_prepend(children_to_process, child);
- }
-
- QLIST_FOREACH(parent, &bs->parents, next_parent) {
- if (g_slist_find(*ignore, parent)) {
- continue;
- }
- *ignore = g_slist_prepend(*ignore, parent);
- parents_to_process = g_slist_prepend(parents_to_process, parent);
- }
-
- for (entry = children_to_process;
- entry != NULL;
- entry = g_slist_next(entry)) {
- child = entry->data;
- bdrv_set_aio_context_ignore(child->bs, new_context, ignore);
- }
- g_slist_free(children_to_process);
-
- for (entry = parents_to_process;
- entry != NULL;
- entry = g_slist_next(entry)) {
- parent = entry->data;
- assert(parent->klass->set_aio_ctx);
- parent->klass->set_aio_ctx(parent, new_context, ignore);
- }
- g_slist_free(parents_to_process);
-
- bdrv_detach_aio_context(bs);
-
- /* Acquire the new context, if necessary */
- if (qemu_get_aio_context() != new_context) {
- aio_context_acquire(new_context);
- }
-
- bdrv_attach_aio_context(bs, new_context);
-
- /*
- * If this function was recursively called from
- * bdrv_set_aio_context_ignore(), there may be nodes in the
- * subtree that have not yet been moved to the new AioContext.
- * Release the old one so bdrv_drained_end() can poll them.
- */
- if (qemu_get_aio_context() != old_context) {
- aio_context_release(old_context);
- }
-
- bdrv_drained_end(bs);
-
- if (qemu_get_aio_context() != old_context) {
- aio_context_acquire(old_context);
- }
- if (qemu_get_aio_context() != new_context) {
- aio_context_release(new_context);
- }
-}
+typedef struct BdrvStateSetAioContext {
+ AioContext *new_ctx;
+ BlockDriverState *bs;
+} BdrvStateSetAioContext;
-static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx,
- GSList **ignore, Error **errp)
+static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx,
+ GHashTable *visited,
+ Transaction *tran,
+ Error **errp)
{
- if (g_slist_find(*ignore, c)) {
+ GLOBAL_STATE_CODE();
+ if (g_hash_table_contains(visited, c)) {
return true;
}
- *ignore = g_slist_prepend(*ignore, c);
+ g_hash_table_add(visited, c);
/*
* A BdrvChildClass that doesn't handle AioContext changes cannot
* tolerate any AioContext changes
*/
- if (!c->klass->can_set_aio_ctx) {
+ if (!c->klass->change_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->klass->can_set_aio_ctx(c, ctx, ignore, errp)) {
+ if (!c->klass->change_aio_ctx(c, ctx, visited, tran, errp)) {
assert(!errp || *errp);
return false;
}
return true;
}
-bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
- GSList **ignore, Error **errp)
+bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp)
{
GLOBAL_STATE_CODE();
- if (g_slist_find(*ignore, c)) {
+ if (g_hash_table_contains(visited, c)) {
return true;
}
- *ignore = g_slist_prepend(*ignore, c);
- return bdrv_can_set_aio_context(c->bs, ctx, ignore, errp);
+ g_hash_table_add(visited, c);
+ return bdrv_change_aio_context(c->bs, ctx, visited, tran, errp);
+}
+
+static void bdrv_set_aio_context_clean(void *opaque)
+{
+ BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque;
+ BlockDriverState *bs = (BlockDriverState *) state->bs;
+
+ /* Paired with bdrv_drained_begin in bdrv_change_aio_context() */
+ bdrv_drained_end(bs);
+
+ g_free(state);
+}
+
+static void bdrv_set_aio_context_commit(void *opaque)
+{
+ BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque;
+ BlockDriverState *bs = (BlockDriverState *) state->bs;
+ AioContext *new_context = state->new_ctx;
+ AioContext *old_context = bdrv_get_aio_context(bs);
+ assert_bdrv_graph_writable(bs);
+
+ /*
+ * Take the old AioContex when detaching it from bs.
+ * At this point, new_context lock is already acquired, and we are now
+ * also taking old_context. This is safe as long as bdrv_detach_aio_context
+ * does not call AIO_POLL_WHILE().
+ */
+ if (old_context != qemu_get_aio_context()) {
+ aio_context_acquire(old_context);
+ }
+ bdrv_detach_aio_context(bs);
+ if (old_context != qemu_get_aio_context()) {
+ aio_context_release(old_context);
+ }
+ bdrv_attach_aio_context(bs, new_context);
}
-/* @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)
+static TransactionActionDrv set_aio_context = {
+ .commit = bdrv_set_aio_context_commit,
+ .clean = bdrv_set_aio_context_clean,
+};
+
+/*
+ * Changes the AioContext used for fd handlers, timers, and BHs by this
+ * BlockDriverState and all its children and parents.
+ *
+ * Must be called from the main AioContext.
+ *
+ * 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).
+ *
+ * @visited will accumulate all visited BdrvChild objects. The caller is
+ * responsible for freeing the list afterwards.
+ */
+static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp)
{
BdrvChild *c;
+ BdrvStateSetAioContext *state;
+
+ GLOBAL_STATE_CODE();
if (bdrv_get_aio_context(bs) == ctx) {
return true;
}
- GLOBAL_STATE_CODE();
-
QLIST_FOREACH(c, &bs->parents, next_parent) {
- if (!bdrv_parent_can_set_aio_context(c, ctx, ignore, errp)) {
+ if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) {
return false;
}
}
+
QLIST_FOREACH(c, &bs->children, next) {
- if (!bdrv_child_can_set_aio_context(c, ctx, ignore, errp)) {
+ if (!bdrv_child_change_aio_context(c, ctx, visited, tran, errp)) {
return false;
}
}
+ state = g_new(BdrvStateSetAioContext, 1);
+ *state = (BdrvStateSetAioContext) {
+ .new_ctx = ctx,
+ .bs = bs,
+ };
+
+ /* Paired with bdrv_drained_end in bdrv_set_aio_context_clean() */
+ bdrv_drained_begin(bs);
+
+ tran_add(tran, &set_aio_context, state);
+
return true;
}
-int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx,
- BdrvChild *ignore_child, Error **errp)
+/*
+ * Change bs's and recursively all of its parents' and children's AioContext
+ * to the given new context, returning an error if that isn't possible.
+ *
+ * If ignore_child is not NULL, that child (and its subgraph) will not
+ * be touched.
+ *
+ * This function still requires the caller to take the bs current
+ * AioContext lock, otherwise draining will fail since AIO_WAIT_WHILE
+ * assumes the lock is always held if bs is in another AioContext.
+ * For the same reason, it temporarily also holds the new AioContext, since
+ * bdrv_drained_end calls BDRV_POLL_WHILE that assumes the lock is taken too.
+ * Therefore the new AioContext lock must not be taken by the caller.
+ */
+int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
+ BdrvChild *ignore_child, Error **errp)
{
- GSList *ignore;
- bool ret;
-
+ Transaction *tran;
+ GHashTable *visited;
+ int ret;
+ AioContext *old_context = bdrv_get_aio_context(bs);
GLOBAL_STATE_CODE();
- ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL;
- ret = bdrv_can_set_aio_context(bs, ctx, &ignore, errp);
- g_slist_free(ignore);
+ /*
+ * Recursion phase: go through all nodes of the graph.
+ * Take care of checking that all nodes support changing AioContext
+ * and drain them, builing a linear list of callbacks to run if everything
+ * is successful (the transaction itself).
+ */
+ tran = tran_new();
+ visited = g_hash_table_new(NULL, NULL);
+ if (ignore_child) {
+ g_hash_table_add(visited, ignore_child);
+ }
+ ret = bdrv_change_aio_context(bs, ctx, visited, tran, errp);
+ g_hash_table_destroy(visited);
+
+ /*
+ * Linear phase: go through all callbacks collected in the transaction.
+ * Run all callbacks collected in the recursion to switch all nodes
+ * AioContext lock (transaction commit), or undo all changes done in the
+ * recursion (transaction abort).
+ */
if (!ret) {
+ /* Just run clean() callbacks. No AioContext changed. */
+ tran_abort(tran);
return -EPERM;
}
- ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL;
- bdrv_set_aio_context_ignore(bs, ctx, &ignore);
- g_slist_free(ignore);
+ /*
+ * Release old AioContext, it won't be needed anymore, as all
+ * bdrv_drained_begin() have been called already.
+ */
+ if (qemu_get_aio_context() != old_context) {
+ aio_context_release(old_context);
+ }
+
+ /*
+ * Acquire new AioContext since bdrv_drained_end() is going to be called
+ * after we switched all nodes in the new AioContext, and the function
+ * assumes that the lock of the bs is always taken.
+ */
+ if (qemu_get_aio_context() != ctx) {
+ aio_context_acquire(ctx);
+ }
- return 0;
-}
+ tran_commit(tran);
-int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx,
- Error **errp)
-{
- GLOBAL_STATE_CODE();
- return bdrv_child_try_set_aio_context(bs, ctx, NULL, errp);
+ if (qemu_get_aio_context() != ctx) {
+ aio_context_release(ctx);
+ }
+
+ /* Re-acquire the old AioContext, since the caller takes and releases it. */
+ if (qemu_get_aio_context() != old_context) {
+ aio_context_acquire(old_context);
+ }
+
+ return 0;
}
void bdrv_add_aio_context_notifier(BlockDriverState *bs,