aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2022-10-30 15:15:12 -0400
committerStefan Hajnoczi <stefanha@redhat.com>2022-10-30 15:15:12 -0400
commitd5ab9490cdf3ebcd97793982d4664f951816f6db (patch)
tree4d16d89f72ae91858dc0a7a89f1b7d407797364b
parenta8183c34688f7066fc2c169771bcae2b387eb297 (diff)
parentbe8da05b5ed8fb546731b9edb997f303f272bad8 (diff)
downloadqemu-d5ab9490cdf3ebcd97793982d4664f951816f6db.zip
qemu-d5ab9490cdf3ebcd97793982d4664f951816f6db.tar.gz
qemu-d5ab9490cdf3ebcd97793982d4664f951816f6db.tar.bz2
Merge tag 'for-upstream' of https://repo.or.cz/qemu/kevin into staging
Block layer patches - Cleanup bs->backing and bs->file handling - Refactor bdrv_try_set_aio_context using transactions - Changes for improved coroutine_fn consistency - vhost-user-blk: fix the resize crash - io_uring: Use of io_uring_register_ring_fd() led to breakage, revert - vvfat: Fix some problems with r/w mode - Code cleanup - MAINTAINERS: Fold "Block QAPI, monitor, ..." into "Block layer core" # -----BEGIN PGP SIGNATURE----- # # iQJFBAABCAAvFiEE3D3rFZqa+V09dFb+fwmycsiPL9YFAmNazhIRHGt3b2xmQHJl # ZGhhdC5jb20ACgkQfwmycsiPL9ZyTw/8Dfck/SuxfyeLlnQItkjaV4cnqWOU8vHs # 9x0KhlptCs+HXdF/3iicpA0lHojn7mNnbdFGjPRY4E0LriQv91TQ5ycdEmrseFPf # sgeQlgdKCVU/pHjZ2wYarm2pE43Cx85a5xuufmw+7w49dNNZn14l4t+DgviuClVM # nuVaogfZFbYyetre+Qd2TgLl+gJ+0d4o7Zs5lSWLrT8t0L9AGkcWPA7Nrbl6loIE # dOautV4G7jLjuMiCeJZOGcnuRVe3gCQ5rCGBFzzH4DUtz4BmiYx4hd3LMEsP0PMM # CrsfDZS04Ztybl9M7TmJuwkAm1gx1JDMOuJuh18lbJocIOBvhkKKxY2wI5LIdZVI # ZntmU36RowkX+GGu/PYpYyMjBDClJppZCl7vnjyLYsVt6r0Vu6SmlHpJhcRYabhe # 96Kv1LXH9A6+ogKPU3Layw6JGjg01GNr1ALuT7PO3pGto/JshmOuBEJJDucoF84M # 5AfxFCohMROVldwblA6M0eKnlQBgtr5BvtgbV54BBo88VlFJgDJFQn7R09cTFUEo # UwaJoS+nIaiZ0bQQVZhZloVppUaTdVJojzfVRCZZctga96/tu1HSFnGLnbEFpUN3 # KOf+XnVNS6Ro+nPSDf9bMjbIom2JicGFfV+6yMgIoxY/d5UA2dTZfefil4TAlSod # 6PsTgg+jrm8= # =/Fw0 # -----END PGP SIGNATURE----- # gpg: Signature made Thu 27 Oct 2022 14:29:38 EDT # gpg: using RSA key DC3DEB159A9AF95D3D7456FE7F09B272C88F2FD6 # gpg: issuer "kwolf@redhat.com" # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" [full] # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * tag 'for-upstream' of https://repo.or.cz/qemu/kevin: (58 commits) block/block-backend: blk_set_enable_write_cache is IO_CODE monitor: switch to *_co_* functions vmdk: switch to *_co_* functions vhdx: switch to *_co_* functions vdi: switch to *_co_* functions qed: switch to *_co_* functions qcow2: switch to *_co_* functions qcow: switch to *_co_* functions parallels: switch to *_co_* functions mirror: switch to *_co_* functions block: switch to *_co_* functions commit: switch to *_co_* functions vmdk: manually add more coroutine_fn annotations qcow2: manually add more coroutine_fn annotations qcow: manually add more coroutine_fn annotations blkdebug: add missing coroutine_fn annotation for indirect-called functions qcow2: add coroutine_fn annotation for indirect-called functions block: add missing coroutine_fn annotation to BlockDriverState callbacks coroutine-io: add missing coroutine_fn annotation to prototypes coroutine-lock: add missing coroutine_fn annotation to prototypes ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r--MAINTAINERS12
-rw-r--r--block.c855
-rw-r--r--block/backup.c2
-rw-r--r--block/blkdebug.c11
-rw-r--r--block/blklogwrites.c11
-rw-r--r--block/blkreplay.c7
-rw-r--r--block/blkverify.c9
-rw-r--r--block/block-backend.c76
-rw-r--r--block/bochs.c7
-rw-r--r--block/cloop.c7
-rw-r--r--block/commit.c3
-rw-r--r--block/copy-before-write.c9
-rw-r--r--block/copy-on-read.c9
-rw-r--r--block/crypto.c11
-rw-r--r--block/dmg.c7
-rw-r--r--block/export/export.c2
-rw-r--r--block/filter-compress.c8
-rw-r--r--block/io.c8
-rw-r--r--block/io_uring.c13
-rw-r--r--block/mirror.c5
-rw-r--r--block/monitor/block-hmp-cmds.c2
-rw-r--r--block/nfs.c8
-rw-r--r--block/parallels.c35
-rw-r--r--block/preallocate.c9
-rw-r--r--block/qcow.c66
-rw-r--r--block/qcow2-bitmap.c4
-rw-r--r--block/qcow2-cluster.c29
-rw-r--r--block/qcow2-refcount.c18
-rw-r--r--block/qcow2-snapshot.c6
-rw-r--r--block/qcow2.c40
-rw-r--r--block/qcow2.h32
-rw-r--r--block/qed-table.c2
-rw-r--r--block/qed.c20
-rw-r--r--block/raw-format.c4
-rw-r--r--block/replication.c8
-rw-r--r--block/snapshot-access.c6
-rw-r--r--block/snapshot.c59
-rw-r--r--block/ssh.c6
-rw-r--r--block/throttle.c8
-rw-r--r--block/vdi.c24
-rw-r--r--block/vhdx.c15
-rw-r--r--block/vmdk.c95
-rw-r--r--block/vpc.c7
-rw-r--r--block/vvfat.c35
-rw-r--r--blockdev.c24
-rw-r--r--blockjob.c50
-rw-r--r--docs/devel/multiple-iothreads.txt4
-rw-r--r--hw/block/vhost-user-blk.c4
-rw-r--r--include/block/block-common.h39
-rw-r--r--include/block/block-global-state.h18
-rw-r--r--include/block/block-hmp-cmds.h2
-rw-r--r--include/block/block-io.h5
-rw-r--r--include/block/block_int-common.h49
-rw-r--r--include/monitor/hmp.h3
-rw-r--r--include/qemu/coroutine.h18
-rw-r--r--job.c2
-rw-r--r--meson.build1
-rwxr-xr-xtests/qemu-iotests/0513
-rw-r--r--tests/qemu-iotests/051.out2
-rw-r--r--tests/qemu-iotests/051.pc.out2
-rw-r--r--tests/unit/test-bdrv-drain.c17
-rw-r--r--tests/unit/test-bdrv-graph-mod.c104
-rw-r--r--tests/unit/test-block-iothread.c10
63 files changed, 984 insertions, 983 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 64893e3..1706a08 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2509,6 +2509,8 @@ S: Supported
F: block*
F: block/
F: hw/block/
+F: qapi/block*.json
+F: qapi/transaction.json
F: include/block/
F: include/sysemu/block-*.h
F: qemu-img*
@@ -2583,16 +2585,6 @@ F: include/qemu/co-shared-resource.h
T: git https://gitlab.com/jsnow/qemu.git jobs
T: git https://gitlab.com/vsementsov/qemu.git block
-Block QAPI, monitor, command line
-M: Markus Armbruster <armbru@redhat.com>
-S: Supported
-F: blockdev.c
-F: blockdev-hmp-cmds.c
-F: block/qapi.c
-F: qapi/block*.json
-F: qapi/transaction.json
-T: git https://repo.or.cz/qemu/armbru.git block-next
-
Compute Express Link
M: Ben Widawsky <ben.widawsky@intel.com>
M: Jonathan Cameron <jonathan.cameron@huawei.com>
diff --git a/block.c b/block.c
index c69be2c..3bd594e 100644
--- a/block.c
+++ b/block.c
@@ -90,13 +90,9 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
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);
@@ -108,6 +104,10 @@ static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
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;
@@ -643,7 +643,7 @@ create_file_fallback_zero_first_sector(BlockBackend *blk,
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");
@@ -861,38 +861,42 @@ int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
/*
* 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);
}
/*
@@ -1238,18 +1242,12 @@ static int bdrv_child_cb_inactivate(BdrvChild *child)
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);
}
/*
@@ -1443,9 +1441,39 @@ static void bdrv_child_cb_attach(BdrvChild *child)
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);
@@ -1463,6 +1491,12 @@ static void bdrv_child_cb_detach(BdrvChild *child)
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,
@@ -1492,8 +1526,7 @@ const BdrvChildClass child_of_bds = {
.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,
};
@@ -1682,7 +1715,7 @@ open_failed:
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;
@@ -2353,9 +2386,7 @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm,
typedef struct BdrvReplaceChildState {
BdrvChild *child;
- BdrvChild **childp;
BlockDriverState *old_bs;
- bool free_empty_child;
} BdrvReplaceChildState;
static void bdrv_replace_child_commit(void *opaque)
@@ -2363,9 +2394,6 @@ 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);
}
@@ -2375,34 +2403,8 @@ static void bdrv_replace_child_abort(void *opaque)
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);
}
@@ -2418,46 +2420,22 @@ static TransactionActionDrv bdrv_replace_child_drv = {
* 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 */
}
/*
@@ -2838,24 +2816,9 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
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;
@@ -2892,9 +2855,6 @@ static void bdrv_replace_child_noperm(BdrvChild **childp,
}
child->bs = new_bs;
- if (!new_bs) {
- *childp = NULL;
- }
if (new_bs) {
assert_bdrv_graph_writable(new_bs);
@@ -2925,10 +2885,6 @@ static void bdrv_replace_child_noperm(BdrvChild **childp,
bdrv_parent_drained_end_single(child);
drain_saldo++;
}
-
- if (free_empty_child && !child->bs) {
- bdrv_child_free(child);
- }
}
/**
@@ -2948,7 +2904,7 @@ static void bdrv_child_free(BdrvChild *child)
}
typedef struct BdrvAttachChildCommonState {
- BdrvChild **child;
+ BdrvChild *child;
AioContext *old_parent_ctx;
AioContext *old_child_ctx;
} BdrvAttachChildCommonState;
@@ -2956,39 +2912,35 @@ typedef struct 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;
GLOBAL_STATE_CODE();
- /*
- * 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);
+ 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 = {
@@ -2999,28 +2951,22 @@ 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();
@@ -3043,63 +2989,57 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
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);
@@ -3108,29 +3048,25 @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs,
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) {
/*
@@ -3144,7 +3080,7 @@ static void bdrv_detach_child(BdrvChild **childp)
* 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);
}
}
@@ -3166,15 +3102,16 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
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;
}
@@ -3182,11 +3119,10 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
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;
}
/*
@@ -3208,14 +3144,15 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
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;
}
@@ -3226,12 +3163,10 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs,
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. */
@@ -3242,7 +3177,7 @@ void bdrv_root_unref_child(BdrvChild *child)
GLOBAL_STATE_CODE();
child_bs = child->bs;
- bdrv_detach_child(&child);
+ bdrv_detach_child(child);
bdrv_unref(child_bs);
}
@@ -3373,7 +3308,6 @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs,
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;
@@ -3424,21 +3358,19 @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs,
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;
}
@@ -3684,6 +3616,29 @@ BdrvChild *bdrv_open_child(const char *filename,
}
/*
+ * 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.
*/
@@ -3732,8 +3687,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
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;
@@ -3752,9 +3706,8 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
}
/* 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;
}
@@ -3788,7 +3741,6 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
out:
qobject_unref(snapshot_options);
- g_free(tmp_filename);
return bs_snapshot;
}
@@ -4955,8 +4907,8 @@ static void bdrv_close(BlockDriverState *bs)
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);
@@ -5087,96 +5039,28 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
return ret;
}
-typedef struct BdrvRemoveFilterOrCowChild {
- BdrvChild *child;
- BlockDriverState *bs;
- bool is_backing;
-} BdrvRemoveFilterOrCowChild;
-
-static void bdrv_remove_filter_or_cow_child_abort(void *opaque)
+static void bdrv_remove_child_commit(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)
-{
- BdrvRemoveFilterOrCowChild *s = opaque;
GLOBAL_STATE_CODE();
- bdrv_child_free(s->child);
-}
-
-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);
+ bdrv_child_free(opaque);
}
-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);
}
/*
@@ -5187,7 +5071,7 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
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,
@@ -5197,7 +5081,6 @@ 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) {
@@ -5215,12 +5098,7 @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
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;
@@ -5235,8 +5113,6 @@ static int 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.
- *
- * @to must not be NULL.
*/
static int bdrv_replace_node_common(BlockDriverState *from,
BlockDriverState *to,
@@ -5250,7 +5126,6 @@ static int bdrv_replace_node_common(BlockDriverState *from,
int ret;
GLOBAL_STATE_CODE();
- assert(to != NULL);
if (detach_subchain) {
assert(bdrv_chain_contains(from, to));
@@ -5307,9 +5182,6 @@ out:
return ret;
}
-/**
- * Replace node @from by @to (where neither may be NULL).
- */
int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
Error **errp)
{
@@ -5342,16 +5214,18 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top,
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;
}
@@ -5385,9 +5259,7 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
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);
@@ -7299,6 +7171,7 @@ static void bdrv_detach_aio_context(BlockDriverState *bs)
if (bs->quiesce_counter) {
aio_enable_external(bs->aio_context);
}
+ assert_bdrv_graph_writable(bs);
bs->aio_context = NULL;
}
@@ -7312,6 +7185,7 @@ static void bdrv_attach_aio_context(BlockDriverState *bs,
aio_disable_external(new_context);
}
+ assert_bdrv_graph_writable(bs);
bs->aio_context = new_context;
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
@@ -7330,191 +7204,222 @@ static void bdrv_attach_aio_context(BlockDriverState *bs,
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());
- GLOBAL_STATE_CODE();
-
- 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)
{
GLOBAL_STATE_CODE();
- if (g_slist_find(*ignore, c)) {
+ 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,
diff --git a/block/backup.c b/block/backup.c
index b2b649e..6a9ad97 100644
--- a/block/backup.c
+++ b/block/backup.c
@@ -309,7 +309,7 @@ static void coroutine_fn backup_pause(Job *job)
}
}
-static void coroutine_fn backup_set_speed(BlockJob *job, int64_t speed)
+static void backup_set_speed(BlockJob *job, int64_t speed)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
diff --git a/block/blkdebug.c b/block/blkdebug.c
index bbf2948..4265ca1 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -503,12 +503,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
}
/* Open the image file */
- bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
- bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- ret = -EINVAL;
+ ret = bdrv_open_file_child(qemu_opt_get(opts, "x-image"), options, "image",
+ bs, errp);
+ if (ret < 0) {
goto out;
}
@@ -672,7 +669,7 @@ blkdebug_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
}
-static int blkdebug_co_flush(BlockDriverState *bs)
+static int coroutine_fn blkdebug_co_flush(BlockDriverState *bs)
{
int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
index e3c6c40..cef9efe 100644
--- a/block/blklogwrites.c
+++ b/block/blklogwrites.c
@@ -155,11 +155,8 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
}
/* Open the file */
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false,
- errp);
- if (!bs->file) {
- ret = -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
goto fail;
}
@@ -257,10 +254,6 @@ fail_log:
s->log_file = NULL;
}
fail:
- if (ret < 0) {
- bdrv_unref_child(bs, bs->file);
- bs->file = NULL;
- }
qemu_opts_del(opts);
return ret;
}
diff --git a/block/blkreplay.c b/block/blkreplay.c
index dcbe780..76a0b8d 100644
--- a/block/blkreplay.c
+++ b/block/blkreplay.c
@@ -26,11 +26,8 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
int ret;
/* Open the image file */
- bs->file = bdrv_open_child(NULL, options, "image", bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- ret = -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "image", bs, errp);
+ if (ret < 0) {
goto fail;
}
diff --git a/block/blkverify.c b/block/blkverify.c
index f36fd6a..c60a2dc 100644
--- a/block/blkverify.c
+++ b/block/blkverify.c
@@ -122,12 +122,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
}
/* Open the raw file */
- bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw",
- bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- ret = -EINVAL;
+ ret = bdrv_open_file_child(qemu_opt_get(opts, "x-raw"), options, "raw",
+ bs, errp);
+ if (ret < 0) {
goto fail;
}
diff --git a/block/block-backend.c b/block/block-backend.c
index 4f59664..c0c7d56 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -134,10 +134,9 @@ static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter);
static void blk_root_change_media(BdrvChild *child, bool load);
static void blk_root_resize(BdrvChild *child);
-static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx,
- GSList **ignore, Error **errp);
-static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx,
- GSList **ignore);
+static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp);
static char *blk_root_get_parent_desc(BdrvChild *child)
{
@@ -334,8 +333,7 @@ static const BdrvChildClass child_root = {
.attach = blk_root_attach,
.detach = blk_root_detach,
- .can_set_aio_ctx = blk_root_can_set_aio_ctx,
- .set_aio_ctx = blk_root_set_aio_ctx,
+ .change_aio_ctx = blk_root_change_aio_ctx,
.get_parent_aio_context = blk_root_get_parent_aio_context,
};
@@ -1946,7 +1944,7 @@ bool blk_enable_write_cache(BlockBackend *blk)
void blk_set_enable_write_cache(BlockBackend *blk, bool wce)
{
- GLOBAL_STATE_CODE();
+ IO_CODE();
blk->enable_write_cache = wce;
}
@@ -2149,8 +2147,11 @@ static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context,
bdrv_ref(bs);
if (update_root_node) {
- ret = bdrv_child_try_set_aio_context(bs, new_context, blk->root,
- errp);
+ /*
+ * update_root_node MUST be false for blk_root_set_aio_ctx_commit(),
+ * as we are already in the commit function of a transaction.
+ */
+ ret = bdrv_try_change_aio_context(bs, new_context, blk->root, errp);
if (ret < 0) {
bdrv_unref(bs);
return ret;
@@ -2177,33 +2178,54 @@ int blk_set_aio_context(BlockBackend *blk, AioContext *new_context,
return blk_do_set_aio_context(blk, new_context, true, errp);
}
-static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx,
- GSList **ignore, Error **errp)
+typedef struct BdrvStateBlkRootContext {
+ AioContext *new_ctx;
+ BlockBackend *blk;
+} BdrvStateBlkRootContext;
+
+static void blk_root_set_aio_ctx_commit(void *opaque)
+{
+ BdrvStateBlkRootContext *s = opaque;
+ BlockBackend *blk = s->blk;
+
+ blk_do_set_aio_context(blk, s->new_ctx, false, &error_abort);
+}
+
+static TransactionActionDrv set_blk_root_context = {
+ .commit = blk_root_set_aio_ctx_commit,
+ .clean = g_free,
+};
+
+static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp)
{
BlockBackend *blk = child->opaque;
+ BdrvStateBlkRootContext *s;
- if (blk->allow_aio_context_change) {
- return true;
+ if (!blk->allow_aio_context_change) {
+ /*
+ * Manually created BlockBackends (those with a name) that are not
+ * attached to anything can change their AioContext without updating
+ * their user; return an error for others.
+ */
+ if (!blk->name || blk->dev) {
+ /* TODO Add BB name/QOM path */
+ error_setg(errp, "Cannot change iothread of active block backend");
+ return false;
+ }
}
- /* Only manually created BlockBackends that are not attached to anything
- * can change their AioContext without updating their user. */
- if (!blk->name || blk->dev) {
- /* TODO Add BB name/QOM path */
- error_setg(errp, "Cannot change iothread of active block backend");
- return false;
- }
+ s = g_new(BdrvStateBlkRootContext, 1);
+ *s = (BdrvStateBlkRootContext) {
+ .new_ctx = ctx,
+ .blk = blk,
+ };
+ tran_add(tran, &set_blk_root_context, s);
return true;
}
-static void blk_root_set_aio_ctx(BdrvChild *child, AioContext *ctx,
- GSList **ignore)
-{
- BlockBackend *blk = child->opaque;
- blk_do_set_aio_context(blk, ctx, false, &error_abort);
-}
-
void blk_add_aio_context_notifier(BlockBackend *blk,
void (*attached_aio_context)(AioContext *new_context, void *opaque),
void (*detach_aio_context)(void *opaque), void *opaque)
diff --git a/block/bochs.c b/block/bochs.c
index b76f34f..e30e390 100644
--- a/block/bochs.c
+++ b/block/bochs.c
@@ -110,10 +110,9 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
return ret;
}
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
ret = bdrv_pread(bs->file, 0, sizeof(bochs), &bochs, 0);
diff --git a/block/cloop.c b/block/cloop.c
index 40b146e..3ff975a 100644
--- a/block/cloop.c
+++ b/block/cloop.c
@@ -71,10 +71,9 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
return ret;
}
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
/* read header */
diff --git a/block/commit.c b/block/commit.c
index 3857151..0029b31 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -135,7 +135,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
}
if (base_len < len) {
- ret = blk_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL);
+ ret = blk_co_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL);
if (ret) {
return ret;
}
@@ -238,6 +238,7 @@ static BlockDriver bdrv_commit_top = {
.bdrv_child_perm = bdrv_commit_top_child_perm,
.is_filter = true,
+ .filtered_child_is_backing = true,
};
void commit_start(const char *job_id, BlockDriverState *bs,
diff --git a/block/copy-before-write.c b/block/copy-before-write.c
index afbdd04..4abaa73 100644
--- a/block/copy-before-write.c
+++ b/block/copy-before-write.c
@@ -412,6 +412,7 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
int64_t cluster_size;
g_autoptr(BlockdevOptions) full_opts = NULL;
BlockdevOptionsCbw *opts;
+ int ret;
full_opts = cbw_parse_options(options, errp);
if (!full_opts) {
@@ -420,11 +421,9 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
assert(full_opts->driver == BLOCKDEV_DRIVER_COPY_BEFORE_WRITE);
opts = &full_opts->u.copy_before_write;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
s->target = bdrv_open_child(NULL, options, "target", bs, &child_of_bds,
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
index 1fc7fb3..815ac1d 100644
--- a/block/copy-on-read.c
+++ b/block/copy-on-read.c
@@ -41,12 +41,11 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
BDRVStateCOR *state = bs->opaque;
/* Find a bottom node name, if any */
const char *bottom_node = qdict_get_try_str(options, "bottom");
+ int ret;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
bs->supported_read_flags = BDRV_REQ_PREFETCH;
diff --git a/block/crypto.c b/block/crypto.c
index c736559..2fb8add 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -261,15 +261,14 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
{
BlockCrypto *crypto = bs->opaque;
QemuOpts *opts = NULL;
- int ret = -EINVAL;
+ int ret;
QCryptoBlockOpenOptions *open_opts = NULL;
unsigned int cflags = 0;
QDict *cryptoopts = NULL;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
bs->supported_write_flags = BDRV_REQ_FUA &
@@ -277,6 +276,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort);
if (!qemu_opts_absorb_qdict(opts, options, errp)) {
+ ret = -EINVAL;
goto cleanup;
}
@@ -285,6 +285,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
open_opts = block_crypto_open_opts_init(cryptoopts, errp);
if (!open_opts) {
+ ret = -EINVAL;
goto cleanup;
}
diff --git a/block/dmg.c b/block/dmg.c
index 98db18d..4221362 100644
--- a/block/dmg.c
+++ b/block/dmg.c
@@ -440,10 +440,9 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
return ret;
}
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
block_module_load_one("dmg-bz2");
diff --git a/block/export/export.c b/block/export/export.c
index 4744862..7cc0c25 100644
--- a/block/export/export.c
+++ b/block/export/export.c
@@ -129,7 +129,7 @@ BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp)
/* Ignore errors with fixed-iothread=false */
set_context_errp = fixed_iothread ? errp : NULL;
- ret = bdrv_try_set_aio_context(bs, new_ctx, set_context_errp);
+ ret = bdrv_try_change_aio_context(bs, new_ctx, NULL, set_context_errp);
if (ret == 0) {
aio_context_release(ctx);
aio_context_acquire(new_ctx);
diff --git a/block/filter-compress.c b/block/filter-compress.c
index d5be538..305716c 100644
--- a/block/filter-compress.c
+++ b/block/filter-compress.c
@@ -30,11 +30,9 @@
static int compress_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- return -EINVAL;
+ int ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
if (!bs->file->bs->drv || !block_driver_can_compress(bs->file->bs->drv)) {
diff --git a/block/io.c b/block/io.c
index a967346..34b30e3 100644
--- a/block/io.c
+++ b/block/io.c
@@ -2744,8 +2744,8 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
return 1;
}
- ret = bdrv_common_block_status_above(bs, NULL, false, false, offset,
- bytes, &pnum, NULL, NULL, NULL);
+ ret = bdrv_co_common_block_status_above(bs, NULL, false, false, offset,
+ bytes, &pnum, NULL, NULL, NULL);
if (ret < 0) {
return ret;
@@ -2754,8 +2754,8 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset,
return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO);
}
-int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
- int64_t bytes, int64_t *pnum)
+int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
+ int64_t *pnum)
{
int ret;
int64_t dummy;
diff --git a/block/io_uring.c b/block/io_uring.c
index a176015..973e15d 100644
--- a/block/io_uring.c
+++ b/block/io_uring.c
@@ -11,7 +11,6 @@
#include "qemu/osdep.h"
#include <liburing.h>
#include "block/aio.h"
-#include "qemu/error-report.h"
#include "qemu/queue.h"
#include "block/block.h"
#include "block/raw-aio.h"
@@ -19,7 +18,6 @@
#include "qapi/error.h"
#include "trace.h"
-
/* io_uring ring size */
#define MAX_ENTRIES 128
@@ -432,17 +430,8 @@ LuringState *luring_init(Error **errp)
}
ioq_init(&s->io_q);
-#ifdef CONFIG_LIBURING_REGISTER_RING_FD
- if (io_uring_register_ring_fd(&s->ring) < 0) {
- /*
- * Only warn about this error: we will fallback to the non-optimized
- * io_uring operations.
- */
- warn_report("failed to register linux io_uring ring file descriptor");
- }
-#endif
-
return s;
+
}
void luring_cleanup(LuringState *s)
diff --git a/block/mirror.c b/block/mirror.c
index bed089d..1a75a47 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -922,8 +922,8 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
* active layer. */
if (s->base == blk_bs(s->target)) {
if (s->bdev_length > target_length) {
- ret = blk_truncate(s->target, s->bdev_length, false,
- PREALLOC_MODE_OFF, 0, NULL);
+ ret = blk_co_truncate(s->target, s->bdev_length, false,
+ PREALLOC_MODE_OFF, 0, NULL);
if (ret < 0) {
goto immediate_exit;
}
@@ -1589,6 +1589,7 @@ static BlockDriver bdrv_mirror_top = {
.bdrv_child_perm = bdrv_mirror_top_child_perm,
.is_filter = true,
+ .filtered_child_is_backing = true,
};
static BlockJob *mirror_start_job(
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index 939a520..b6135e9 100644
--- a/block/monitor/block-hmp-cmds.c
+++ b/block/monitor/block-hmp-cmds.c
@@ -489,7 +489,7 @@ void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, err);
}
-void hmp_block_resize(Monitor *mon, const QDict *qdict)
+void coroutine_fn hmp_block_resize(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
int64_t size = qdict_get_int(qdict, "size");
diff --git a/block/nfs.c b/block/nfs.c
index 596ebe9..ece2235 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -418,7 +418,11 @@ static int64_t nfs_client_open(NFSClient *client, BlockdevOptionsNfs *opts,
int flags, int open_flags, Error **errp)
{
int64_t ret = -EINVAL;
+#ifdef _WIN32
+ struct __stat64 st;
+#else
struct stat st;
+#endif
char *file = NULL, *strp = NULL;
qemu_mutex_init(&client->mutex);
@@ -781,7 +785,11 @@ static int nfs_reopen_prepare(BDRVReopenState *state,
BlockReopenQueue *queue, Error **errp)
{
NFSClient *client = state->bs->opaque;
+#ifdef _WIN32
+ struct __stat64 st;
+#else
struct stat st;
+#endif
int ret = 0;
if (state->flags & BDRV_O_RDWR && bdrv_is_read_only(state->bs)) {
diff --git a/block/parallels.c b/block/parallels.c
index dd15a44..fa08c11 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -205,18 +205,18 @@ static coroutine_fn int64_t allocate_clusters(BlockDriverState *bs,
* force the safer-but-slower fallocate.
*/
if (s->prealloc_mode == PRL_PREALLOC_MODE_TRUNCATE) {
- ret = bdrv_truncate(bs->file,
- (s->data_end + space) << BDRV_SECTOR_BITS,
- false, PREALLOC_MODE_OFF, BDRV_REQ_ZERO_WRITE,
- NULL);
+ ret = bdrv_co_truncate(bs->file,
+ (s->data_end + space) << BDRV_SECTOR_BITS,
+ false, PREALLOC_MODE_OFF,
+ BDRV_REQ_ZERO_WRITE, NULL);
if (ret == -ENOTSUP) {
s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE;
}
}
if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) {
- ret = bdrv_pwrite_zeroes(bs->file,
- s->data_end << BDRV_SECTOR_BITS,
- space << BDRV_SECTOR_BITS, 0);
+ ret = bdrv_co_pwrite_zeroes(bs->file,
+ s->data_end << BDRV_SECTOR_BITS,
+ space << BDRV_SECTOR_BITS, 0);
}
if (ret < 0) {
return ret;
@@ -278,8 +278,8 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs)
if (off + to_write > s->header_size) {
to_write = s->header_size - off;
}
- ret = bdrv_pwrite(bs->file, off, to_write, (uint8_t *)s->header + off,
- 0);
+ ret = bdrv_co_pwrite(bs->file, off, to_write,
+ (uint8_t *)s->header + off, 0);
if (ret < 0) {
qemu_co_mutex_unlock(&s->lock);
return ret;
@@ -503,8 +503,8 @@ static int coroutine_fn parallels_co_check(BlockDriverState *bs,
* In order to really repair the image, we must shrink it.
* That means we have to pass exact=true.
*/
- ret = bdrv_truncate(bs->file, res->image_end_offset, true,
- PREALLOC_MODE_OFF, 0, &local_err);
+ ret = bdrv_co_truncate(bs->file, res->image_end_offset, true,
+ PREALLOC_MODE_OFF, 0, &local_err);
if (ret < 0) {
error_report_err(local_err);
res->check_errors++;
@@ -599,12 +599,12 @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
memset(tmp, 0, sizeof(tmp));
memcpy(tmp, &header, sizeof(header));
- ret = blk_pwrite(blk, 0, BDRV_SECTOR_SIZE, tmp, 0);
+ ret = blk_co_pwrite(blk, 0, BDRV_SECTOR_SIZE, tmp, 0);
if (ret < 0) {
goto exit;
}
- ret = blk_pwrite_zeroes(blk, BDRV_SECTOR_SIZE,
- (bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
+ ret = blk_co_pwrite_zeroes(blk, BDRV_SECTOR_SIZE,
+ (bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
if (ret < 0) {
goto exit;
}
@@ -736,10 +736,9 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
Error *local_err = NULL;
char *buf;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
ret = bdrv_pread(bs->file, 0, sizeof(ph), &ph, 0);
diff --git a/block/preallocate.c b/block/preallocate.c
index e15cb8c..d50ee7f 100644
--- a/block/preallocate.c
+++ b/block/preallocate.c
@@ -134,6 +134,7 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVPreallocateState *s = bs->opaque;
+ int ret;
/*
* s->data_end and friends should be initialized on permission update.
@@ -141,11 +142,9 @@ static int preallocate_open(BlockDriverState *bs, QDict *options, int flags,
*/
s->file_end = s->zero_start = s->data_end = -EINVAL;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
if (!preallocate_absorb_opts(&s->opts, options, bs->file->bs, errp)) {
diff --git a/block/qcow.c b/block/qcow.c
index e9180c7..daa3883 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -92,7 +92,8 @@ typedef struct BDRVQcowState {
static QemuOptsList qcow_create_opts;
-static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
+static int coroutine_fn decompress_cluster(BlockDriverState *bs,
+ uint64_t cluster_offset);
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
{
@@ -121,10 +122,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
qdict_extract_subqdict(options, &encryptopts, "encrypt.");
encryptfmt = qdict_get_try_str(encryptopts, "format");
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- ret = -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
goto fail;
}
@@ -351,10 +350,11 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
* return 0 if not allocated, 1 if *result is assigned, and negative
* errno on failure.
*/
-static int get_cluster_offset(BlockDriverState *bs,
- uint64_t offset, int allocate,
- int compressed_size,
- int n_start, int n_end, uint64_t *result)
+static int coroutine_fn get_cluster_offset(BlockDriverState *bs,
+ uint64_t offset, int allocate,
+ int compressed_size,
+ int n_start, int n_end,
+ uint64_t *result)
{
BDRVQcowState *s = bs->opaque;
int min_index, i, j, l1_index, l2_index, ret;
@@ -381,9 +381,9 @@ static int get_cluster_offset(BlockDriverState *bs,
s->l1_table[l1_index] = l2_offset;
tmp = cpu_to_be64(l2_offset);
BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
- ret = bdrv_pwrite_sync(bs->file,
- s->l1_table_offset + l1_index * sizeof(tmp),
- sizeof(tmp), &tmp, 0);
+ ret = bdrv_co_pwrite_sync(bs->file,
+ s->l1_table_offset + l1_index * sizeof(tmp),
+ sizeof(tmp), &tmp, 0);
if (ret < 0) {
return ret;
}
@@ -414,14 +414,14 @@ static int get_cluster_offset(BlockDriverState *bs,
BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
if (new_l2_table) {
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
- ret = bdrv_pwrite_sync(bs->file, l2_offset,
- s->l2_size * sizeof(uint64_t), l2_table, 0);
+ ret = bdrv_co_pwrite_sync(bs->file, l2_offset,
+ s->l2_size * sizeof(uint64_t), l2_table, 0);
if (ret < 0) {
return ret;
}
} else {
- ret = bdrv_pread(bs->file, l2_offset, s->l2_size * sizeof(uint64_t),
- l2_table, 0);
+ ret = bdrv_co_pread(bs->file, l2_offset,
+ s->l2_size * sizeof(uint64_t), l2_table, 0);
if (ret < 0) {
return ret;
}
@@ -453,8 +453,8 @@ static int get_cluster_offset(BlockDriverState *bs,
cluster_offset = QEMU_ALIGN_UP(cluster_offset, s->cluster_size);
/* write the cluster content */
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
- ret = bdrv_pwrite(bs->file, cluster_offset, s->cluster_size,
- s->cluster_cache, 0);
+ ret = bdrv_co_pwrite(bs->file, cluster_offset, s->cluster_size,
+ s->cluster_cache, 0);
if (ret < 0) {
return ret;
}
@@ -469,8 +469,9 @@ static int get_cluster_offset(BlockDriverState *bs,
if (cluster_offset + s->cluster_size > INT64_MAX) {
return -E2BIG;
}
- ret = bdrv_truncate(bs->file, cluster_offset + s->cluster_size,
- false, PREALLOC_MODE_OFF, 0, NULL);
+ ret = bdrv_co_truncate(bs->file,
+ cluster_offset + s->cluster_size,
+ false, PREALLOC_MODE_OFF, 0, NULL);
if (ret < 0) {
return ret;
}
@@ -492,9 +493,9 @@ static int get_cluster_offset(BlockDriverState *bs,
return -EIO;
}
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
- ret = bdrv_pwrite(bs->file, cluster_offset + i,
- BDRV_SECTOR_SIZE,
- s->cluster_data, 0);
+ ret = bdrv_co_pwrite(bs->file, cluster_offset + i,
+ BDRV_SECTOR_SIZE,
+ s->cluster_data, 0);
if (ret < 0) {
return ret;
}
@@ -514,8 +515,8 @@ static int get_cluster_offset(BlockDriverState *bs,
} else {
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
}
- ret = bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
- sizeof(tmp), &tmp, 0);
+ ret = bdrv_co_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp),
+ sizeof(tmp), &tmp, 0);
if (ret < 0) {
return ret;
}
@@ -585,7 +586,8 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
return 0;
}
-static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
+static int coroutine_fn decompress_cluster(BlockDriverState *bs,
+ uint64_t cluster_offset)
{
BDRVQcowState *s = bs->opaque;
int ret, csize;
@@ -596,7 +598,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
csize = cluster_offset >> (63 - s->cluster_bits);
csize &= (s->cluster_size - 1);
BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED);
- ret = bdrv_pread(bs->file, coffset, csize, s->cluster_data, 0);
+ ret = bdrv_co_pread(bs->file, coffset, csize, s->cluster_data, 0);
if (ret < 0)
return -1;
if (decompress_buffer(s->cluster_cache, s->cluster_size,
@@ -888,14 +890,14 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
}
/* write all the data */
- ret = blk_pwrite(qcow_blk, 0, sizeof(header), &header, 0);
+ ret = blk_co_pwrite(qcow_blk, 0, sizeof(header), &header, 0);
if (ret < 0) {
goto exit;
}
if (qcow_opts->has_backing_file) {
- ret = blk_pwrite(qcow_blk, sizeof(header), backing_filename_len,
- qcow_opts->backing_file, 0);
+ ret = blk_co_pwrite(qcow_blk, sizeof(header), backing_filename_len,
+ qcow_opts->backing_file, 0);
if (ret < 0) {
goto exit;
}
@@ -904,8 +906,8 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
tmp = g_malloc0(BDRV_SECTOR_SIZE);
for (i = 0; i < DIV_ROUND_UP(sizeof(uint64_t) * l1_size, BDRV_SECTOR_SIZE);
i++) {
- ret = blk_pwrite(qcow_blk, header_size + BDRV_SECTOR_SIZE * i,
- BDRV_SECTOR_SIZE, tmp, 0);
+ ret = blk_co_pwrite(qcow_blk, header_size + BDRV_SECTOR_SIZE * i,
+ BDRV_SECTOR_SIZE, tmp, 0);
if (ret < 0) {
g_free(tmp);
goto exit;
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 7197754..bcad567 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -955,8 +955,8 @@ static void set_readonly_helper(gpointer bitmap, gpointer value)
* If header_updated is not NULL then it is set appropriately regardless of
* the return value.
*/
-bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated,
- Error **errp)
+bool coroutine_fn qcow2_load_dirty_bitmaps(BlockDriverState *bs,
+ bool *header_updated, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
Qcow2BitmapList *bm_list;
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 0f29395..40ed847 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -31,7 +31,8 @@
#include "qemu/memalign.h"
#include "trace.h"
-int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size)
+int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs,
+ uint64_t exact_size)
{
BDRVQcow2State *s = bs->opaque;
int new_l1_size, i, ret;
@@ -47,14 +48,14 @@ int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t exact_size)
#endif
BLKDBG_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE);
- ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset +
- new_l1_size * L1E_SIZE,
- (s->l1_size - new_l1_size) * L1E_SIZE, 0);
+ ret = bdrv_co_pwrite_zeroes(bs->file,
+ s->l1_table_offset + new_l1_size * L1E_SIZE,
+ (s->l1_size - new_l1_size) * L1E_SIZE, 0);
if (ret < 0) {
goto fail;
}
- ret = bdrv_flush(bs->file->bs);
+ ret = bdrv_co_flush(bs->file->bs);
if (ret < 0) {
goto fail;
}
@@ -823,10 +824,10 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
*
* Return 0 on success and -errno in error cases
*/
-int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
- uint64_t offset,
- int compressed_size,
- uint64_t *host_offset)
+int coroutine_fn qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
+ uint64_t offset,
+ int compressed_size,
+ uint64_t *host_offset)
{
BDRVQcow2State *s = bs->opaque;
int l2_index, ret;
@@ -1488,8 +1489,9 @@ static int coroutine_fn handle_dependencies(BlockDriverState *bs,
*
* -errno: in error cases
*/
-static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
- uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m)
+static int coroutine_fn handle_copied(BlockDriverState *bs,
+ uint64_t guest_offset, uint64_t *host_offset, uint64_t *bytes,
+ QCowL2Meta **m)
{
BDRVQcow2State *s = bs->opaque;
int l2_index;
@@ -1653,8 +1655,9 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
*
* -errno: in error cases
*/
-static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
- uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m)
+static int coroutine_fn handle_alloc(BlockDriverState *bs,
+ uint64_t guest_offset, uint64_t *host_offset, uint64_t *bytes,
+ QCowL2Meta **m)
{
BDRVQcow2State *s = bs->opaque;
int l2_index;
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 1fbb07c..8126474 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -97,7 +97,7 @@ static void update_max_refcount_table_index(BDRVQcow2State *s)
s->max_refcount_table_index = i;
}
-int qcow2_refcount_init(BlockDriverState *bs)
+int coroutine_fn qcow2_refcount_init(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
unsigned int refcount_table_size2, i;
@@ -118,8 +118,8 @@ int qcow2_refcount_init(BlockDriverState *bs)
goto fail;
}
BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD);
- ret = bdrv_pread(bs->file, s->refcount_table_offset,
- refcount_table_size2, s->refcount_table, 0);
+ ret = bdrv_co_pread(bs->file, s->refcount_table_offset,
+ refcount_table_size2, s->refcount_table, 0);
if (ret < 0) {
goto fail;
}
@@ -3559,8 +3559,8 @@ static int64_t get_refblock_offset(BlockDriverState *bs, uint64_t offset)
return covering_refblock_offset;
}
-static int qcow2_discard_refcount_block(BlockDriverState *bs,
- uint64_t discard_block_offs)
+static int coroutine_fn
+qcow2_discard_refcount_block(BlockDriverState *bs, uint64_t discard_block_offs)
{
BDRVQcow2State *s = bs->opaque;
int64_t refblock_offs;
@@ -3616,7 +3616,7 @@ static int qcow2_discard_refcount_block(BlockDriverState *bs,
return 0;
}
-int qcow2_shrink_reftable(BlockDriverState *bs)
+int coroutine_fn qcow2_shrink_reftable(BlockDriverState *bs)
{
BDRVQcow2State *s = bs->opaque;
uint64_t *reftable_tmp =
@@ -3657,9 +3657,9 @@ int qcow2_shrink_reftable(BlockDriverState *bs)
reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]);
}
- ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset,
- s->refcount_table_size * REFTABLE_ENTRY_SIZE,
- reftable_tmp, 0);
+ ret = bdrv_co_pwrite_sync(bs->file, s->refcount_table_offset,
+ s->refcount_table_size * REFTABLE_ENTRY_SIZE,
+ reftable_tmp, 0);
/*
* If the write in the reftable failed the image may contain a partially
* overwritten reftable. In this case it would be better to clear the
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index d1d46fa..62e8a03 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -441,9 +441,9 @@ int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs,
} QEMU_PACKED snapshot_table_pointer;
/* qcow2_do_open() discards this information in check mode */
- ret = bdrv_pread(bs->file, offsetof(QCowHeader, nb_snapshots),
- sizeof(snapshot_table_pointer), &snapshot_table_pointer,
- 0);
+ ret = bdrv_co_pread(bs->file, offsetof(QCowHeader, nb_snapshots),
+ sizeof(snapshot_table_pointer), &snapshot_table_pointer,
+ 0);
if (ret < 0) {
result->check_errors++;
fprintf(stderr, "ERROR failed to read the snapshot table pointer from "
diff --git a/block/qcow2.c b/block/qcow2.c
index b57f7cc..4d6666d 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1306,7 +1306,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
uint64_t l1_vm_state_index;
bool update_header = false;
- ret = bdrv_pread(bs->file, 0, sizeof(header), &header, 0);
+ ret = bdrv_co_pread(bs->file, 0, sizeof(header), &header, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read qcow2 header");
goto fail;
@@ -1382,9 +1382,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
if (header.header_length > sizeof(header)) {
s->unknown_header_fields_size = header.header_length - sizeof(header);
s->unknown_header_fields = g_malloc(s->unknown_header_fields_size);
- ret = bdrv_pread(bs->file, sizeof(header),
- s->unknown_header_fields_size,
- s->unknown_header_fields, 0);
+ ret = bdrv_co_pread(bs->file, sizeof(header),
+ s->unknown_header_fields_size,
+ s->unknown_header_fields, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read unknown qcow2 header "
"fields");
@@ -1579,8 +1579,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
ret = -ENOMEM;
goto fail;
}
- ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_size * L1E_SIZE,
- s->l1_table, 0);
+ ret = bdrv_co_pread(bs->file, s->l1_table_offset, s->l1_size * L1E_SIZE,
+ s->l1_table, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read L1 table");
goto fail;
@@ -1699,8 +1699,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
}
s->image_backing_file = g_malloc(len + 1);
- ret = bdrv_pread(bs->file, header.backing_file_offset, len,
- s->image_backing_file, 0);
+ ret = bdrv_co_pread(bs->file, header.backing_file_offset, len,
+ s->image_backing_file, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read backing file name");
goto fail;
@@ -1905,11 +1905,11 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
.errp = errp,
.ret = -EINPROGRESS
};
+ int ret;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
/* Initialise locks */
@@ -3679,7 +3679,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
cpu_to_be64(QCOW2_INCOMPAT_EXTL2);
}
- ret = blk_pwrite(blk, 0, cluster_size, header, 0);
+ ret = blk_co_pwrite(blk, 0, cluster_size, header, 0);
g_free(header);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write qcow2 header");
@@ -3689,7 +3689,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
/* Write a refcount table with one refcount block */
refcount_table = g_malloc0(2 * cluster_size);
refcount_table[0] = cpu_to_be64(2 * cluster_size);
- ret = blk_pwrite(blk, cluster_size, 2 * cluster_size, refcount_table, 0);
+ ret = blk_co_pwrite(blk, cluster_size, 2 * cluster_size, refcount_table, 0);
g_free(refcount_table);
if (ret < 0) {
@@ -3744,8 +3744,8 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
}
/* Okay, now that we have a valid image, let's give it the right size */
- ret = blk_truncate(blk, qcow2_opts->size, false, qcow2_opts->preallocation,
- 0, errp);
+ ret = blk_co_truncate(blk, qcow2_opts->size, false,
+ qcow2_opts->preallocation, 0, errp);
if (ret < 0) {
error_prepend(errp, "Could not resize image: ");
goto out;
@@ -5287,8 +5287,8 @@ static int64_t qcow2_check_vmstate_request(BlockDriverState *bs,
return pos;
}
-static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
- int64_t pos)
+static coroutine_fn int qcow2_save_vmstate(BlockDriverState *bs,
+ QEMUIOVector *qiov, int64_t pos)
{
int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos);
if (offset < 0) {
@@ -5299,8 +5299,8 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
return bs->drv->bdrv_co_pwritev_part(bs, offset, qiov->size, qiov, 0, 0);
}
-static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
- int64_t pos)
+static coroutine_fn int qcow2_load_vmstate(BlockDriverState *bs,
+ QEMUIOVector *qiov, int64_t pos)
{
int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos);
if (offset < 0) {
diff --git a/block/qcow2.h b/block/qcow2.h
index 3e7c5e8..2285f18 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -846,7 +846,7 @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset,
Error **errp);
/* qcow2-refcount.c functions */
-int qcow2_refcount_init(BlockDriverState *bs);
+int coroutine_fn qcow2_refcount_init(BlockDriverState *bs);
void qcow2_refcount_close(BlockDriverState *bs);
int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
@@ -893,14 +893,14 @@ int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res,
int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order,
BlockDriverAmendStatusCB *status_cb,
void *cb_opaque, Error **errp);
-int qcow2_shrink_reftable(BlockDriverState *bs);
+int coroutine_fn qcow2_shrink_reftable(BlockDriverState *bs);
int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size);
int coroutine_fn qcow2_detect_metadata_preallocation(BlockDriverState *bs);
/* qcow2-cluster.c functions */
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
bool exact_size);
-int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size);
+int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size);
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
uint8_t *buf, int nb_sectors, bool enc, Error **errp);
@@ -911,10 +911,10 @@ int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset,
int coroutine_fn qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset,
unsigned int *bytes,
uint64_t *host_offset, QCowL2Meta **m);
-int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
- uint64_t offset,
- int compressed_size,
- uint64_t *host_offset);
+int coroutine_fn qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
+ uint64_t offset,
+ int compressed_size,
+ uint64_t *host_offset);
void qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry,
uint64_t *coffset, int *csize);
@@ -982,8 +982,8 @@ void qcow2_cache_discard(Qcow2Cache *c, void *table);
int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
void **refcount_table,
int64_t *refcount_table_size);
-bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated,
- Error **errp);
+bool coroutine_fn qcow2_load_dirty_bitmaps(BlockDriverState *bs,
+ bool *header_updated, Error **errp);
bool qcow2_get_bitmap_info_list(BlockDriverState *bs,
Qcow2BitmapInfoList **info_list, Error **errp);
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
@@ -991,13 +991,13 @@ int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp);
bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
bool release_stored, Error **errp);
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
-bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
- const char *name,
- uint32_t granularity,
- Error **errp);
-int qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs,
- const char *name,
- Error **errp);
+bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
+ const char *name,
+ uint32_t granularity,
+ Error **errp);
+int coroutine_fn qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs,
+ const char *name,
+ Error **errp);
bool qcow2_supports_persistent_dirty_bitmap(BlockDriverState *bs);
uint64_t qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *bs,
uint32_t cluster_size);
diff --git a/block/qed-table.c b/block/qed-table.c
index 1cc844b..aa203f2 100644
--- a/block/qed-table.c
+++ b/block/qed-table.c
@@ -100,7 +100,7 @@ static int coroutine_fn qed_write_table(BDRVQEDState *s, uint64_t offset,
}
if (flush) {
- ret = bdrv_flush(s->bs);
+ ret = bdrv_co_flush(s->bs);
if (ret < 0) {
goto out;
}
diff --git a/block/qed.c b/block/qed.c
index 99a9ec9..2f36ad3 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -387,7 +387,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
int64_t file_size;
int ret;
- ret = bdrv_pread(bs->file, 0, sizeof(le_header), &le_header, 0);
+ ret = bdrv_co_pread(bs->file, 0, sizeof(le_header), &le_header, 0);
if (ret < 0) {
error_setg(errp, "Failed to read QED header");
return ret;
@@ -492,7 +492,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
}
/* From here on only known autoclear feature bits are valid */
- bdrv_flush(bs->file->bs);
+ bdrv_co_flush(bs->file->bs);
}
s->l1_table = qed_alloc_table(s);
@@ -561,11 +561,11 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
.errp = errp,
.ret = -EINPROGRESS
};
+ int ret;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
bdrv_qed_init_state(bs);
@@ -693,7 +693,7 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
* The QED format associates file length with allocation status,
* so a new file (which is empty) must have a length of 0.
*/
- ret = blk_truncate(blk, 0, true, PREALLOC_MODE_OFF, 0, errp);
+ ret = blk_co_truncate(blk, 0, true, PREALLOC_MODE_OFF, 0, errp);
if (ret < 0) {
goto out;
}
@@ -712,18 +712,18 @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
}
qed_header_cpu_to_le(&header, &le_header);
- ret = blk_pwrite(blk, 0, sizeof(le_header), &le_header, 0);
+ ret = blk_co_pwrite(blk, 0, sizeof(le_header), &le_header, 0);
if (ret < 0) {
goto out;
}
- ret = blk_pwrite(blk, sizeof(le_header), header.backing_filename_size,
+ ret = blk_co_pwrite(blk, sizeof(le_header), header.backing_filename_size,
qed_opts->backing_file, 0);
if (ret < 0) {
goto out;
}
l1_table = g_malloc0(l1_size);
- ret = blk_pwrite(blk, header.l1_table_offset, l1_size, l1_table, 0);
+ ret = blk_co_pwrite(blk, header.l1_table_offset, l1_size, l1_table, 0);
if (ret < 0) {
goto out;
}
diff --git a/block/raw-format.c b/block/raw-format.c
index c8dc9bc..a68014e 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -460,8 +460,8 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
file_role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY;
}
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- file_role, false, errp);
+ bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
+ file_role, false, errp);
if (!bs->file) {
return -EINVAL;
}
diff --git a/block/replication.c b/block/replication.c
index 13f1d39..f1eed25 100644
--- a/block/replication.c
+++ b/block/replication.c
@@ -88,11 +88,9 @@ static int replication_open(BlockDriverState *bs, QDict *options,
const char *mode;
const char *top_id;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
ret = -EINVAL;
diff --git a/block/snapshot-access.c b/block/snapshot-access.c
index 77b87c1..0a30ec6 100644
--- a/block/snapshot-access.c
+++ b/block/snapshot-access.c
@@ -82,9 +82,9 @@ static void snapshot_access_refresh_filename(BlockDriverState *bs)
static int snapshot_access_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY,
- false, errp);
+ bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
+ BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY,
+ false, errp);
if (!bs->file) {
return -EINVAL;
}
diff --git a/block/snapshot.c b/block/snapshot.c
index d6f53c3..e22ac3e 100644
--- a/block/snapshot.c
+++ b/block/snapshot.c
@@ -151,41 +151,29 @@ bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs,
}
/**
- * Return a pointer to the child BDS pointer to which we can fall
+ * Return a pointer to child of given BDS to which we can fall
* back if the given BDS does not support snapshots.
* Return NULL if there is no BDS to (safely) fall back to.
- *
- * We need to return an indirect pointer because bdrv_snapshot_goto()
- * has to modify the BdrvChild pointer.
*/
-static BdrvChild **bdrv_snapshot_fallback_ptr(BlockDriverState *bs)
+static BdrvChild *bdrv_snapshot_fallback_child(BlockDriverState *bs)
{
- BdrvChild **fallback;
+ BdrvChild *fallback = bdrv_primary_child(bs);
BdrvChild *child;
- /*
- * The only BdrvChild pointers that are safe to modify (and which
- * we can thus return a reference to) are bs->file and
- * bs->backing.
- */
- fallback = &bs->file;
- if (!*fallback && bs->drv && bs->drv->is_filter) {
- fallback = &bs->backing;
- }
-
- if (!*fallback) {
+ /* We allow fallback only to primary child */
+ if (!fallback) {
return NULL;
}
/*
* Check that there are no other children that would need to be
* snapshotted. If there are, it is not safe to fall back to
- * *fallback.
+ * fallback.
*/
QLIST_FOREACH(child, &bs->children, next) {
if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA |
BDRV_CHILD_FILTERED) &&
- child != *fallback)
+ child != fallback)
{
return NULL;
}
@@ -196,8 +184,7 @@ static BdrvChild **bdrv_snapshot_fallback_ptr(BlockDriverState *bs)
static BlockDriverState *bdrv_snapshot_fallback(BlockDriverState *bs)
{
- BdrvChild **child_ptr = bdrv_snapshot_fallback_ptr(bs);
- return child_ptr ? (*child_ptr)->bs : NULL;
+ return child_bs(bdrv_snapshot_fallback_child(bs));
}
int bdrv_can_snapshot(BlockDriverState *bs)
@@ -244,7 +231,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
Error **errp)
{
BlockDriver *drv = bs->drv;
- BdrvChild **fallback_ptr;
+ BdrvChild *fallback;
int ret, open_ret;
GLOBAL_STATE_CODE();
@@ -267,13 +254,13 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
return ret;
}
- fallback_ptr = bdrv_snapshot_fallback_ptr(bs);
- if (fallback_ptr) {
+ fallback = bdrv_snapshot_fallback_child(bs);
+ if (fallback) {
QDict *options;
QDict *file_options;
Error *local_err = NULL;
- BlockDriverState *fallback_bs = (*fallback_ptr)->bs;
- char *subqdict_prefix = g_strdup_printf("%s.", (*fallback_ptr)->name);
+ BlockDriverState *fallback_bs = fallback->bs;
+ char *subqdict_prefix = g_strdup_printf("%s.", fallback->name);
options = qdict_clone_shallow(bs->options);
@@ -284,8 +271,8 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
qobject_unref(file_options);
g_free(subqdict_prefix);
- /* Force .bdrv_open() below to re-attach fallback_bs on *fallback_ptr */
- qdict_put_str(options, (*fallback_ptr)->name,
+ /* Force .bdrv_open() below to re-attach fallback_bs on fallback */
+ qdict_put_str(options, fallback->name,
bdrv_get_node_name(fallback_bs));
/* Now close bs, apply the snapshot on fallback_bs, and re-open bs */
@@ -294,8 +281,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
}
/* .bdrv_open() will re-attach it */
- bdrv_unref_child(bs, *fallback_ptr);
- *fallback_ptr = NULL;
+ bdrv_unref_child(bs, fallback);
ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp);
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
@@ -309,15 +295,12 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
}
/*
- * fallback_ptr is &bs->file or &bs->backing. *fallback_ptr
- * was closed above and set to NULL, but the .bdrv_open() call
- * has opened it again, because we set the respective option
- * (with the qdict_put_str() call above).
- * Assert that .bdrv_open() has attached some child on
- * *fallback_ptr, and that it has attached the one we wanted
- * it to (i.e., fallback_bs).
+ * fallback was a primary child. It was closed above and set to NULL,
+ * but the .bdrv_open() call has opened it again, because we set the
+ * respective option (with the qdict_put_str() call above).
+ * Assert that .bdrv_open() has attached the right BDS as primary child.
*/
- assert(*fallback_ptr && fallback_bs == (*fallback_ptr)->bs);
+ assert(bdrv_primary_bs(bs) == fallback_bs);
bdrv_unref(fallback_bs);
return ret;
}
diff --git a/block/ssh.c b/block/ssh.c
index a3cddc3..04726d4 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -1129,9 +1129,9 @@ static coroutine_fn int ssh_co_readv(BlockDriverState *bs,
return ret;
}
-static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
- int64_t offset, size_t size,
- QEMUIOVector *qiov)
+static coroutine_fn int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
+ int64_t offset, size_t size,
+ QEMUIOVector *qiov)
{
ssize_t r;
size_t written;
diff --git a/block/throttle.c b/block/throttle.c
index ddd4505..131eba3 100644
--- a/block/throttle.c
+++ b/block/throttle.c
@@ -78,11 +78,9 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
char *group;
int ret;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
- false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
bs->supported_write_flags = bs->file->bs->supported_write_flags |
BDRV_REQ_WRITE_UNCHANGED;
diff --git a/block/vdi.c b/block/vdi.c
index e942325..c0c111c 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -377,10 +377,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
int ret;
QemuUUID uuid_link, uuid_parent;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
logout("\n");
@@ -664,7 +663,8 @@ vdi_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
* so this full-cluster write does not overlap a partial write
* of the same cluster, issued from the "else" branch.
*/
- ret = bdrv_pwrite(bs->file, data_offset, s->block_size, block, 0);
+ ret = bdrv_co_pwrite(bs->file, data_offset, s->block_size, block,
+ 0);
qemu_co_rwlock_unlock(&s->bmap_lock);
} else {
nonallocating_write:
@@ -709,7 +709,7 @@ nonallocating_write:
assert(VDI_IS_ALLOCATED(bmap_first));
*header = s->header;
vdi_header_to_le(header);
- ret = bdrv_pwrite(bs->file, 0, sizeof(*header), header, 0);
+ ret = bdrv_co_pwrite(bs->file, 0, sizeof(*header), header, 0);
g_free(header);
if (ret < 0) {
@@ -726,8 +726,8 @@ nonallocating_write:
base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE;
logout("will write %u block map sectors starting from entry %u\n",
n_sectors, bmap_first);
- ret = bdrv_pwrite(bs->file, offset * SECTOR_SIZE,
- n_sectors * SECTOR_SIZE, base, 0);
+ ret = bdrv_co_pwrite(bs->file, offset * SECTOR_SIZE,
+ n_sectors * SECTOR_SIZE, base, 0);
}
return ret;
@@ -845,7 +845,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
vdi_header_print(&header);
}
vdi_header_to_le(&header);
- ret = blk_pwrite(blk, offset, sizeof(header), &header, 0);
+ ret = blk_co_pwrite(blk, offset, sizeof(header), &header, 0);
if (ret < 0) {
error_setg(errp, "Error writing header");
goto exit;
@@ -866,7 +866,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
bmap[i] = VDI_UNALLOCATED;
}
}
- ret = blk_pwrite(blk, offset, bmap_size, bmap, 0);
+ ret = blk_co_pwrite(blk, offset, bmap_size, bmap, 0);
if (ret < 0) {
error_setg(errp, "Error writing bmap");
goto exit;
@@ -875,8 +875,8 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
}
if (image_type == VDI_TYPE_STATIC) {
- ret = blk_truncate(blk, offset + blocks * block_size, false,
- PREALLOC_MODE_OFF, 0, errp);
+ ret = blk_co_truncate(blk, offset + blocks * block_size, false,
+ PREALLOC_MODE_OFF, 0, errp);
if (ret < 0) {
error_prepend(errp, "Failed to statically allocate file");
goto exit;
diff --git a/block/vhdx.c b/block/vhdx.c
index e2344ee..bad9ca69 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -1001,10 +1001,9 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
uint64_t signature;
Error *local_err = NULL;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
s->bat = NULL;
@@ -2011,15 +2010,15 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
creator = g_utf8_to_utf16("QEMU v" QEMU_VERSION, -1, NULL,
&creator_items, NULL);
signature = cpu_to_le64(VHDX_FILE_SIGNATURE);
- ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET, sizeof(signature), &signature,
- 0);
+ ret = blk_co_pwrite(blk, VHDX_FILE_ID_OFFSET, sizeof(signature), &signature,
+ 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to write file signature");
goto delete_and_exit;
}
if (creator) {
- ret = blk_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature),
- creator_items * sizeof(gunichar2), creator, 0);
+ ret = blk_co_pwrite(blk, VHDX_FILE_ID_OFFSET + sizeof(signature),
+ creator_items * sizeof(gunichar2), creator, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Failed to write creator field");
goto delete_and_exit;
diff --git a/block/vmdk.c b/block/vmdk.c
index f7d8856..2637635 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1308,10 +1308,9 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
BDRVVmdkState *s = bs->opaque;
uint32_t magic;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
buf = vmdk_read_desc(bs->file, 0, errp);
@@ -1404,13 +1403,13 @@ static void vmdk_refresh_limits(BlockDriverState *bs, Error **errp)
* [@skip_start_sector, @skip_end_sector) is not copied or written, and leave
* it for call to write user data in the request.
*/
-static int get_whole_cluster(BlockDriverState *bs,
- VmdkExtent *extent,
- uint64_t cluster_offset,
- uint64_t offset,
- uint64_t skip_start_bytes,
- uint64_t skip_end_bytes,
- bool zeroed)
+static int coroutine_fn get_whole_cluster(BlockDriverState *bs,
+ VmdkExtent *extent,
+ uint64_t cluster_offset,
+ uint64_t offset,
+ uint64_t skip_start_bytes,
+ uint64_t skip_end_bytes,
+ bool zeroed)
{
int ret = VMDK_OK;
int64_t cluster_bytes;
@@ -1441,16 +1440,16 @@ static int get_whole_cluster(BlockDriverState *bs,
if (copy_from_backing) {
/* qcow2 emits this on bs->file instead of bs->backing */
BLKDBG_EVENT(extent->file, BLKDBG_COW_READ);
- ret = bdrv_pread(bs->backing, offset, skip_start_bytes,
- whole_grain, 0);
+ ret = bdrv_co_pread(bs->backing, offset, skip_start_bytes,
+ whole_grain, 0);
if (ret < 0) {
ret = VMDK_ERROR;
goto exit;
}
}
BLKDBG_EVENT(extent->file, BLKDBG_COW_WRITE);
- ret = bdrv_pwrite(extent->file, cluster_offset, skip_start_bytes,
- whole_grain, 0);
+ ret = bdrv_co_pwrite(extent->file, cluster_offset, skip_start_bytes,
+ whole_grain, 0);
if (ret < 0) {
ret = VMDK_ERROR;
goto exit;
@@ -1461,18 +1460,18 @@ static int get_whole_cluster(BlockDriverState *bs,
if (copy_from_backing) {
/* qcow2 emits this on bs->file instead of bs->backing */
BLKDBG_EVENT(extent->file, BLKDBG_COW_READ);
- ret = bdrv_pread(bs->backing, offset + skip_end_bytes,
- cluster_bytes - skip_end_bytes,
- whole_grain + skip_end_bytes, 0);
+ ret = bdrv_co_pread(bs->backing, offset + skip_end_bytes,
+ cluster_bytes - skip_end_bytes,
+ whole_grain + skip_end_bytes, 0);
if (ret < 0) {
ret = VMDK_ERROR;
goto exit;
}
}
BLKDBG_EVENT(extent->file, BLKDBG_COW_WRITE);
- ret = bdrv_pwrite(extent->file, cluster_offset + skip_end_bytes,
- cluster_bytes - skip_end_bytes,
- whole_grain + skip_end_bytes, 0);
+ ret = bdrv_co_pwrite(extent->file, cluster_offset + skip_end_bytes,
+ cluster_bytes - skip_end_bytes,
+ whole_grain + skip_end_bytes, 0);
if (ret < 0) {
ret = VMDK_ERROR;
goto exit;
@@ -1485,29 +1484,29 @@ exit:
return ret;
}
-static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data,
- uint32_t offset)
+static int coroutine_fn vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data,
+ uint32_t offset)
{
offset = cpu_to_le32(offset);
/* update L2 table */
BLKDBG_EVENT(extent->file, BLKDBG_L2_UPDATE);
- if (bdrv_pwrite(extent->file,
- ((int64_t)m_data->l2_offset * 512)
- + (m_data->l2_index * sizeof(offset)),
- sizeof(offset), &offset, 0) < 0) {
+ if (bdrv_co_pwrite(extent->file,
+ ((int64_t)m_data->l2_offset * 512)
+ + (m_data->l2_index * sizeof(offset)),
+ sizeof(offset), &offset, 0) < 0) {
return VMDK_ERROR;
}
/* update backup L2 table */
if (extent->l1_backup_table_offset != 0) {
m_data->l2_offset = extent->l1_backup_table[m_data->l1_index];
- if (bdrv_pwrite(extent->file,
- ((int64_t)m_data->l2_offset * 512)
- + (m_data->l2_index * sizeof(offset)),
- sizeof(offset), &offset, 0) < 0) {
+ if (bdrv_co_pwrite(extent->file,
+ ((int64_t)m_data->l2_offset * 512)
+ + (m_data->l2_index * sizeof(offset)),
+ sizeof(offset), &offset, 0) < 0) {
return VMDK_ERROR;
}
}
- if (bdrv_flush(extent->file->bs) < 0) {
+ if (bdrv_co_flush(extent->file->bs) < 0) {
return VMDK_ERROR;
}
if (m_data->l2_cache_entry) {
@@ -1537,14 +1536,14 @@ static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data,
* VMDK_UNALLOC if cluster is not mapped and @allocate is false.
* VMDK_ERROR if failed.
*/
-static int get_cluster_offset(BlockDriverState *bs,
- VmdkExtent *extent,
- VmdkMetaData *m_data,
- uint64_t offset,
- bool allocate,
- uint64_t *cluster_offset,
- uint64_t skip_start_bytes,
- uint64_t skip_end_bytes)
+static int coroutine_fn get_cluster_offset(BlockDriverState *bs,
+ VmdkExtent *extent,
+ VmdkMetaData *m_data,
+ uint64_t offset,
+ bool allocate,
+ uint64_t *cluster_offset,
+ uint64_t skip_start_bytes,
+ uint64_t skip_end_bytes)
{
unsigned int l1_index, l2_offset, l2_index;
int min_index, i, j;
@@ -1624,11 +1623,10 @@ static int get_cluster_offset(BlockDriverState *bs,
}
l2_table = (char *)extent->l2_cache + (min_index * l2_size_bytes);
BLKDBG_EVENT(extent->file, BLKDBG_L2_LOAD);
- if (bdrv_pread(extent->file,
+ if (bdrv_co_pread(extent->file,
(int64_t)l2_offset * 512,
l2_size_bytes,
- l2_table,
- 0
+ l2_table, 0
) < 0) {
return VMDK_ERROR;
}
@@ -1899,7 +1897,8 @@ vmdk_read_extent(VmdkExtent *extent, int64_t cluster_offset,
cluster_buf = g_malloc(buf_bytes);
uncomp_buf = g_malloc(cluster_bytes);
BLKDBG_EVENT(extent->file, BLKDBG_READ_COMPRESSED);
- ret = bdrv_pread(extent->file, cluster_offset, buf_bytes, cluster_buf, 0);
+ ret = bdrv_co_pread(extent->file, cluster_offset, buf_bytes, cluster_buf,
+ 0);
if (ret < 0) {
goto out;
}
@@ -2144,8 +2143,8 @@ vmdk_co_pwritev_compressed(BlockDriverState *bs, int64_t offset, int64_t bytes,
return length;
}
length = QEMU_ALIGN_UP(length, BDRV_SECTOR_SIZE);
- ret = bdrv_truncate(s->extents[i].file, length, false,
- PREALLOC_MODE_OFF, 0, NULL);
+ ret = bdrv_co_truncate(s->extents[i].file, length, false,
+ PREALLOC_MODE_OFF, 0, NULL);
if (ret < 0) {
return ret;
}
@@ -2586,7 +2585,7 @@ static int coroutine_fn vmdk_co_do_create(int64_t size,
desc_offset = 0x200;
}
- ret = blk_pwrite(blk, desc_offset, desc_len, desc, 0);
+ ret = blk_co_pwrite(blk, desc_offset, desc_len, desc, 0);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write description");
goto exit;
@@ -2594,7 +2593,7 @@ static int coroutine_fn vmdk_co_do_create(int64_t size,
/* bdrv_pwrite write padding zeros to align to sector, we don't need that
* for description file */
if (desc_offset == 0) {
- ret = blk_truncate(blk, desc_len, false, PREALLOC_MODE_OFF, 0, errp);
+ ret = blk_co_truncate(blk, desc_len, false, PREALLOC_MODE_OFF, 0, errp);
if (ret < 0) {
goto exit;
}
diff --git a/block/vpc.c b/block/vpc.c
index 4f49ef2..95841f2 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -233,10 +233,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
int ret;
int64_t bs_size;
- bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
- BDRV_CHILD_IMAGE, false, errp);
- if (!bs->file) {
- return -EINVAL;
+ ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
+ if (ret < 0) {
+ return ret;
}
opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort);
diff --git a/block/vvfat.c b/block/vvfat.c
index d6dd919..c5b1442 100644
--- a/block/vvfat.c
+++ b/block/vvfat.c
@@ -499,7 +499,7 @@ static bool valid_filename(const unsigned char *name)
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c > 127 ||
- strchr("$%'-_@~`!(){}^#&.+,;=[]", c) != NULL))
+ strchr(" $%'-_@~`!(){}^#&.+,;=[]", c) != NULL))
{
return false;
}
@@ -2993,11 +2993,35 @@ DLOG(checkpoint());
vvfat_close_current_file(s);
+ if (sector_num == s->offset_to_bootsector && nb_sectors == 1) {
+ /*
+ * Write on bootsector. Allow only changing the reserved1 field,
+ * used to mark volume dirtiness
+ */
+ unsigned char *bootsector = s->first_sectors
+ + s->offset_to_bootsector * 0x200;
+ /*
+ * LATER TODO: if FAT32, this is wrong (see init_directories(),
+ * which always creates a FAT16 bootsector)
+ */
+ const int reserved1_offset = offsetof(bootsector_t, u.fat16.reserved1);
+
+ for (i = 0; i < 0x200; i++) {
+ if (i != reserved1_offset && bootsector[i] != buf[i]) {
+ fprintf(stderr, "Tried to write to protected bootsector\n");
+ return -1;
+ }
+ }
+
+ /* Update bootsector with the only updatable byte, and return success */
+ bootsector[reserved1_offset] = buf[reserved1_offset];
+ return 0;
+ }
+
/*
* Some sanity checks:
* - do not allow writing to the boot sector
*/
-
if (sector_num < s->offset_to_fat)
return -1;
@@ -3146,10 +3170,9 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
array_init(&(s->commits), sizeof(commit_t));
- s->qcow_filename = g_malloc(PATH_MAX);
- ret = get_tmp_filename(s->qcow_filename, PATH_MAX);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "can't create temporary file");
+ s->qcow_filename = create_tmp_file(errp);
+ if (!s->qcow_filename) {
+ ret = -ENOENT;
goto err;
}
diff --git a/blockdev.c b/blockdev.c
index a32bafc..3f1dec6 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -1630,8 +1630,8 @@ static void external_snapshot_abort(BlkActionState *common)
aio_context_release(aio_context);
aio_context_acquire(tmp_context);
- ret = bdrv_try_set_aio_context(state->old_bs,
- aio_context, NULL);
+ ret = bdrv_try_change_aio_context(state->old_bs,
+ aio_context, NULL, NULL);
assert(ret == 0);
aio_context_release(tmp_context);
@@ -1792,12 +1792,12 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
goto out;
}
- /* Honor bdrv_try_set_aio_context() context acquisition requirements. */
+ /* Honor bdrv_try_change_aio_context() context acquisition requirements. */
old_context = bdrv_get_aio_context(target_bs);
aio_context_release(aio_context);
aio_context_acquire(old_context);
- ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
+ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp);
if (ret < 0) {
bdrv_unref(target_bs);
aio_context_release(old_context);
@@ -1892,12 +1892,12 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
return;
}
- /* Honor bdrv_try_set_aio_context() context acquisition requirements. */
+ /* Honor bdrv_try_change_aio_context() context acquisition requirements. */
aio_context = bdrv_get_aio_context(bs);
old_context = bdrv_get_aio_context(target_bs);
aio_context_acquire(old_context);
- ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
+ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp);
if (ret < 0) {
aio_context_release(old_context);
return;
@@ -2448,7 +2448,7 @@ void coroutine_fn qmp_block_resize(bool has_device, const char *device,
bdrv_co_unlock(bs);
old_ctx = bdrv_co_enter(bs);
- blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
+ blk_co_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
bdrv_co_leave(bs, old_ctx);
bdrv_co_lock(bs);
@@ -3194,12 +3194,12 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
!bdrv_has_zero_init(target_bs)));
- /* Honor bdrv_try_set_aio_context() context acquisition requirements. */
+ /* Honor bdrv_try_change_aio_context() context acquisition requirements. */
old_context = bdrv_get_aio_context(target_bs);
aio_context_release(aio_context);
aio_context_acquire(old_context);
- ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
+ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp);
if (ret < 0) {
bdrv_unref(target_bs);
aio_context_release(old_context);
@@ -3266,12 +3266,12 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
zero_target = (sync == MIRROR_SYNC_MODE_FULL);
- /* Honor bdrv_try_set_aio_context() context acquisition requirements. */
+ /* Honor bdrv_try_change_aio_context() context acquisition requirements. */
old_context = bdrv_get_aio_context(target_bs);
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(old_context);
- ret = bdrv_try_set_aio_context(target_bs, aio_context, errp);
+ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp);
aio_context_release(old_context);
aio_context_acquire(aio_context);
@@ -3767,7 +3767,7 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
old_context = bdrv_get_aio_context(bs);
aio_context_acquire(old_context);
- bdrv_try_set_aio_context(bs, new_context, errp);
+ bdrv_try_change_aio_context(bs, new_context, NULL, errp);
aio_context_release(old_context);
}
diff --git a/blockjob.c b/blockjob.c
index bdf20a0..2d86014 100644
--- a/blockjob.c
+++ b/blockjob.c
@@ -126,37 +126,48 @@ static void child_job_drained_end(BdrvChild *c, int *drained_end_counter)
job_resume(&job->job);
}
-static bool child_job_can_set_aio_ctx(BdrvChild *c, AioContext *ctx,
- GSList **ignore, Error **errp)
+typedef struct BdrvStateChildJobContext {
+ AioContext *new_ctx;
+ BlockJob *job;
+} BdrvStateChildJobContext;
+
+static void child_job_set_aio_ctx_commit(void *opaque)
{
- BlockJob *job = c->opaque;
- GSList *l;
+ BdrvStateChildJobContext *s = opaque;
+ BlockJob *job = s->job;
- for (l = job->nodes; l; l = l->next) {
- BdrvChild *sibling = l->data;
- if (!bdrv_child_can_set_aio_context(sibling, ctx, ignore, errp)) {
- return false;
- }
- }
- return true;
+ job_set_aio_context(&job->job, s->new_ctx);
}
-static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx,
- GSList **ignore)
+static TransactionActionDrv change_child_job_context = {
+ .commit = child_job_set_aio_ctx_commit,
+ .clean = g_free,
+};
+
+static bool child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp)
{
BlockJob *job = c->opaque;
+ BdrvStateChildJobContext *s;
GSList *l;
for (l = job->nodes; l; l = l->next) {
BdrvChild *sibling = l->data;
- if (g_slist_find(*ignore, sibling)) {
- continue;
+ if (!bdrv_child_change_aio_context(sibling, ctx, visited,
+ tran, errp)) {
+ return false;
}
- *ignore = g_slist_prepend(*ignore, sibling);
- bdrv_set_aio_context_ignore(sibling->bs, ctx, ignore);
}
- job_set_aio_context(&job->job, ctx);
+ s = g_new(BdrvStateChildJobContext, 1);
+ *s = (BdrvStateChildJobContext) {
+ .new_ctx = ctx,
+ .job = job,
+ };
+
+ tran_add(tran, &change_child_job_context, s);
+ return true;
}
static AioContext *child_job_get_parent_aio_context(BdrvChild *c)
@@ -172,8 +183,7 @@ static const BdrvChildClass child_job = {
.drained_begin = child_job_drained_begin,
.drained_poll = child_job_drained_poll,
.drained_end = child_job_drained_end,
- .can_set_aio_ctx = child_job_can_set_aio_ctx,
- .set_aio_ctx = child_job_set_aio_ctx,
+ .change_aio_ctx = child_job_change_aio_ctx,
.stay_at_node = true,
.get_parent_aio_context = child_job_get_parent_aio_context,
};
diff --git a/docs/devel/multiple-iothreads.txt b/docs/devel/multiple-iothreads.txt
index aeb997b..343120f 100644
--- a/docs/devel/multiple-iothreads.txt
+++ b/docs/devel/multiple-iothreads.txt
@@ -109,7 +109,7 @@ The AioContext originates from the QEMU block layer, even though nowadays
AioContext is a generic event loop that can be used by any QEMU subsystem.
The block layer has support for AioContext integrated. Each BlockDriverState
-is associated with an AioContext using bdrv_try_set_aio_context() and
+is associated with an AioContext using bdrv_try_change_aio_context() and
bdrv_get_aio_context(). This allows block layer code to process I/O inside the
right AioContext. Other subsystems may wish to follow a similar approach.
@@ -134,5 +134,5 @@ Long-running jobs (usually in the form of coroutines) are best scheduled in
the BlockDriverState's AioContext to avoid the need to acquire/release around
each bdrv_*() call. The functions bdrv_add/remove_aio_context_notifier,
or alternatively blk_add/remove_aio_context_notifier if you use BlockBackends,
-can be used to get a notification whenever bdrv_try_set_aio_context() moves a
+can be used to get a notification whenever bdrv_try_change_aio_context() moves a
BlockDriverState to a different AioContext.
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index 84902dd..13bf5cc 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -97,6 +97,10 @@ static int vhost_user_blk_handle_config_change(struct vhost_dev *dev)
VHostUserBlk *s = VHOST_USER_BLK(dev->vdev);
Error *local_err = NULL;
+ if (!dev->started) {
+ return 0;
+ }
+
ret = vhost_dev_get_config(dev, (uint8_t *)&blkcfg,
vdev->config_len, &local_err);
if (ret < 0) {
diff --git a/include/block/block-common.h b/include/block/block-common.h
index 061606e..297704c 100644
--- a/include/block/block-common.h
+++ b/include/block/block-common.h
@@ -322,6 +322,45 @@ enum {
*
* At least one of DATA, METADATA, FILTERED, or COW must be set for
* every child.
+ *
+ *
+ * = Connection with bs->children, bs->file and bs->backing fields =
+ *
+ * 1. Filters
+ *
+ * Filter drivers have drv->is_filter = true.
+ *
+ * Filter node has exactly one FILTERED|PRIMARY child, and may have other
+ * children which must not have these bits (one example is the
+ * copy-before-write filter, which also has its target DATA child).
+ *
+ * Filter nodes never have COW children.
+ *
+ * For most filters, the filtered child is linked in bs->file, bs->backing is
+ * NULL. For some filters (as an exception), it is the other way around; those
+ * drivers will have drv->filtered_child_is_backing set to true (see that
+ * field’s documentation for what drivers this concerns)
+ *
+ * 2. "raw" driver (block/raw-format.c)
+ *
+ * Formally it's not a filter (drv->is_filter = false)
+ *
+ * bs->backing is always NULL
+ *
+ * Only has one child, linked in bs->file. Its role is either FILTERED|PRIMARY
+ * (like filter) or DATA|PRIMARY depending on options.
+ *
+ * 3. Other drivers
+ *
+ * Don't have any FILTERED children.
+ *
+ * May have at most one COW child. In this case it's linked in bs->backing.
+ * Otherwise bs->backing is NULL. COW child is never PRIMARY.
+ *
+ * May have at most one PRIMARY child. In this case it's linked in bs->file.
+ * Otherwise bs->file is NULL.
+ *
+ * May also have some other children that don't have the PRIMARY or COW bit set.
*/
enum BdrvChildRoleBits {
/*
diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h
index eba4ed2..bb42ed9 100644
--- a/include/block/block-global-state.h
+++ b/include/block/block-global-state.h
@@ -76,6 +76,9 @@ BdrvChild *bdrv_open_child(const char *filename,
const BdrvChildClass *child_class,
BdrvChildRole child_role,
bool allow_none, Error **errp);
+int bdrv_open_file_child(const char *filename,
+ QDict *options, const char *bdref_key,
+ BlockDriverState *parent, Error **errp);
BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp);
int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
Error **errp);
@@ -217,17 +220,12 @@ void coroutine_fn bdrv_co_lock(BlockDriverState *bs);
*/
void coroutine_fn bdrv_co_unlock(BlockDriverState *bs);
-void bdrv_set_aio_context_ignore(BlockDriverState *bs,
- AioContext *new_context, GSList **ignore);
-int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx,
- Error **errp);
-int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx,
- BdrvChild *ignore_child, Error **errp);
-bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx,
- GSList **ignore, Error **errp);
-bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx,
- GSList **ignore, Error **errp);
AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c);
+bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp);
+int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx,
+ BdrvChild *ignore_child, Error **errp);
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz);
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo);
diff --git a/include/block/block-hmp-cmds.h b/include/block/block-hmp-cmds.h
index 50ce024..ba0593c 100644
--- a/include/block/block-hmp-cmds.h
+++ b/include/block/block-hmp-cmds.h
@@ -38,7 +38,7 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_remove(Monitor *mon, const QDict *qdict);
void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
-void hmp_block_resize(Monitor *mon, const QDict *qdict);
+void coroutine_fn hmp_block_resize(Monitor *mon, const QDict *qdict);
void hmp_block_stream(Monitor *mon, const QDict *qdict);
void hmp_block_passwd(Monitor *mon, const QDict *qdict);
void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict);
diff --git a/include/block/block-io.h b/include/block/block-io.h
index 492f95f..770ddeb 100644
--- a/include/block/block-io.h
+++ b/include/block/block-io.h
@@ -83,12 +83,13 @@ void bdrv_aio_cancel(BlockAIOCB *acb);
void bdrv_aio_cancel_async(BlockAIOCB *acb);
/* sg packet commands */
-int bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf);
+int coroutine_fn bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf);
/* Ensure contents are flushed to disk. */
int coroutine_fn bdrv_co_flush(BlockDriverState *bs);
-int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes);
+int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
+ int64_t bytes);
bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
int bdrv_block_status(BlockDriverState *bs, int64_t offset,
int64_t bytes, int64_t *pnum, int64_t *map,
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
index 9c569be..5a2cc07 100644
--- a/include/block/block_int-common.h
+++ b/include/block/block_int-common.h
@@ -120,6 +120,20 @@ struct BlockDriver {
*/
bool is_filter;
/*
+ * Only make sense for filter drivers, for others must be false.
+ * If true, filtered child is bs->backing. Otherwise it's bs->file.
+ * Two internal filters use bs->backing as filtered child and has this
+ * field set to true: mirror_top and commit_top. There also two such test
+ * filters in tests/unit/test-bdrv-graph-mod.c.
+ *
+ * Never create any more such filters!
+ *
+ * TODO: imagine how to deprecate this behavior and make all filters work
+ * similarly using bs->file as filtered child.
+ */
+ bool filtered_child_is_backing;
+
+ /*
* Set to true if the BlockDriver is a format driver. Format nodes
* generally do not expect their children to be other format nodes
* (except for backing files), and so format probing is disabled
@@ -734,13 +748,11 @@ struct BlockDriver {
void coroutine_fn (*bdrv_co_drain_end)(BlockDriverState *bs);
bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs);
- bool (*bdrv_co_can_store_new_dirty_bitmap)(BlockDriverState *bs,
- const char *name,
- uint32_t granularity,
- Error **errp);
- int (*bdrv_co_remove_persistent_dirty_bitmap)(BlockDriverState *bs,
- const char *name,
- Error **errp);
+ bool coroutine_fn (*bdrv_co_can_store_new_dirty_bitmap)(
+ BlockDriverState *bs, const char *name, uint32_t granularity,
+ Error **errp);
+ int coroutine_fn (*bdrv_co_remove_persistent_dirty_bitmap)(
+ BlockDriverState *bs, const char *name, Error **errp);
};
static inline bool block_driver_can_compress(BlockDriver *drv)
@@ -895,9 +907,9 @@ struct BdrvChildClass {
int (*update_filename)(BdrvChild *child, BlockDriverState *new_base,
const char *filename, Error **errp);
- bool (*can_set_aio_ctx)(BdrvChild *child, AioContext *ctx,
- GSList **ignore, Error **errp);
- void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore);
+ bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx,
+ GHashTable *visited, Transaction *tran,
+ Error **errp);
AioContext *(*get_parent_aio_context)(BdrvChild *child);
@@ -1045,9 +1057,6 @@ struct BlockDriverState {
QDict *full_open_options;
char exact_filename[PATH_MAX];
- BdrvChild *backing;
- BdrvChild *file;
-
/* I/O Limits */
BlockLimits bl;
@@ -1106,7 +1115,19 @@ struct BlockDriverState {
* parent node of this node.
*/
BlockDriverState *inherits_from;
+
+ /*
+ * @backing and @file are some of @children or NULL. All these three fields
+ * (@file, @backing and @children) are modified only in
+ * bdrv_child_cb_attach() and bdrv_child_cb_detach().
+ *
+ * See also comment in include/block/block.h, to learn how backing and file
+ * are connected with BdrvChildRole.
+ */
QLIST_HEAD(, BdrvChild) children;
+ BdrvChild *backing;
+ BdrvChild *file;
+
QLIST_HEAD(, BdrvChild) parents;
QDict *options;
@@ -1233,7 +1254,7 @@ static inline BlockDriverState *child_bs(BdrvChild *child)
}
int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp);
-int get_tmp_filename(char *filename, int size);
+char *create_tmp_file(Error **errp);
void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
QDict *options);
diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h
index a9cf064..dfbc0c9 100644
--- a/include/monitor/hmp.h
+++ b/include/monitor/hmp.h
@@ -15,6 +15,7 @@
#define HMP_H
#include "qemu/readline.h"
+#include "qemu/coroutine.h"
#include "qapi/qapi-types-common.h"
bool hmp_handle_error(Monitor *mon, Error *err);
@@ -81,7 +82,7 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict);
void hmp_getfd(Monitor *mon, const QDict *qdict);
void hmp_closefd(Monitor *mon, const QDict *qdict);
void hmp_sendkey(Monitor *mon, const QDict *qdict);
-void hmp_screendump(Monitor *mon, const QDict *qdict);
+void coroutine_fn hmp_screendump(Monitor *mon, const QDict *qdict);
void hmp_chardev_add(Monitor *mon, const QDict *qdict);
void hmp_chardev_change(Monitor *mon, const QDict *qdict);
void hmp_chardev_remove(Monitor *mon, const QDict *qdict);
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
index 608fe45..89650a2 100644
--- a/include/qemu/coroutine.h
+++ b/include/qemu/coroutine.h
@@ -287,7 +287,7 @@ void qemu_co_rwlock_init(CoRwlock *lock);
* of a parallel writer, control is transferred to the caller of the current
* coroutine.
*/
-void qemu_co_rwlock_rdlock(CoRwlock *lock);
+void coroutine_fn qemu_co_rwlock_rdlock(CoRwlock *lock);
/**
* Write Locks the CoRwlock from a reader. This is a bit more efficient than
@@ -296,7 +296,7 @@ void qemu_co_rwlock_rdlock(CoRwlock *lock);
* to the caller of the current coroutine; another writer might run while
* @qemu_co_rwlock_upgrade blocks.
*/
-void qemu_co_rwlock_upgrade(CoRwlock *lock);
+void coroutine_fn qemu_co_rwlock_upgrade(CoRwlock *lock);
/**
* Downgrades a write-side critical section to a reader. Downgrading with
@@ -304,20 +304,20 @@ void qemu_co_rwlock_upgrade(CoRwlock *lock);
* followed by @qemu_co_rwlock_rdlock. This makes it more efficient, but
* may also sometimes be necessary for correctness.
*/
-void qemu_co_rwlock_downgrade(CoRwlock *lock);
+void coroutine_fn qemu_co_rwlock_downgrade(CoRwlock *lock);
/**
* Write Locks the mutex. If the lock cannot be taken immediately because
* of a parallel reader, control is transferred to the caller of the current
* coroutine.
*/
-void qemu_co_rwlock_wrlock(CoRwlock *lock);
+void coroutine_fn qemu_co_rwlock_wrlock(CoRwlock *lock);
/**
* Unlocks the read/write lock and schedules the next coroutine that was
* waiting for this lock to be run.
*/
-void qemu_co_rwlock_unlock(CoRwlock *lock);
+void coroutine_fn qemu_co_rwlock_unlock(CoRwlock *lock);
typedef struct QemuCoSleep {
Coroutine *to_wake;
@@ -389,8 +389,9 @@ void qemu_coroutine_dec_pool_size(unsigned int additional_pool_size);
* The same interface as qemu_sendv_recvv(), with added yielding.
* XXX should mark these as coroutine_fn
*/
-ssize_t qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt,
- size_t offset, size_t bytes, bool do_send);
+ssize_t coroutine_fn qemu_co_sendv_recvv(int sockfd, struct iovec *iov,
+ unsigned iov_cnt, size_t offset,
+ size_t bytes, bool do_send);
#define qemu_co_recvv(sockfd, iov, iov_cnt, offset, bytes) \
qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, false)
#define qemu_co_sendv(sockfd, iov, iov_cnt, offset, bytes) \
@@ -399,7 +400,8 @@ ssize_t qemu_co_sendv_recvv(int sockfd, struct iovec *iov, unsigned iov_cnt,
/**
* The same as above, but with just a single buffer
*/
-ssize_t qemu_co_send_recv(int sockfd, void *buf, size_t bytes, bool do_send);
+ssize_t coroutine_fn qemu_co_send_recv(int sockfd, void *buf, size_t bytes,
+ bool do_send);
#define qemu_co_recv(sockfd, buf, bytes) \
qemu_co_send_recv(sockfd, buf, bytes, false)
#define qemu_co_send(sockfd, buf, bytes) \
diff --git a/job.c b/job.c
index 78feae0..72d57f0 100644
--- a/job.c
+++ b/job.c
@@ -588,7 +588,7 @@ static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns)
next_aio_context = job->aio_context;
/*
* Coroutine has resumed, but in the meanwhile the job AioContext
- * might have changed via bdrv_try_set_aio_context(), so we need to move
+ * might have changed via bdrv_try_change_aio_context(), so we need to move
* the coroutine too in the new aiocontext.
*/
while (qemu_get_current_aio_context() != next_aio_context) {
diff --git a/meson.build b/meson.build
index f06d465..7d39756 100644
--- a/meson.build
+++ b/meson.build
@@ -1853,7 +1853,6 @@ config_host_data.set('CONFIG_LIBNFS', libnfs.found())
config_host_data.set('CONFIG_LIBSSH', libssh.found())
config_host_data.set('CONFIG_LINUX_AIO', libaio.found())
config_host_data.set('CONFIG_LINUX_IO_URING', linux_io_uring.found())
-config_host_data.set('CONFIG_LIBURING_REGISTER_RING_FD', cc.has_function('io_uring_register_ring_fd', prefix: '#include <liburing.h>', dependencies:linux_io_uring))
config_host_data.set('CONFIG_LIBPMEM', libpmem.found())
config_host_data.set('CONFIG_NUMA', numa.found())
config_host_data.set('CONFIG_OPENGL', opengl.found())
diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051
index f1a5065..4c079b1 100755
--- a/tests/qemu-iotests/051
+++ b/tests/qemu-iotests/051
@@ -375,7 +375,8 @@ if [ "${VALGRIND_QEMU_VM}" == "y" ]; then
_casenotrun "Valgrind needs a valid TMPDIR for itself"
fi
VALGRIND_QEMU_VM= \
-TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on
+TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on |
+ sed -e "s#'[^']*/vl\.[A-Za-z0-9]\{6\}'#SNAPSHOT_PATH#g"
# Using snapshot=on together with read-only=on
echo "info block" |
diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out
index 441f83e..e5ddb03 100644
--- a/tests/qemu-iotests/051.out
+++ b/tests/qemu-iotests/051.out
@@ -459,7 +459,7 @@ wrote 4096/4096 bytes at offset 0
read 4096/4096 bytes at offset 0
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Testing: -drive driver=null-co,snapshot=on
-QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory
+QEMU_PROG: -drive driver=null-co,snapshot=on: Could not open temporary file SNAPSHOT_PATH: No such file or directory
Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0
QEMU X.Y.Z monitor - type 'help' for more information
diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out
index 063e4fc..bade1ff 100644
--- a/tests/qemu-iotests/051.pc.out
+++ b/tests/qemu-iotests/051.pc.out
@@ -539,7 +539,7 @@ wrote 4096/4096 bytes at offset 0
read 4096/4096 bytes at offset 0
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Testing: -drive driver=null-co,snapshot=on
-QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory
+QEMU_PROG: -drive driver=null-co,snapshot=on: Could not open temporary file SNAPSHOT_PATH: No such file or directory
Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0
QEMU X.Y.Z monitor - type 'help' for more information
diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c
index 4924ceb..09dc4a4 100644
--- a/tests/unit/test-bdrv-drain.c
+++ b/tests/unit/test-bdrv-drain.c
@@ -1538,16 +1538,16 @@ static void test_set_aio_context(void)
&error_abort);
bdrv_drained_begin(bs);
- bdrv_try_set_aio_context(bs, ctx_a, &error_abort);
+ bdrv_try_change_aio_context(bs, ctx_a, NULL, &error_abort);
aio_context_acquire(ctx_a);
bdrv_drained_end(bs);
bdrv_drained_begin(bs);
- bdrv_try_set_aio_context(bs, ctx_b, &error_abort);
+ bdrv_try_change_aio_context(bs, ctx_b, NULL, &error_abort);
aio_context_release(ctx_a);
aio_context_acquire(ctx_b);
- bdrv_try_set_aio_context(bs, qemu_get_aio_context(), &error_abort);
+ bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort);
aio_context_release(ctx_b);
bdrv_drained_end(bs);
@@ -1830,9 +1830,8 @@ static void test_drop_intermediate_poll(void)
for (i = 0; i < 3; i++) {
if (i) {
/* Takes the reference to chain[i - 1] */
- chain[i]->backing = bdrv_attach_child(chain[i], chain[i - 1],
- "chain", &chain_child_class,
- BDRV_CHILD_COW, &error_abort);
+ bdrv_attach_child(chain[i], chain[i - 1], "chain",
+ &chain_child_class, BDRV_CHILD_COW, &error_abort);
}
}
@@ -1970,6 +1969,7 @@ static void coroutine_fn bdrv_replace_test_co_drain_end(BlockDriverState *bs)
static BlockDriver bdrv_replace_test = {
.format_name = "replace_test",
.instance_size = sizeof(BDRVReplaceTestState),
+ .supports_backing = true,
.bdrv_close = bdrv_replace_test_close,
.bdrv_co_preadv = bdrv_replace_test_co_preadv,
@@ -2049,9 +2049,8 @@ static void do_test_replace_child_mid_drain(int old_drain_count,
new_child_bs->total_sectors = 1;
bdrv_ref(old_child_bs);
- parent_bs->backing = bdrv_attach_child(parent_bs, old_child_bs, "child",
- &child_of_bds, BDRV_CHILD_COW,
- &error_abort);
+ bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds,
+ BDRV_CHILD_COW, &error_abort);
for (i = 0; i < old_drain_count; i++) {
bdrv_drained_begin(old_child_bs);
diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c
index a6e3bb7..c522591 100644
--- a/tests/unit/test-bdrv-graph-mod.c
+++ b/tests/unit/test-bdrv-graph-mod.c
@@ -26,6 +26,8 @@
static BlockDriver bdrv_pass_through = {
.format_name = "pass-through",
+ .is_filter = true,
+ .filtered_child_is_backing = true,
.bdrv_child_perm = bdrv_default_perms,
};
@@ -57,6 +59,8 @@ static void exclusive_write_perms(BlockDriverState *bs, BdrvChild *c,
static BlockDriver bdrv_exclusive_writer = {
.format_name = "exclusive-writer",
+ .is_filter = true,
+ .filtered_child_is_backing = true,
.bdrv_child_perm = exclusive_write_perms,
};
@@ -134,7 +138,7 @@ static void test_update_perm_tree(void)
blk_insert_bs(root, bs, &error_abort);
bdrv_attach_child(filter, bs, "child", &child_of_bds,
- BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort);
+ BDRV_CHILD_DATA, &error_abort);
ret = bdrv_append(filter, bs, NULL);
g_assert_cmpint(ret, <, 0);
@@ -228,11 +232,14 @@ static void test_parallel_exclusive_write(void)
*/
bdrv_ref(base);
- bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_DATA,
+ bdrv_attach_child(top, fl1, "backing", &child_of_bds,
+ BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
- bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds,
+ BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
- bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds,
+ BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
bdrv_replace_node(fl1, fl2, &error_abort);
@@ -241,13 +248,26 @@ static void test_parallel_exclusive_write(void)
bdrv_unref(top);
}
-static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c,
- BdrvChildRole role,
- BlockReopenQueue *reopen_queue,
- uint64_t perm, uint64_t shared,
- uint64_t *nperm, uint64_t *nshared)
+/*
+ * write-to-selected node may have several DATA children, one of them may be
+ * "selected". Exclusive write permission is taken on selected child.
+ *
+ * We don't realize write handler itself, as we need only to test how permission
+ * update works.
+ */
+typedef struct BDRVWriteToSelectedState {
+ BdrvChild *selected;
+} BDRVWriteToSelectedState;
+
+static void write_to_selected_perms(BlockDriverState *bs, BdrvChild *c,
+ BdrvChildRole role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
{
- if (bs->file && c == bs->file) {
+ BDRVWriteToSelectedState *s = bs->opaque;
+
+ if (s->selected && c == s->selected) {
*nperm = BLK_PERM_WRITE;
*nshared = BLK_PERM_ALL & ~BLK_PERM_WRITE;
} else {
@@ -256,9 +276,10 @@ static void write_to_file_perms(BlockDriverState *bs, BdrvChild *c,
}
}
-static BlockDriver bdrv_write_to_file = {
- .format_name = "tricky-perm",
- .bdrv_child_perm = write_to_file_perms,
+static BlockDriver bdrv_write_to_selected = {
+ .format_name = "write-to-selected",
+ .instance_size = sizeof(BDRVWriteToSelectedState),
+ .bdrv_child_perm = write_to_selected_perms,
};
@@ -266,15 +287,18 @@ static BlockDriver bdrv_write_to_file = {
* The following test shows that topological-sort order is required for
* permission update, simple DFS is not enough.
*
- * Consider the block driver which has two filter children: one active
- * with exclusive write access and one inactive with no specific
- * permissions.
+ * Consider the block driver (write-to-selected) which has two children: one is
+ * selected so we have exclusive write access to it and for the other one we
+ * don't need any specific permissions.
*
* And, these two children has a common base child, like this:
+ * (additional "top" on top is used in test just because the only public
+ * function to update permission should get a specific child to update.
+ * Making bdrv_refresh_perms() public just for this test isn't worth it)
*
- * ┌─────┐ ┌──────┐
- * │ fl2 │ ◀── │ top │
- * └─────┘ └──────┘
+ * ┌─────┐ ┌───────────────────┐ ┌─────┐
+ * │ fl2 │ ◀── │ write-to-selected │ ◀── │ top │
+ * └─────┘ └───────────────────┘ └─────┘
* │ │
* │ │ w
* │ ▼
@@ -290,14 +314,14 @@ static BlockDriver bdrv_write_to_file = {
*
* So, exclusive write is propagated.
*
- * Assume, we want to make fl2 active instead of fl1.
- * So, we set some option for top driver and do permission update.
+ * Assume, we want to select fl2 instead of fl1.
+ * So, we set some option for write-to-selected driver and do permission update.
*
* With simple DFS, if permission update goes first through
- * top->fl1->base branch it will succeed: it firstly drop exclusive write
- * permissions and than apply them for another BdrvChildren.
- * But if permission update goes first through top->fl2->base branch it
- * will fail, as when we try to update fl2->base child, old not yet
+ * write-to-selected -> fl1 -> base branch it will succeed: it firstly drop
+ * exclusive write permissions and than apply them for another BdrvChildren.
+ * But if permission update goes first through write-to-selected -> fl2 -> base
+ * branch it will fail, as when we try to update fl2->base child, old not yet
* updated fl1->base child will be in conflict.
*
* With topological-sort order we always update parents before children, so fl1
@@ -306,9 +330,10 @@ static BlockDriver bdrv_write_to_file = {
static void test_parallel_perm_update(void)
{
BlockDriverState *top = no_perm_node("top");
- BlockDriverState *tricky =
- bdrv_new_open_driver(&bdrv_write_to_file, "tricky", BDRV_O_RDWR,
+ BlockDriverState *ws =
+ bdrv_new_open_driver(&bdrv_write_to_selected, "ws", BDRV_O_RDWR,
&error_abort);
+ BDRVWriteToSelectedState *s = ws->opaque;
BlockDriverState *base = no_perm_node("base");
BlockDriverState *fl1 = pass_through_node("fl1");
BlockDriverState *fl2 = pass_through_node("fl2");
@@ -320,33 +345,35 @@ static void test_parallel_perm_update(void)
*/
bdrv_ref(base);
- bdrv_attach_child(top, tricky, "file", &child_of_bds, BDRV_CHILD_DATA,
+ bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA,
&error_abort);
- c_fl1 = bdrv_attach_child(tricky, fl1, "first", &child_of_bds,
- BDRV_CHILD_FILTERED, &error_abort);
- c_fl2 = bdrv_attach_child(tricky, fl2, "second", &child_of_bds,
- BDRV_CHILD_FILTERED, &error_abort);
- bdrv_attach_child(fl1, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds,
+ BDRV_CHILD_DATA, &error_abort);
+ c_fl2 = bdrv_attach_child(ws, fl2, "second", &child_of_bds,
+ BDRV_CHILD_DATA, &error_abort);
+ bdrv_attach_child(fl1, base, "backing", &child_of_bds,
+ BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
- bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED,
+ bdrv_attach_child(fl2, base, "backing", &child_of_bds,
+ BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
/* Select fl1 as first child to be active */
- tricky->file = c_fl1;
+ s->selected = c_fl1;
bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
assert(c_fl1->perm & BLK_PERM_WRITE);
assert(!(c_fl2->perm & BLK_PERM_WRITE));
/* Now, try to switch active child and update permissions */
- tricky->file = c_fl2;
+ s->selected = c_fl2;
bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
assert(c_fl2->perm & BLK_PERM_WRITE);
assert(!(c_fl1->perm & BLK_PERM_WRITE));
/* Switch once more, to not care about real child order in the list */
- tricky->file = c_fl1;
+ s->selected = c_fl1;
bdrv_child_refresh_perms(top, top->children.lh_first, &error_abort);
assert(c_fl1->perm & BLK_PERM_WRITE);
@@ -379,7 +406,8 @@ static void test_append_greedy_filter(void)
BlockDriverState *base = no_perm_node("base");
BlockDriverState *fl = exclusive_writer_node("fl1");
- bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_COW,
+ bdrv_attach_child(top, base, "backing", &child_of_bds,
+ BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
&error_abort);
bdrv_append(fl, base, &error_abort);
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
index def0709..8ca5ade 100644
--- a/tests/unit/test-block-iothread.c
+++ b/tests/unit/test-block-iothread.c
@@ -765,7 +765,7 @@ static void test_propagate_mirror(void)
filter = bdrv_find_node("filter_node");
/* Change the AioContext of src */
- bdrv_try_set_aio_context(src, ctx, &error_abort);
+ bdrv_try_change_aio_context(src, ctx, NULL, &error_abort);
g_assert(bdrv_get_aio_context(src) == ctx);
g_assert(bdrv_get_aio_context(target) == ctx);
g_assert(bdrv_get_aio_context(filter) == ctx);
@@ -773,7 +773,7 @@ static void test_propagate_mirror(void)
/* Change the AioContext of target */
aio_context_acquire(ctx);
- bdrv_try_set_aio_context(target, main_ctx, &error_abort);
+ bdrv_try_change_aio_context(target, main_ctx, NULL, &error_abort);
aio_context_release(ctx);
g_assert(bdrv_get_aio_context(src) == main_ctx);
g_assert(bdrv_get_aio_context(target) == main_ctx);
@@ -783,7 +783,7 @@ static void test_propagate_mirror(void)
blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
blk_insert_bs(blk, src, &error_abort);
- bdrv_try_set_aio_context(target, ctx, &local_err);
+ bdrv_try_change_aio_context(target, ctx, NULL, &local_err);
error_free_or_abort(&local_err);
g_assert(blk_get_aio_context(blk) == main_ctx);
@@ -794,7 +794,7 @@ static void test_propagate_mirror(void)
/* ...unless we explicitly allow it */
aio_context_acquire(ctx);
blk_set_allow_aio_context_change(blk, true);
- bdrv_try_set_aio_context(target, ctx, &error_abort);
+ bdrv_try_change_aio_context(target, ctx, NULL, &error_abort);
aio_context_release(ctx);
g_assert(blk_get_aio_context(blk) == ctx);
@@ -806,7 +806,7 @@ static void test_propagate_mirror(void)
aio_context_acquire(ctx);
blk_set_aio_context(blk, main_ctx, &error_abort);
- bdrv_try_set_aio_context(target, main_ctx, &error_abort);
+ bdrv_try_change_aio_context(target, main_ctx, NULL, &error_abort);
aio_context_release(ctx);
blk_unref(blk);