From b885cdda79045791e1f33600639b7614ea114f16 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 20 Oct 2022 14:05:41 +0200 Subject: [PATCH 01/58] MAINTAINERS: Fold "Block QAPI, monitor, ..." into "Block layer core" Section "Block QAPI, monitor, command line" is about the external interfaces we provide for block devices. It covers the relevant QAPI schema parts, monitor and command line code, more or less. The section's files are also covered by section "Block layer core", except for the QAPI schema files. I haven't acted as maintainer in this area for a long time. Make it official: add the QAPI schema files to section "Block layer core", and delete section "Block QAPI, monitor, command line". Cc: Kevin Wolf Cc: Hanna Reitz Signed-off-by: Markus Armbruster Message-Id: <20221020120541.80757-1-armbru@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- MAINTAINERS | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 32e495e165..07a022b0d0 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: qemu-img* F: docs/tools/qemu-img.rst @@ -2582,16 +2584,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 -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 M: Jonathan Cameron From 6b6471eee11dce4c995419b68441c6637be3d90a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 10 Oct 2022 12:04:30 +0800 Subject: [PATCH 02/58] block: Ignore close() failure in get_tmp_filename() The temporary file has been created and is ready for use. Checking return value of close() does not seem useful. The file descriptor is almost certainly closed; see close(2) under "Dealing with error returns from close()". Let's simply ignore close() failure here. Suggested-by: Markus Armbruster Signed-off-by: Bin Meng Reviewed-by: Markus Armbruster Message-Id: <20221010040432.3380478-1-bin.meng@windriver.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/block.c b/block.c index 1fbf6b9e69..66a35b3982 100644 --- a/block.c +++ b/block.c @@ -887,10 +887,7 @@ int get_tmp_filename(char *filename, int size) if (fd < 0) { return -errno; } - if (close(fd) != 0) { - unlink(filename); - return -errno; - } + close(fd); return 0; #endif } From 69fbfff95e849156985cf95e2010ffc8762e34e6 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 10 Oct 2022 12:04:31 +0800 Subject: [PATCH 03/58] block: Refactor get_tmp_filename() At present there are two callers of get_tmp_filename() and they are inconsistent. One does: /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ char *tmp_filename = g_malloc0(PATH_MAX + 1); ... ret = get_tmp_filename(tmp_filename, PATH_MAX + 1); while the other does: s->qcow_filename = g_malloc(PATH_MAX); ret = get_tmp_filename(s->qcow_filename, PATH_MAX); As we can see different 'size' arguments are passed. There are also platform specific implementations inside the function, and the use of snprintf is really undesirable. The function name is also misleading. It creates a temporary file, not just a filename. Refactor this routine by changing its name and signature to: char *create_tmp_file(Error **errp) and use g_get_tmp_dir() / g_mkstemp() for a consistent implementation. While we are here, add some comments to mention that /var/tmp is preferred over /tmp on non-win32 hosts. Signed-off-by: Bin Meng Message-Id: <20221010040432.3380478-2-bin.meng@windriver.com> [kwolf: Fixed incorrect errno negation and iotest 051] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 56 +++++++++++++++++--------------- block/vvfat.c | 7 ++-- include/block/block_int-common.h | 2 +- tests/qemu-iotests/051 | 3 +- tests/qemu-iotests/051.out | 2 +- tests/qemu-iotests/051.pc.out | 2 +- 6 files changed, 38 insertions(+), 34 deletions(-) diff --git a/block.c b/block.c index 66a35b3982..c8374dac4f 100644 --- a/block.c +++ b/block.c @@ -861,35 +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; + error_setg_errno(errp, errno, "Could not open temporary file '%s'", + filename); + return NULL; } close(fd); - return 0; -#endif + + return g_steal_pointer(&filename); } /* @@ -3715,8 +3722,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; @@ -3735,9 +3741,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; } @@ -3771,7 +3776,6 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, out: qobject_unref(snapshot_options); - g_free(tmp_filename); return bs_snapshot; } diff --git a/block/vvfat.c b/block/vvfat.c index d6dd919683..f9bf8406d3 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3146,10 +3146,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/include/block/block_int-common.h b/include/block/block_int-common.h index 8947abab76..d7c0a7e96f 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -1230,7 +1230,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/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index f1a506518b..4c079b11e3 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 441f83e41a..e5ddb03bda 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 063e4fc584..bade1ff3b9 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 From d0f95b6ca0241b14d6cc0f366d162684909370a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Mon, 10 Oct 2022 19:55:10 +0200 Subject: [PATCH 04/58] vvfat: allow some writes to bootsector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'reserved1' field in bootsector is used to mark volume dirty, or need to verify. Allow writes to bootsector which only changes the 'reserved1' field. This fixes I/O errors on Windows guests. Resolves: https://bugs.launchpad.net/qemu/+bug/1889421 Signed-off-by: Hervé Poussineau Message-Id: <20221010175511.3414357-2-hpoussin@reactos.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/vvfat.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/block/vvfat.c b/block/vvfat.c index f9bf8406d3..e76b42dbaf 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -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; From 1e85c7259b7f5b5b7f9a360e2327b1baff723c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Mon, 10 Oct 2022 19:55:11 +0200 Subject: [PATCH 05/58] vvfat: allow spaces in file names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In R/W mode, files with spaces were never created on host side. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1176 Fixes: c79e243ed67683d6d06692bd7040f7394da178b0 Signed-off-by: Hervé Poussineau Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kevin Wolf Message-Id: <20221010175511.3414357-3-hpoussin@reactos.org> Signed-off-by: Kevin Wolf --- block/vvfat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/vvfat.c b/block/vvfat.c index e76b42dbaf..c5b1442145 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; } From 7845e731471365663dea8e34338f93791d31ea44 Mon Sep 17 00:00:00 2001 From: Sam Li Date: Sat, 24 Sep 2022 22:48:15 +0800 Subject: [PATCH 06/58] block/io_uring: revert "Use io_uring_register_ring_fd() to skip fd operations" Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1193 The commit "Use io_uring_register_ring_fd() to skip fd operations" broke when booting a guest with iothread and io_uring. That is because the io_uring_register_ring_fd() call is made from the main thread instead of IOThread where io_uring_submit() is called. It can not be guaranteed to register the ring fd in the correct thread or unregister the same ring fd if the IOThread is disabled. This optimization is not critical so we will revert previous commit. This reverts commit e2848bc574fe2715c694bf8fe9a1ba7f78a1125a and 77e3f038af1764983087e3551a0fde9951952c4d. Cc: qemu-stable@nongnu.org Signed-off-by: Sam Li Message-Id: <20220924144815.5591-1-faithilikerun@gmail.com> Reviewed-by: Stefano Garzarella Tested-by: Dario Faggioli Signed-off-by: Kevin Wolf --- block/io_uring.c | 13 +------------ meson.build | 1 - 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/block/io_uring.c b/block/io_uring.c index a1760152e0..973e15d876 100644 --- a/block/io_uring.c +++ b/block/io_uring.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include #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/meson.build b/meson.build index b686dfef75..44c1f92697 100644 --- a/meson.build +++ b/meson.build @@ -1839,7 +1839,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 ', 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()) From ab6075d849f4285fc730d3ae6e17418d65d09998 Mon Sep 17 00:00:00 2001 From: Li Feng Date: Mon, 19 Sep 2022 20:18:16 +0800 Subject: [PATCH 07/58] vhost-user-blk: fix the resize crash If the os is not installed and doesn't have the virtio guest driver, the vhost dev isn't started, so the dev->vdev is NULL. Reproduce: mount a Win 2019 iso, go into the install ui, then resize the virtio-blk device, qemu crash. Signed-off-by: Li Feng Message-Id: <20220919121816.3252223-1-fengli@smartx.com> Reviewed-by: Raphael Norwitz Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/block/vhost-user-blk.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 84902dde17..13bf5cc47a 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) { From 046fd84facfc4d326070bf5306dd9cedb7bd9854 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:20 +0300 Subject: [PATCH 08/58] block: BlockDriver: add .filtered_child_is_backing field Unfortunately not all filters use .file child as filtered child. Two exclusions are mirror_top and commit_top. Happily they both are private filters. Bad thing is that this inconsistency is observable through qmp commands query-block / query-named-block-nodes. So, could we just change mirror_top and commit_top to use file child as all other filter driver is an open question. Probably, we could do that with some kind of deprecation period, but how to warn users during it? For now, let's just add a field so we can distinguish them in generic code, it will be used in further commits. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-2-vsementsov@yandex-team.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/commit.c | 1 + block/mirror.c | 1 + include/block/block_int-common.h | 13 +++++++++++++ 3 files changed, 15 insertions(+) diff --git a/block/commit.c b/block/commit.c index 38571510cb..e210e86bac 100644 --- a/block/commit.c +++ b/block/commit.c @@ -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/mirror.c b/block/mirror.c index 80c0109d39..f9432af3df 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -1587,6 +1587,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/include/block/block_int-common.h b/include/block/block_int-common.h index d7c0a7e96f..95392052c9 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -119,6 +119,19 @@ struct BlockDriver { * (And this filtered child must then be bs->file or bs->backing.) */ 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. + * Only two internal filters use bs->backing as filtered child and has this + * field set to true: mirror_top and commit_top. + * + * 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 From 83930780325b144a5908c45b3957b9b6457b3831 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:21 +0300 Subject: [PATCH 09/58] block: introduce bdrv_open_file_child() helper Almost all drivers call bdrv_open_child() similarly. Let's create a helper for this. The only not updated drivers that call bdrv_open_child() to set bs->file are raw-format and snapshot-access: raw-format sometimes want to have filtered child but don't set drv->is_filter to true. snapshot-access wants only DATA | PRIMARY Possibly we should implement drv->is_filter_func() handler, to consider raw-format as filter when it works as filter.. But it's another story. Note also, that we decrease assignments to bs->file in code: it helps us restrict modifying this field in further commit. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-3-vsementsov@yandex-team.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 21 +++++++++++++++++++++ block/blkdebug.c | 9 +++------ block/blklogwrites.c | 7 ++----- block/blkreplay.c | 7 ++----- block/blkverify.c | 9 +++------ block/bochs.c | 7 +++---- block/cloop.c | 7 +++---- block/copy-before-write.c | 9 ++++----- block/copy-on-read.c | 9 ++++----- block/crypto.c | 11 ++++++----- block/dmg.c | 7 +++---- block/filter-compress.c | 8 +++----- block/parallels.c | 7 +++---- block/preallocate.c | 9 ++++----- block/qcow.c | 6 ++---- block/qcow2.c | 8 ++++---- block/qed.c | 8 ++++---- block/replication.c | 8 +++----- block/throttle.c | 8 +++----- block/vdi.c | 7 +++---- block/vhdx.c | 7 +++---- block/vmdk.c | 7 +++---- block/vpc.c | 7 +++---- include/block/block-global-state.h | 3 +++ 24 files changed, 95 insertions(+), 101 deletions(-) diff --git a/block.c b/block.c index c8374dac4f..25a596a612 100644 --- a/block.c +++ b/block.c @@ -3673,6 +3673,27 @@ BdrvChild *bdrv_open_child(const char *filename, errp); } +/* + * Wrapper on bdrv_open_child() for most popular case: open primary child of bs. + */ +int bdrv_open_file_child(const char *filename, + QDict *options, const char *bdref_key, + BlockDriverState *parent, Error **errp) +{ + BdrvChildRole role; + + /* commit_top and mirror_top don't use this function */ + assert(!parent->drv->filtered_child_is_backing); + + role = parent->drv->is_filter ? + (BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY) : BDRV_CHILD_IMAGE; + + parent->file = bdrv_open_child(filename, options, bdref_key, parent, + &child_of_bds, role, false, errp); + + return parent->file ? 0 : -EINVAL; +} + /* * 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. diff --git a/block/blkdebug.c b/block/blkdebug.c index bbf2948703..5fcfc8ac6f 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; } diff --git a/block/blklogwrites.c b/block/blklogwrites.c index e3c6c4039c..12b4c3c8cf 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; } diff --git a/block/blkreplay.c b/block/blkreplay.c index dcbe780ddb..76a0b8d12a 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 020b1ae7b6..43a2d94f7b 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/bochs.c b/block/bochs.c index b76f34fe03..e30e3908d9 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 40b146e714..3ff975a94d 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/copy-before-write.c b/block/copy-before-write.c index afbdd04489..4abaa7339e 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 1fc7fb3333..815ac1d835 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 7a57774b76..396e529bb2 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 98db18d82a..422136276a 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/filter-compress.c b/block/filter-compress.c index d5be538619..305716c86c 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/parallels.c b/block/parallels.c index c1523e7dab..27c81cee40 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -737,10 +737,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 e15cb8c74a..d50ee7f49b 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 311aaa8705..72ed4c3321 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -121,10 +121,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; } diff --git a/block/qcow2.c b/block/qcow2.c index b57f7cc8ee..b47016cf61 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -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 */ diff --git a/block/qed.c b/block/qed.c index bda00e6257..4627169348 100644 --- a/block/qed.c +++ b/block/qed.c @@ -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); diff --git a/block/replication.c b/block/replication.c index c67f931f37..ee33dc4598 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/throttle.c b/block/throttle.c index ddd450593a..131eba3ab4 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 e942325455..a9bafb5a9e 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"); diff --git a/block/vhdx.c b/block/vhdx.c index e10e78ebfd..ce1e75f5ec 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; diff --git a/block/vmdk.c b/block/vmdk.c index f7d8856dfb..d75a87cb85 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); diff --git a/block/vpc.c b/block/vpc.c index 4f49ef207f..95841f259a 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/include/block/block-global-state.h b/include/block/block-global-state.h index 21265e3966..29a38d7e18 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); From a987aa7d3c70a2f7d2e052c1e8e449fcd35c3f78 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:22 +0300 Subject: [PATCH 10/58] block/blklogwrites: don't care to remove bs->file child on failure We don't need to remove bs->file, generic layer takes care of it. No other driver cares to remove bs->file on failure by hand. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-4-vsementsov@yandex-team.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/blklogwrites.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 12b4c3c8cf..cef9efe55d 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -254,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; } From 1dcea719794b38752fdd4f8f47c743ecb03be1cc Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:23 +0300 Subject: [PATCH 11/58] test-bdrv-graph-mod: update test_parallel_perm_update test case test_parallel_perm_update() does two things that we are going to restrict in the near future: 1. It updates bs->file field by hand. bs->file will be managed automatically by generic code (together with bs->children list). Let's better refactor our "tricky" bds to have own state where one of children is linked as "selected". This also looks less "tricky", so avoid using this word. 2. It create FILTERED children that are not PRIMARY. Except for tests all FILTERED children in the Qemu block layer are always PRIMARY as well. We are going to formalize this rule, so let's better use DATA children here. 3. It creates more than one FILTERED child, which is already abandoned in BDRV_CHILD_FILTERED's description. While being here, update the picture to better correspond to the test code. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-5-vsementsov@yandex-team.ru> Signed-off-by: Kevin Wolf --- tests/unit/test-bdrv-graph-mod.c | 80 +++++++++++++++++++------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index a6e3bb79be..e2f1355af1 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -241,13 +241,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 +269,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 +280,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 +307,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 +323,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 +338,33 @@ 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); + 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, &error_abort); bdrv_attach_child(fl2, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED, &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); From 9ebfc111a1e34fcec5285095954c360acc2be01a Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:24 +0300 Subject: [PATCH 12/58] tests-bdrv-drain: bdrv_replace_test driver: declare supports_backing We do add COW child to the node. In future we are going to forbid adding COW child to the node that doesn't support backing. So, fix it here now. Don't worry about setting bs->backing itself: in further commit we'll update the block-layer to automatically set/unset this field in generic code. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-6-vsementsov@yandex-team.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/unit/test-bdrv-drain.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 4924ceb562..84e09d7070 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -1970,6 +1970,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, From 1921b4f7868cb398a8939b48e2cdac4d6ff79e41 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:25 +0300 Subject: [PATCH 13/58] test-bdrv-graph-mod: fix filters to be filters bdrv_pass_through is used as filter, even all node variables has corresponding names. We want to append it, so it should be backing-child-based filter like mirror_top. So, in test_update_perm_tree, first child should be DATA, as we don't want filters with two filtered children. bdrv_exclusive_writer is used as a filter once. So it should be filter anyway. We want to append it, so it should be backing-child-based fitler too. Make all FILTERED children to be PRIMARY as well. We are going to force this rule by assertion soon. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-7-vsementsov@yandex-team.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block_int-common.h | 5 +++-- tests/unit/test-bdrv-graph-mod.c | 24 +++++++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 95392052c9..10feef9f0c 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -122,8 +122,9 @@ struct BlockDriver { /* * Only make sense for filter drivers, for others must be false. * If true, filtered child is bs->backing. Otherwise it's bs->file. - * Only two internal filters use bs->backing as filtered child and has this - * field set to true: mirror_top and commit_top. + * 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! * diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index e2f1355af1..c522591531 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); @@ -344,9 +351,11 @@ static void test_parallel_perm_update(void) 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_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 */ @@ -397,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); From 71ca43852aeb59d69d39ca12f93063aa171a9dbf Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:26 +0300 Subject: [PATCH 14/58] block: document connection between child roles and bs->backing/bs->file Make the informal rules formal. In further commit we'll add corresponding assertions. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-8-vsementsov@yandex-team.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-common.h | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/include/block/block-common.h b/include/block/block-common.h index fdb7306e78..fda67a7c38 100644 --- a/include/block/block-common.h +++ b/include/block/block-common.h @@ -313,6 +313,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 { /* From 0c6100a7ff4733773e32b4b9bd00eb81955bd788 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:27 +0300 Subject: [PATCH 15/58] block/snapshot: stress that we fallback to primary child Actually what we chose is a primary child. Let's stress it in the code. We are going to drop indirect pointer logic here in future. Actually this commit simplifies the future work: we drop use of indirection in the assertion now. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-9-vsementsov@yandex-team.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/snapshot.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/block/snapshot.c b/block/snapshot.c index d6f53c3065..75e8d3a937 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -161,21 +161,14 @@ bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, static BdrvChild **bdrv_snapshot_fallback_ptr(BlockDriverState *bs) { BdrvChild **fallback; - BdrvChild *child; + BdrvChild *child = bdrv_primary_child(bs); - /* - * 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 (!child) { return NULL; } + fallback = (child == bs->file ? &bs->file : &bs->backing); + assert(*fallback == child); /* * Check that there are no other children that would need to be @@ -309,15 +302,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; } From 4eba825a8237985dc1c53a42bc810dcfa1a3d5e3 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:28 +0300 Subject: [PATCH 16/58] Revert "block: Let replace_child_noperm free children" We are going to reimplement this behavior (clear bs->file / bs->backing pointers automatically when child->bs is cleared) in a nicer way, see further commit "block: Manipulate bs->file / bs->backing pointers in .attach/.detach". With this revert we bring back a problem that was fixed by b0a9f6fed3d8. Still the problem was mostly theoretical, we don't have concrete bugs fixed by b0a9f6fed3d8, we don't have a specific test. Probably some accidental failures of iotests are related. Alternatively, we may merge this and following three reverts into final "block: Manipulate ..." to avoid any kind of regression. But seems that in this case having separate clear revert commits is better. This reverts commit b0a9f6fed3d80de610dcd04a7e66f9f30a04174f. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-10-vsementsov@yandex-team.ru> Signed-off-by: Kevin Wolf --- block.c | 102 +++++++++++++------------------------------------------- 1 file changed, 23 insertions(+), 79 deletions(-) diff --git a/block.c b/block.c index 25a596a612..7a9a6efca8 100644 --- a/block.c +++ b/block.c @@ -90,10 +90,8 @@ 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); + BlockDriverState *new_bs); static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, BdrvChild *child, Transaction *tran); @@ -2345,7 +2343,6 @@ typedef struct BdrvReplaceChildState { BdrvChild *child; BdrvChild **childp; BlockDriverState *old_bs; - bool free_empty_child; } BdrvReplaceChildState; static void bdrv_replace_child_commit(void *opaque) @@ -2353,9 +2350,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); } @@ -2373,26 +2367,22 @@ static void bdrv_replace_child_abort(void *opaque) * 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. + * (TODO: Right now, bdrv_replace_child_noperm() never modifies that + * pointer anyway (though it will in the future), so at this point it + * absolutely 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. + * (TODO: In its current state, bdrv_replace_child_noperm() will not + * have NULLed *s->childp, so this does not apply yet. It will in the + * future.) * * 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; - } + bdrv_replace_child_noperm(&s->child, s->old_bs); bdrv_unref(new_bs); } @@ -2409,44 +2399,30 @@ static TransactionActionDrv bdrv_replace_child_drv = { * * 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). + * (TODO: The reinstating does not happen yet, but it will once + * bdrv_replace_child_noperm() NULLs *childp when new_bs is NULL.) */ static void bdrv_replace_child_tran(BdrvChild **childp, BlockDriverState *new_bs, - Transaction *tran, - bool free_empty_child) + 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, }; 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); + bdrv_replace_child_noperm(childp, new_bs); /* old_bs reference is transparently moved from *childp to @s */ } @@ -2828,22 +2804,8 @@ 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) + BlockDriverState *new_bs) { BdrvChild *child = *childp; BlockDriverState *old_bs = child->bs; @@ -2882,9 +2844,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); @@ -2915,10 +2874,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); - } } /** @@ -2950,14 +2905,7 @@ static void bdrv_attach_child_common_abort(void *opaque) BlockDriverState *bs = 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); @@ -2979,6 +2927,7 @@ static void bdrv_attach_child_common_abort(void *opaque) bdrv_unref(bs); bdrv_child_free(child); + *s->child = NULL; } static TransactionActionDrv bdrv_attach_child_common_drv = { @@ -3057,9 +3006,7 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, } 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); + bdrv_replace_child_noperm(&new_child, child_bs); *child = new_child; @@ -3120,7 +3067,8 @@ static void bdrv_detach_child(BdrvChild **childp) BlockDriverState *old_bs = (*childp)->bs; GLOBAL_STATE_CODE(); - bdrv_replace_child_noperm(childp, NULL, true); + bdrv_replace_child_noperm(childp, NULL); + bdrv_child_free(*childp); if (old_bs) { /* @@ -5171,11 +5119,7 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, } 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(childp, NULL, tran); } s = g_new(BdrvRemoveFilterOrCowChild, 1); @@ -5185,6 +5129,8 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, .is_backing = (childp == &bs->backing), }; tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); + + *childp = NULL; } /* @@ -5228,7 +5174,7 @@ static int bdrv_replace_node_noperm(BlockDriverState *from, * 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; @@ -5393,9 +5339,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); From 0f0b1e29d35e0a7da6271b3fa0fefa63380204e7 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:29 +0300 Subject: [PATCH 17/58] Revert "block: Let replace_child_tran keep indirect pointer" That's a preparation to previously reverted "block: Let replace_child_noperm free children". Drop it too, we don't need it for a new approach. This reverts commit 82b54cf51656bf3cd5ed1ac549e8a1085a0e3290. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-11-vsementsov@yandex-team.ru> Signed-off-by: Kevin Wolf --- block.c | 81 +++++++-------------------------------------------------- 1 file changed, 10 insertions(+), 71 deletions(-) diff --git a/block.c b/block.c index 7a9a6efca8..196d1cf665 100644 --- a/block.c +++ b/block.c @@ -2341,7 +2341,6 @@ static int bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, typedef struct BdrvReplaceChildState { BdrvChild *child; - BdrvChild **childp; BlockDriverState *old_bs; } BdrvReplaceChildState; @@ -2359,29 +2358,7 @@ 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. - * (TODO: Right now, bdrv_replace_child_noperm() never modifies that - * pointer anyway (though it will in the future), so at this point it - * absolutely 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. - * (TODO: In its current state, bdrv_replace_child_noperm() will not - * have NULLed *s->childp, so this does not apply yet. It will in the - * future.) - * - * 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. - */ + /* old_bs reference is transparently moved from @s to @s->child */ bdrv_replace_child_noperm(&s->child, s->old_bs); bdrv_unref(new_bs); } @@ -2398,32 +2375,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. - * - * 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. - * - * (TODO: The reinstating does not happen yet, but it will once - * bdrv_replace_child_noperm() NULLs *childp when new_bs is NULL.) */ -static void bdrv_replace_child_tran(BdrvChild **childp, - BlockDriverState *new_bs, +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, + .child = child, + .old_bs = child->bs, }; tran_add(tran, &bdrv_replace_child_drv, s); if (new_bs) { bdrv_ref(new_bs); } - bdrv_replace_child_noperm(childp, new_bs); - /* 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 */ } /* @@ -5045,7 +5012,6 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) typedef struct BdrvRemoveFilterOrCowChild { BdrvChild *child; - BlockDriverState *bs; bool is_backing; } BdrvRemoveFilterOrCowChild; @@ -5075,19 +5041,10 @@ static void bdrv_remove_filter_or_cow_child_commit(void *opaque) 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); -} - 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, + .clean = g_free, }; /* @@ -5105,11 +5062,6 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, 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) { @@ -5119,13 +5071,12 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, } if (child->bs) { - bdrv_replace_child_tran(childp, NULL, tran); + bdrv_replace_child_tran(*childp, 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); @@ -5151,7 +5102,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) { @@ -5169,12 +5119,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); + bdrv_replace_child_tran(c, to, tran); } return 0; @@ -5189,8 +5134,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, @@ -5204,7 +5147,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)); @@ -5261,9 +5203,6 @@ out: return ret; } -/** - * Replace node @from by @to (where neither may be NULL). - */ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp) { @@ -5339,7 +5278,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); + 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); From a2c37a30422328fe2391bd1ff342f8941c7681bc Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:30 +0300 Subject: [PATCH 18/58] Revert "block: Restructure remove_file_or_backing_child()" That's a preparation to previously reverted "block: Let replace_child_noperm free children". Drop it too, we don't need it for a new approach. This reverts commit 562bda8bb41879eeda0bd484dd3d55134579b28e. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-12-vsementsov@yandex-team.ru> Signed-off-by: Kevin Wolf --- block.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/block.c b/block.c index 196d1cf665..2f80b7e094 100644 --- a/block.c +++ b/block.c @@ -5055,33 +5055,30 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, BdrvChild *child, Transaction *tran) { - BdrvChild **childp; BdrvRemoveFilterOrCowChild *s; + assert(child == bs->backing || child == bs->file); + if (!child) { return; } - if (child == bs->backing) { - childp = &bs->backing; - } else if (child == bs->file) { - childp = &bs->file; - } else { - g_assert_not_reached(); - } - if (child->bs) { - bdrv_replace_child_tran(*childp, NULL, tran); + bdrv_replace_child_tran(child, NULL, tran); } s = g_new(BdrvRemoveFilterOrCowChild, 1); *s = (BdrvRemoveFilterOrCowChild) { .child = child, - .is_backing = (childp == &bs->backing), + .is_backing = (child == bs->backing), }; tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); - *childp = NULL; + if (s->is_backing) { + bs->backing = NULL; + } else { + bs->file = NULL; + } } /* From 544acc7d1e1eebce76c9a8bc76a660433df2c3cd Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:31 +0300 Subject: [PATCH 19/58] Revert "block: Pass BdrvChild ** to replace_child_noperm" That's a preparation to previously reverted "block: Let replace_child_noperm free children". Drop it too, we don't need it for a new approach. This reverts commit be64bbb0149748f3999c49b13976aafb8330ea86. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-13-vsementsov@yandex-team.ru> Signed-off-by: Kevin Wolf --- block.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/block.c b/block.c index 2f80b7e094..683a9e0eff 100644 --- a/block.c +++ b/block.c @@ -90,7 +90,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, static bool bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child); -static void bdrv_replace_child_noperm(BdrvChild **child, +static void bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs); static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, BdrvChild *child, @@ -2359,7 +2359,7 @@ static void bdrv_replace_child_abort(void *opaque) GLOBAL_STATE_CODE(); /* old_bs reference is transparently moved from @s to @s->child */ - bdrv_replace_child_noperm(&s->child, s->old_bs); + bdrv_replace_child_noperm(s->child, s->old_bs); bdrv_unref(new_bs); } @@ -2389,7 +2389,7 @@ static void bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, if (new_bs) { bdrv_ref(new_bs); } - bdrv_replace_child_noperm(&child, new_bs); + bdrv_replace_child_noperm(child, new_bs); /* old_bs reference is transparently moved from @child to @s */ } @@ -2771,10 +2771,9 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) return permissions[qapi_perm]; } -static void bdrv_replace_child_noperm(BdrvChild **childp, +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; @@ -2872,7 +2871,7 @@ static void bdrv_attach_child_common_abort(void *opaque) BlockDriverState *bs = child->bs; GLOBAL_STATE_CODE(); - bdrv_replace_child_noperm(s->child, NULL); + bdrv_replace_child_noperm(child, NULL); if (bdrv_get_aio_context(bs) != s->old_child_ctx) { bdrv_try_set_aio_context(bs, s->old_child_ctx, &error_abort); @@ -2973,7 +2972,7 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, } bdrv_ref(child_bs); - bdrv_replace_child_noperm(&new_child, child_bs); + bdrv_replace_child_noperm(new_child, child_bs); *child = new_child; @@ -3029,13 +3028,13 @@ static int bdrv_attach_child_noperm(BlockDriverState *parent_bs, return 0; } -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); - bdrv_child_free(*childp); + bdrv_replace_child_noperm(child, NULL); + bdrv_child_free(child); if (old_bs) { /* @@ -3147,7 +3146,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); } From 5bb047477807375e2d5e2494b1d1302d5cea4b73 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:32 +0300 Subject: [PATCH 20/58] block: Manipulate bs->file / bs->backing pointers in .attach/.detach bs->file and bs->backing are a kind of duplication of part of bs->children. But very useful diplication, so let's not drop them at all:) We should manage bs->file and bs->backing in same place, where we manage bs->children, to keep them in sync. Moreover, generic io paths are unprepared to BdrvChild without a bs, so it's double good to clear bs->file / bs->backing when we detach the child. Detach is simple: if we detach bs->file or bs->backing child, just set corresponding field to NULL. Attach is a bit more complicated. But we still can precisely detect should we set one of bs->file / bs->backing or not: - if role is BDRV_CHILD_COW, we definitely deal with bs->backing - else, if role is BDRV_CHILD_FILTERED (it must be also BDRV_CHILD_PRIMARY), it's a filtered child. Use bs->drv->filtered_child_is_backing to chose the pointer field to modify. - else, if role is BDRV_CHILD_PRIMARY, we deal with bs->file - in all other cases, it's neither bs->backing nor bs->file. It's some other child and we shouldn't care OK. This change brings one more good thing: we can (and should) get rid of all indirect pointers in the block-graph-change transactions: bdrv_attach_child_common() stores BdrvChild** into transaction to clear it on abort. bdrv_attach_child_common() has two callers: bdrv_attach_child_noperm() just pass-through this feature, bdrv_root_attach_child() doesn't need the feature. Look at bdrv_attach_child_noperm() callers: - bdrv_attach_child() doesn't need the feature - bdrv_set_file_or_backing_noperm() uses the feature to manage bs->file and bs->backing, we don't want it anymore - bdrv_append() uses the feature to manage bs->backing, again we don't want it anymore So, we should drop this stuff! Great! We could probably keep BdrvChild** argument to keep the int return value, but it seems not worth the complexity. Finally, we now set .file / .backing automatically in generic code and want to restring setting them by hand outside of .attach/.detach. So, this patch cleanups all remaining places where they were set. To find such places I use: git grep '\->file =' git grep '\->backing =' git grep '&.*\' git grep '&.*\' Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-14-vsementsov@yandex-team.ru> Signed-off-by: Kevin Wolf --- block.c | 232 ++++++++++++++----------------- block/raw-format.c | 4 +- block/snapshot-access.c | 6 +- block/snapshot.c | 1 - include/block/block_int-common.h | 15 +- tests/unit/test-bdrv-drain.c | 10 +- 6 files changed, 125 insertions(+), 143 deletions(-) diff --git a/block.c b/block.c index 683a9e0eff..ed11a421b0 100644 --- a/block.c +++ b/block.c @@ -1445,9 +1445,39 @@ static void bdrv_child_cb_attach(BdrvChild *child) assert_bdrv_graph_writable(bs); QLIST_INSERT_HEAD(&bs->children, child, next); + 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 (child->role & BDRV_CHILD_COW) { + 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); @@ -1465,6 +1495,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, @@ -1670,7 +1706,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; @@ -2859,7 +2895,7 @@ static void bdrv_child_free(BdrvChild *child) } typedef struct BdrvAttachChildCommonState { - BdrvChild **child; + BdrvChild *child; AioContext *old_parent_ctx; AioContext *old_child_ctx; } BdrvAttachChildCommonState; @@ -2867,33 +2903,31 @@ 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(); - bdrv_replace_child_noperm(child, NULL); + 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); } - if (bdrv_child_get_parent_aio_context(child) != s->old_parent_ctx) { + if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) { GSList *ignore; /* 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); + s->child->klass->can_set_aio_ctx(s->child, s->old_parent_ctx, &ignore, + &error_abort); g_slist_free(ignore); ignore = NULL; - child->klass->set_aio_ctx(child, s->old_parent_ctx, &ignore); + s->child->klass->set_aio_ctx(s->child, s->old_parent_ctx, &ignore); g_slist_free(ignore); } bdrv_unref(bs); - bdrv_child_free(child); - *s->child = NULL; + bdrv_child_free(s->child); } static TransactionActionDrv bdrv_attach_child_common_drv = { @@ -2904,28 +2938,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(); @@ -2967,42 +2995,35 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs, 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); - *child = new_child; - 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); @@ -3011,21 +3032,16 @@ 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 *child) @@ -3070,15 +3086,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; } @@ -3086,11 +3103,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; } /* @@ -3112,14 +3128,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; } @@ -3130,12 +3147,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. */ @@ -3277,7 +3292,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; @@ -3335,14 +3349,12 @@ static int bdrv_set_file_or_backing_noperm(BlockDriverState *parent_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; } @@ -3598,14 +3610,16 @@ int bdrv_open_file_child(const char *filename, /* 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; - parent->file = bdrv_open_child(filename, options, bdref_key, parent, - &child_of_bds, role, false, errp); + if (!bdrv_open_child(filename, options, bdref_key, parent, + &child_of_bds, role, false, errp)) + { + return -EINVAL; + } - return parent->file ? 0 : -EINVAL; + return 0; } /* @@ -4877,8 +4891,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); @@ -5009,41 +5023,14 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return ret; } -typedef struct BdrvRemoveFilterOrCowChild { - BdrvChild *child; - bool is_backing; -} BdrvRemoveFilterOrCowChild; - -static void bdrv_remove_filter_or_cow_child_abort(void *opaque) -{ - BdrvRemoveFilterOrCowChild *s = opaque; - BlockDriverState *parent_bs = s->child->opaque; - - if (s->is_backing) { - parent_bs->backing = s->child; - } else { - parent_bs->file = s->child; - } - - /* - * We don't have to restore child->bs here to undo bdrv_replace_child_tran() - * because that function is transactionable and it registered own completion - * entries in @tran, so .abort() for bdrv_replace_child_safe() will be - * called automatically. - */ -} - static void bdrv_remove_filter_or_cow_child_commit(void *opaque) { - BdrvRemoveFilterOrCowChild *s = opaque; GLOBAL_STATE_CODE(); - bdrv_child_free(s->child); + 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 = g_free, }; /* @@ -5054,8 +5041,6 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, BdrvChild *child, Transaction *tran) { - BdrvRemoveFilterOrCowChild *s; - assert(child == bs->backing || child == bs->file); if (!child) { @@ -5066,18 +5051,7 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, bdrv_replace_child_tran(child, NULL, tran); } - s = g_new(BdrvRemoveFilterOrCowChild, 1); - *s = (BdrvRemoveFilterOrCowChild) { - .child = child, - .is_backing = (child == bs->backing), - }; - tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, s); - - if (s->is_backing) { - bs->backing = NULL; - } else { - bs->file = NULL; - } + tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, child); } /* @@ -5231,16 +5205,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; } diff --git a/block/raw-format.c b/block/raw-format.c index f337ac7569..408b20e22d 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -458,8 +458,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/snapshot-access.c b/block/snapshot-access.c index 77b87c1946..0a30ec6cd9 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 75e8d3a937..f3971ac2bd 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -288,7 +288,6 @@ int bdrv_snapshot_goto(BlockDriverState *bs, /* .bdrv_open() will re-attach it */ bdrv_unref_child(bs, *fallback_ptr); - *fallback_ptr = NULL; ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 10feef9f0c..1f300ee7f6 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -1056,9 +1056,6 @@ struct BlockDriverState { QDict *full_open_options; char exact_filename[PATH_MAX]; - BdrvChild *backing; - BdrvChild *file; - /* I/O Limits */ BlockLimits bl; @@ -1117,7 +1114,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; diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 84e09d7070..0eecf14310 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -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); } } @@ -2050,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); From c5c2174146a30423103f25a76e079b3c9c4e59b1 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:33 +0300 Subject: [PATCH 21/58] block/snapshot: drop indirection around bdrv_snapshot_fallback_ptr Now the indirection is not actually used, we can safely reduce it to simple pointer. For consistency do a bit of refactoring to get rid of _ptr suffixes that become meaningless. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-15-vsementsov@yandex-team.ru> Signed-off-by: Kevin Wolf --- block/snapshot.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/block/snapshot.c b/block/snapshot.c index f3971ac2bd..e22ac3eac6 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -151,34 +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 *child = bdrv_primary_child(bs); + BdrvChild *fallback = bdrv_primary_child(bs); + BdrvChild *child; /* We allow fallback only to primary child */ - if (!child) { + if (!fallback) { return NULL; } - fallback = (child == bs->file ? &bs->file : &bs->backing); - assert(*fallback == child); /* * 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; } @@ -189,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) @@ -237,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(); @@ -260,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); @@ -277,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 */ @@ -287,7 +281,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, } /* .bdrv_open() will re-attach it */ - bdrv_unref_child(bs, *fallback_ptr); + 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); From 57f08941d32a81e687d222294d33bdbfd75b5f44 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Tue, 26 Jul 2022 23:11:34 +0300 Subject: [PATCH 22/58] block: refactor bdrv_remove_file_or_backing_child to bdrv_remove_child Now the function can remove any child, so give it more common name. Drop assertions and drop bs argument which becomes unused. Function would be reused in a further commit. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Hanna Reitz Message-Id: <20220726201134.924743-16-vsementsov@yandex-team.ru> Signed-off-by: Kevin Wolf --- block.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/block.c b/block.c index ed11a421b0..2d74bfe665 100644 --- a/block.c +++ b/block.c @@ -92,9 +92,7 @@ static bool bdrv_recurse_has_child(BlockDriverState *bs, static void bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs); -static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, - BdrvChild *child, - Transaction *tran); +static void bdrv_remove_child(BdrvChild *child, Transaction *tran); static void bdrv_remove_filter_or_cow_child(BlockDriverState *bs, Transaction *tran); @@ -3342,7 +3340,7 @@ 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) { @@ -5023,26 +5021,19 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return ret; } -static void bdrv_remove_filter_or_cow_child_commit(void *opaque) +static void bdrv_remove_child_commit(void *opaque) { GLOBAL_STATE_CODE(); bdrv_child_free(opaque); } -static TransactionActionDrv bdrv_remove_filter_or_cow_child_drv = { - .commit = bdrv_remove_filter_or_cow_child_commit, +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) { - assert(child == bs->backing || child == bs->file); - if (!child) { return; } @@ -5051,7 +5042,7 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs, bdrv_replace_child_tran(child, NULL, tran); } - tran_add(tran, &bdrv_remove_filter_or_cow_child_drv, child); + tran_add(tran, &bdrv_remove_child_drv, child); } /* @@ -5062,7 +5053,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, From 7f898610f6782d6303c14c3c180b88ce1b303754 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:43 -0400 Subject: [PATCH 23/58] block.c: assert bs->aio_context is written under BQL and drains Also here ->aio_context is read by I/O threads and written under BQL. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Kevin Wolf Reviewed-by: Hanna Reitz Message-Id: <20221025084952.2139888-2-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block.c b/block.c index 2d74bfe665..4d727aa38c 100644 --- a/block.c +++ b/block.c @@ -7153,6 +7153,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; } @@ -7166,6 +7167,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) { From 7e8c182fb5e5950a52623b0d463c2f1fcd15a80a Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:44 -0400 Subject: [PATCH 24/58] block: use transactions as a replacement of ->{can_}set_aio_context() Simplify the way the aiocontext can be changed in a BDS graph. There are currently two problems in bdrv_try_set_aio_context: - There is a confusion of AioContext locks taken and released, because we assume that old aiocontext is always taken and new one is taken inside. - It doesn't look very safe to call bdrv_drained_begin while some nodes have already switched to the new aiocontext and others haven't. This could be especially dangerous because bdrv_drained_begin polls, so something else could be executed while graph is in an inconsistent state. Additional minor nitpick: can_set and set_ callbacks both traverse the graph, both using the ignored list of visited nodes in a different way. Therefore, get rid of all of this and introduce a new callback, change_aio_context, that uses transactions to efficiently, cleanly and most importantly safely change the aiocontext of a graph. This new callback is a "merge" of the two previous ones: - Just like can_set_aio_context, recursively traverses the graph. Marks all nodes that are visited using a GList, and checks if they *could* change the aio_context. - For each node that passes the above check, drain it and add a new transaction that implements a callback that effectively changes the aiocontext. - Once done, the recursive function returns if *all* nodes can change the AioContext. If so, commit the above transactions. Regardless of the outcome, call transaction.clean() to undo all drains done in the recursion. - The transaction list is scanned only after all nodes are being drained, so we are sure that they all are in the same context, and then we switch their AioContext, concluding the drain only after all nodes switched to the new AioContext. In this way we make sure that bdrv_drained_begin() is always called under the old AioContext, and bdrv_drained_end() under the new one. - Because of the above, we don't need to release and re-acquire the old AioContext every time, as everything is done once (and not per-node drain and aiocontext change). Note that the "change" API is not yet invoked anywhere. Signed-off-by: Emanuele Giuseppe Esposito Message-Id: <20221025084952.2139888-3-eesposit@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 220 ++++++++++++++++++++++++++++- include/block/block-global-state.h | 6 + include/block/block_int-common.h | 3 + 3 files changed, 228 insertions(+), 1 deletion(-) diff --git a/block.c b/block.c index 4d727aa38c..38e5d831ca 100644 --- a/block.c +++ b/block.c @@ -104,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, + GSList **visited, Transaction *tran, + Error **errp); + /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; @@ -7196,7 +7200,7 @@ static void bdrv_attach_aio_context(BlockDriverState *bs, * 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 + * @ignore will accumulate all visited BdrvChild objects. The caller is * responsible for freeing the list afterwards. */ void bdrv_set_aio_context_ignore(BlockDriverState *bs, @@ -7305,6 +7309,38 @@ static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx, return true; } +typedef struct BdrvStateSetAioContext { + AioContext *new_ctx; + BlockDriverState *bs; +} BdrvStateSetAioContext; + +static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, + GSList **visited, Transaction *tran, + Error **errp) +{ + GLOBAL_STATE_CODE(); + if (g_slist_find(*visited, c)) { + return true; + } + *visited = g_slist_prepend(*visited, c); + + /* + * A BdrvChildClass that doesn't handle AioContext changes cannot + * tolerate any AioContext changes + */ + 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->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) { @@ -7316,6 +7352,18 @@ bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, return bdrv_can_set_aio_context(c->bs, ctx, ignore, errp); } +bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, + GSList **visited, Transaction *tran, + Error **errp) +{ + GLOBAL_STATE_CODE(); + if (g_slist_find(*visited, c)) { + return true; + } + *visited = g_slist_prepend(*visited, c); + return bdrv_change_aio_context(c->bs, ctx, visited, tran, errp); +} + /* @ignore will accumulate all visited BdrvChild object. The caller is * responsible for freeing the list afterwards. */ bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, @@ -7343,6 +7391,98 @@ bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, return true; } +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); +} + +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, + GSList **visited, Transaction *tran, + Error **errp) +{ + BdrvChild *c; + BdrvStateSetAioContext *state; + + GLOBAL_STATE_CODE(); + + if (bdrv_get_aio_context(bs) == ctx) { + return true; + } + + QLIST_FOREACH(c, &bs->parents, next_parent) { + if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) { + return false; + } + } + + QLIST_FOREACH(c, &bs->children, next) { + 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) { @@ -7366,6 +7506,84 @@ int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, return 0; } +/* + * 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_child_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp) +{ + Transaction *tran; + GSList *visited; + int ret; + AioContext *old_context = bdrv_get_aio_context(bs); + GLOBAL_STATE_CODE(); + + /* + * 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 = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL; + ret = bdrv_change_aio_context(bs, ctx, &visited, tran, errp); + g_slist_free(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; + } + + /* + * 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); + } + + tran_commit(tran); + + 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; +} + int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, Error **errp) { diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 29a38d7e18..7b0095b419 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -232,6 +232,12 @@ 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, + GSList **visited, Transaction *tran, + Error **errp); +int bdrv_child_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_int-common.h b/include/block/block_int-common.h index 1f300ee7f6..9067a99249 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -910,6 +910,9 @@ struct BdrvChildClass { GSList **ignore, Error **errp); void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore); + bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, + GSList **visited, Transaction *tran, Error **errp); + AioContext *(*get_parent_aio_context)(BdrvChild *child); /* From e08cc001d502958040565636e19a42c06e1ae8b7 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:45 -0400 Subject: [PATCH 25/58] bdrv_change_aio_context: use hash table instead of list of visited nodes Minor performance improvement, but given that we have hash tables available, avoid iterating in the visited nodes list every time just to check if a node has been already visited. The data structure is not actually a proper hash map, but an hash set, as we are just adding nodes and not key,value pairs. Suggested-by: Hanna Reitz Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Kevin Wolf Message-Id: <20221025084952.2139888-4-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 28 ++++++++++++++++------------ include/block/block-global-state.h | 2 +- include/block/block_int-common.h | 3 ++- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/block.c b/block.c index 38e5d831ca..59319d9b0f 100644 --- a/block.c +++ b/block.c @@ -105,7 +105,7 @@ 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, - GSList **visited, Transaction *tran, + GHashTable *visited, Transaction *tran, Error **errp); /* If non-zero, use only whitelisted block drivers */ @@ -7315,14 +7315,15 @@ typedef struct BdrvStateSetAioContext { } BdrvStateSetAioContext; static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, - GSList **visited, Transaction *tran, + GHashTable *visited, + Transaction *tran, Error **errp) { GLOBAL_STATE_CODE(); - if (g_slist_find(*visited, c)) { + if (g_hash_table_contains(visited, c)) { return true; } - *visited = g_slist_prepend(*visited, c); + g_hash_table_add(visited, c); /* * A BdrvChildClass that doesn't handle AioContext changes cannot @@ -7353,14 +7354,14 @@ bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, } bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, - GSList **visited, Transaction *tran, + GHashTable *visited, Transaction *tran, Error **errp) { GLOBAL_STATE_CODE(); - if (g_slist_find(*visited, c)) { + if (g_hash_table_contains(visited, c)) { return true; } - *visited = g_slist_prepend(*visited, c); + g_hash_table_add(visited, c); return bdrv_change_aio_context(c->bs, ctx, visited, tran, errp); } @@ -7445,7 +7446,7 @@ static TransactionActionDrv set_aio_context = { * responsible for freeing the list afterwards. */ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, - GSList **visited, Transaction *tran, + GHashTable *visited, Transaction *tran, Error **errp) { BdrvChild *c; @@ -7524,7 +7525,7 @@ int bdrv_child_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, BdrvChild *ignore_child, Error **errp) { Transaction *tran; - GSList *visited; + GHashTable *visited; int ret; AioContext *old_context = bdrv_get_aio_context(bs); GLOBAL_STATE_CODE(); @@ -7536,9 +7537,12 @@ int bdrv_child_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, * is successful (the transaction itself). */ tran = tran_new(); - visited = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL; - ret = bdrv_change_aio_context(bs, ctx, &visited, tran, errp); - g_slist_free(visited); + 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. diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 7b0095b419..e7372ec541 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -233,7 +233,7 @@ bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c); bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, - GSList **visited, Transaction *tran, + GHashTable *visited, Transaction *tran, Error **errp); int bdrv_child_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, BdrvChild *ignore_child, Error **errp); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 9067a99249..7ccbbdae05 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -911,7 +911,8 @@ struct BdrvChildClass { void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore); bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, - GSList **visited, Transaction *tran, Error **errp); + GHashTable *visited, Transaction *tran, + Error **errp); AioContext *(*get_parent_aio_context)(BdrvChild *child); From 3428b100dc65dbce3e3b646df471a8fb2a5df556 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:46 -0400 Subject: [PATCH 26/58] blockjob: implement .change_aio_ctx in child_job child_job_change_aio_ctx() is very similar to child_job_can_set_aio_ctx(), but it implements a new transaction so that if all check pass, the new transaction's .commit() will take care of changin the BlockJob AioContext. child_job_set_aio_ctx_commit() is similar to child_job_set_aio_ctx(), but it doesn't need to invoke the recursion, as this is already taken care by child_job_change_aio_ctx(). Note: bdrv_child_try_change_aio_context() is not called by anyone at this point. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Hanna Reitz Reviewed-by: Kevin Wolf Message-Id: <20221025084952.2139888-5-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- blockjob.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/blockjob.c b/blockjob.c index bdf20a0e35..5a783b75c6 100644 --- a/blockjob.c +++ b/blockjob.c @@ -126,6 +126,50 @@ static void child_job_drained_end(BdrvChild *c, int *drained_end_counter) job_resume(&job->job); } +typedef struct BdrvStateChildJobContext { + AioContext *new_ctx; + BlockJob *job; +} BdrvStateChildJobContext; + +static void child_job_set_aio_ctx_commit(void *opaque) +{ + BdrvStateChildJobContext *s = opaque; + BlockJob *job = s->job; + + job_set_aio_context(&job->job, s->new_ctx); +} + +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 (!bdrv_child_change_aio_context(sibling, ctx, visited, + tran, errp)) { + return false; + } + } + + s = g_new(BdrvStateChildJobContext, 1); + *s = (BdrvStateChildJobContext) { + .new_ctx = ctx, + .job = job, + }; + + tran_add(tran, &change_child_job_context, s); + return true; +} + static bool child_job_can_set_aio_ctx(BdrvChild *c, AioContext *ctx, GSList **ignore, Error **errp) { @@ -174,6 +218,7 @@ static const BdrvChildClass child_job = { .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, }; From 27633e740e784709f256f6ee70f2dd0b5a87279f Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:47 -0400 Subject: [PATCH 27/58] block: implement .change_aio_ctx in child_of_bds bdrv_child_cb_change_aio_ctx() is identical to bdrv_child_cb_can_set_aio_ctx(), as we only need to recursively go on the parent bs. Note: bdrv_child_try_change_aio_context() is not called by anyone at this point. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Hanna Reitz Reviewed-by: Kevin Wolf Message-Id: <20221025084952.2139888-6-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/block.c b/block.c index 59319d9b0f..3386457083 100644 --- a/block.c +++ b/block.c @@ -1242,6 +1242,14 @@ static int bdrv_child_cb_inactivate(BdrvChild *child) return 0; } +static bool bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) +{ + BlockDriverState *bs = child->opaque; + return bdrv_change_aio_context(bs, ctx, visited, tran, errp); +} + static bool bdrv_child_cb_can_set_aio_ctx(BdrvChild *child, AioContext *ctx, GSList **ignore, Error **errp) { @@ -1534,6 +1542,7 @@ const BdrvChildClass child_of_bds = { .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, }; From 33949396216fa16a97f5b33877b50d5830f21aad Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:48 -0400 Subject: [PATCH 28/58] block-backend: implement .change_aio_ctx in child_root blk_root_change_aio_ctx() is very similar to blk_root_can_set_aio_ctx(), but implements a new transaction so that if all check pass, the new transaction's .commit will take care of changing the BlockBackend AioContext. blk_root_set_aio_ctx_commit() is the same as blk_root_set_aio_ctx(). Note: bdrv_child_try_change_aio_context() is not called by anyone at this point. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Kevin Wolf Message-Id: <20221025084952.2139888-7-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- block/block-backend.c | 52 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/block/block-backend.c b/block/block-backend.c index aa4adf06ae..d87ae435a7 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -138,6 +138,9 @@ 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) { @@ -336,6 +339,7 @@ static const BdrvChildClass child_root = { .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, }; @@ -2177,6 +2181,54 @@ int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, return blk_do_set_aio_context(blk, new_context, true, 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) { + /* + * 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; + } + } + + s = g_new(BdrvStateBlkRootContext, 1); + *s = (BdrvStateBlkRootContext) { + .new_ctx = ctx, + .blk = blk, + }; + + tran_add(tran, &set_blk_root_context, s); + return true; +} + static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx, GSList **ignore, Error **errp) { From f8be48adf08641f43dfb34b6abf50f9bc21fc250 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:49 -0400 Subject: [PATCH 29/58] block: use the new _change_ API instead of _can_set_ and _set_ Replace all direct usage of ->can_set_aio_ctx and ->set_aio_ctx, and call bdrv_child_try_change_aio_context() in bdrv_try_set_aio_context(), the main function called through the whole block layer. From this point onwards, ->can_set_aio_ctx and ->set_aio_ctx won't be used anymore. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Kevin Wolf Message-Id: <20221025084952.2139888-8-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 44 ++++++++++++++++++++++++------------------- block/block-backend.c | 8 ++++++-- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/block.c b/block.c index 3386457083..c35249e8c3 100644 --- a/block.c +++ b/block.c @@ -2924,17 +2924,21 @@ static void bdrv_attach_child_common_abort(void *opaque) } if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) { - GSList *ignore; + Transaction *tran; + GHashTable *visited; + bool ret; - /* No need to ignore `child`, because it has been detached already */ - ignore = NULL; - s->child->klass->can_set_aio_ctx(s->child, s->old_parent_ctx, &ignore, - &error_abort); - g_slist_free(ignore); + tran = tran_new(); - ignore = NULL; - s->child->klass->set_aio_ctx(s->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); @@ -2989,18 +2993,20 @@ static BdrvChild *bdrv_attach_child_common(BlockDriverState *child_bs, 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)) - { + 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) { @@ -7601,7 +7607,7 @@ 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); + return bdrv_child_try_change_aio_context(bs, ctx, NULL, errp); } void bdrv_add_aio_context_notifier(BlockDriverState *bs, diff --git a/block/block-backend.c b/block/block-backend.c index d87ae435a7..ff417dbff9 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2153,8 +2153,12 @@ 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_child_try_change_aio_context(bs, new_context, blk->root, + errp); if (ret < 0) { bdrv_unref(bs); return ret; From d2aafbb68a0390b8166fbf62c572b306f7bf02ce Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:50 -0400 Subject: [PATCH 30/58] block: remove all unused ->can_set_aio_ctx and ->set_aio_ctx callbacks Together with all _can_set_ and _set_ APIs, as they are not needed anymore. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Kevin Wolf Message-Id: <20221025084952.2139888-9-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 196 ----------------------------- block/block-backend.c | 33 ----- blockjob.c | 35 ------ include/block/block-global-state.h | 9 -- include/block/block_int-common.h | 4 - 5 files changed, 277 deletions(-) diff --git a/block.c b/block.c index c35249e8c3..353240eecd 100644 --- a/block.c +++ b/block.c @@ -1250,20 +1250,6 @@ static bool bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx, return bdrv_change_aio_context(bs, ctx, visited, tran, errp); } -static bool bdrv_child_cb_can_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore, Error **errp) -{ - BlockDriverState *bs = child->opaque; - return bdrv_can_set_aio_context(bs, ctx, ignore, errp); -} - -static void bdrv_child_cb_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore) -{ - BlockDriverState *bs = child->opaque; - return bdrv_set_aio_context_ignore(bs, ctx, ignore); -} - /* * Returns the options and flags that a temporary snapshot should get, based on * the originally requested flags (the originally requested image will have @@ -1540,8 +1526,6 @@ 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, @@ -7205,125 +7189,6 @@ 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 objects. 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); - } -} - -static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx, - GSList **ignore, Error **errp) -{ - GLOBAL_STATE_CODE(); - if (g_slist_find(*ignore, c)) { - return true; - } - *ignore = g_slist_prepend(*ignore, c); - - /* - * A BdrvChildClass that doesn't handle AioContext changes cannot - * tolerate any AioContext changes - */ - if (!c->klass->can_set_aio_ctx) { - char *user = bdrv_child_user_desc(c); - error_setg(errp, "Changing iothreads is not supported by %s", user); - g_free(user); - return false; - } - if (!c->klass->can_set_aio_ctx(c, ctx, ignore, errp)) { - assert(!errp || *errp); - return false; - } - return true; -} - typedef struct BdrvStateSetAioContext { AioContext *new_ctx; BlockDriverState *bs; @@ -7357,17 +7222,6 @@ static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, return true; } -bool bdrv_child_can_set_aio_context(BdrvChild *c, AioContext *ctx, - GSList **ignore, Error **errp) -{ - GLOBAL_STATE_CODE(); - if (g_slist_find(*ignore, c)) { - return true; - } - *ignore = g_slist_prepend(*ignore, c); - return bdrv_can_set_aio_context(c->bs, ctx, ignore, errp); -} - bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, GHashTable *visited, Transaction *tran, Error **errp) @@ -7380,33 +7234,6 @@ bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, return bdrv_change_aio_context(c->bs, ctx, visited, tran, errp); } -/* @ignore will accumulate all visited BdrvChild object. The caller is - * responsible for freeing the list afterwards. */ -bool bdrv_can_set_aio_context(BlockDriverState *bs, AioContext *ctx, - GSList **ignore, Error **errp) -{ - BdrvChild *c; - - if (bdrv_get_aio_context(bs) == ctx) { - return true; - } - - GLOBAL_STATE_CODE(); - - QLIST_FOREACH(c, &bs->parents, next_parent) { - if (!bdrv_parent_can_set_aio_context(c, ctx, ignore, errp)) { - return false; - } - } - QLIST_FOREACH(c, &bs->children, next) { - if (!bdrv_child_can_set_aio_context(c, ctx, ignore, errp)) { - return false; - } - } - - return true; -} - static void bdrv_set_aio_context_clean(void *opaque) { BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; @@ -7499,29 +7326,6 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, return true; } -int bdrv_child_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, Error **errp) -{ - GSList *ignore; - bool ret; - - 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); - - if (!ret) { - return -EPERM; - } - - ignore = ignore_child ? g_slist_prepend(NULL, ignore_child) : NULL; - bdrv_set_aio_context_ignore(bs, ctx, &ignore); - g_slist_free(ignore); - - return 0; -} - /* * 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. diff --git a/block/block-backend.c b/block/block-backend.c index ff417dbff9..a91c8d3916 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -134,10 +134,6 @@ 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); @@ -337,8 +333,6 @@ 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, @@ -2233,33 +2227,6 @@ static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, return true; } -static bool blk_root_can_set_aio_ctx(BdrvChild *child, AioContext *ctx, - GSList **ignore, Error **errp) -{ - BlockBackend *blk = child->opaque; - - if (blk->allow_aio_context_change) { - return true; - } - - /* 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; - } - - 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/blockjob.c b/blockjob.c index 5a783b75c6..2d86014fa5 100644 --- a/blockjob.c +++ b/blockjob.c @@ -170,39 +170,6 @@ static bool child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx, return true; } -static bool child_job_can_set_aio_ctx(BdrvChild *c, AioContext *ctx, - GSList **ignore, Error **errp) -{ - BlockJob *job = c->opaque; - GSList *l; - - 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; -} - -static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx, - GSList **ignore) -{ - BlockJob *job = c->opaque; - GSList *l; - - for (l = job->nodes; l; l = l->next) { - BdrvChild *sibling = l->data; - if (g_slist_find(*ignore, sibling)) { - continue; - } - *ignore = g_slist_prepend(*ignore, sibling); - bdrv_set_aio_context_ignore(sibling->bs, ctx, ignore); - } - - job_set_aio_context(&job->job, ctx); -} - static AioContext *child_job_get_parent_aio_context(BdrvChild *c) { BlockJob *job = c->opaque; @@ -216,8 +183,6 @@ 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/include/block/block-global-state.h b/include/block/block-global-state.h index e7372ec541..03d4ade7c2 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -220,18 +220,9 @@ 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); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 7ccbbdae05..c756b838e8 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -906,10 +906,6 @@ 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); From a41cfda12674a296579bc5459646ded9547b1220 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:51 -0400 Subject: [PATCH 31/58] block: rename bdrv_child_try_change_aio_context in bdrv_try_change_aio_context No functional changes intended. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Kevin Wolf Message-Id: <20221025084952.2139888-10-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 6 +++--- block/block-backend.c | 3 +-- include/block/block-global-state.h | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/block.c b/block.c index 353240eecd..cf97a35f82 100644 --- a/block.c +++ b/block.c @@ -7340,8 +7340,8 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, * 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_child_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, Error **errp) +int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp) { Transaction *tran; GHashTable *visited; @@ -7411,7 +7411,7 @@ int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, Error **errp) { GLOBAL_STATE_CODE(); - return bdrv_child_try_change_aio_context(bs, ctx, NULL, errp); + return bdrv_try_change_aio_context(bs, ctx, NULL, errp); } void bdrv_add_aio_context_notifier(BlockDriverState *bs, diff --git a/block/block-backend.c b/block/block-backend.c index a91c8d3916..705afef9b3 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -2151,8 +2151,7 @@ static int blk_do_set_aio_context(BlockBackend *blk, AioContext *new_context, * 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_child_try_change_aio_context(bs, new_context, blk->root, - errp); + ret = bdrv_try_change_aio_context(bs, new_context, blk->root, errp); if (ret < 0) { bdrv_unref(bs); return ret; diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 03d4ade7c2..8db3132e8f 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -226,8 +226,8 @@ 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_child_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, 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); From 142e6907120d12de1e7ac402e556597ebbab86e8 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Tue, 25 Oct 2022 04:49:52 -0400 Subject: [PATCH 32/58] block: remove bdrv_try_set_aio_context and replace it with bdrv_try_change_aio_context No functional change intended. Signed-off-by: Emanuele Giuseppe Esposito Reviewed-by: Kevin Wolf Message-Id: <20221025084952.2139888-11-eesposit@redhat.com> Signed-off-by: Kevin Wolf --- block.c | 14 ++++---------- block/export/export.c | 2 +- blockdev.c | 22 +++++++++++----------- docs/devel/multiple-iothreads.txt | 4 ++-- include/block/block-global-state.h | 2 -- job.c | 2 +- tests/unit/test-bdrv-drain.c | 6 +++--- tests/unit/test-block-iothread.c | 10 +++++----- 8 files changed, 27 insertions(+), 35 deletions(-) diff --git a/block.c b/block.c index cf97a35f82..5da15d0f4e 100644 --- a/block.c +++ b/block.c @@ -2904,7 +2904,7 @@ static void bdrv_attach_child_common_abort(void *opaque) 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(s->child) != s->old_parent_ctx) { @@ -2975,7 +2975,8 @@ static BdrvChild *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); + 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(); @@ -3065,7 +3066,7 @@ static void bdrv_detach_child(BdrvChild *child) * 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); } } @@ -7407,13 +7408,6 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, return 0; } -int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - Error **errp) -{ - GLOBAL_STATE_CODE(); - return bdrv_try_change_aio_context(bs, ctx, NULL, errp); -} - void bdrv_add_aio_context_notifier(BlockDriverState *bs, void (*attached_aio_context)(AioContext *new_context, void *opaque), void (*detach_aio_context)(void *opaque), void *opaque) diff --git a/block/export/export.c b/block/export/export.c index 4744862915..7cc0c25c1c 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/blockdev.c b/blockdev.c index a32bafc07a..9dd88d14d0 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; @@ -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/docs/devel/multiple-iothreads.txt b/docs/devel/multiple-iothreads.txt index aeb997bed5..343120f2ef 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/include/block/block-global-state.h b/include/block/block-global-state.h index 8db3132e8f..73795a0095 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -220,8 +220,6 @@ void coroutine_fn bdrv_co_lock(BlockDriverState *bs); */ void coroutine_fn bdrv_co_unlock(BlockDriverState *bs); -int bdrv_try_set_aio_context(BlockDriverState *bs, AioContext *ctx, - 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, diff --git a/job.c b/job.c index 78feae05fb..72d57f0934 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/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 0eecf14310..09dc4a4891 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); diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index def0709b2b..8ca5adec5e 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); From ebdebe47283dd7d20d88834f03f741758419c254 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 8 Sep 2022 21:28:15 +0800 Subject: [PATCH 33/58] block/nfs: Fix 32-bit Windows build libnfs.h declares nfs_fstat() as the following for win32: int nfs_fstat(struct nfs_context *nfs, struct nfsfh *nfsfh, struct __stat64 *st); The 'st' parameter should be of type 'struct __stat64'. The codes happen to build successfully for 64-bit Windows, but it does not build for 32-bit Windows. Fixes: 6542aa9c75bc ("block: add native support for NFS") Fixes: 18a8056e0bc7 ("block/nfs: cache allocated filesize for read-only files") Signed-off-by: Bin Meng Message-Id: <20220908132817.1831008-6-bmeng.cn@gmail.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/nfs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/block/nfs.c b/block/nfs.c index 596ebe98cb..ece22353ac 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)) { From 1a2152568ad5d9afafff96d47d2bf340ac3dc2ea Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:48 +0200 Subject: [PATCH 34/58] backup: remove incorrect coroutine_fn annotation The .set_speed callback is not called from coroutine. Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-2-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/backup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/backup.c b/block/backup.c index b2b649e305..6a9ad97a53 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); From 7c85803c496744c30d070a37be2faf2070d417d3 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:49 +0200 Subject: [PATCH 35/58] block: remove incorrect coroutine_fn annotation Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-3-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/io.c b/block/io.c index d30073036e..236b12da2a 100644 --- a/block/io.c +++ b/block/io.c @@ -2739,8 +2739,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; From 6894ee2bee20fd22055f8a7337ef465efab0c823 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:50 +0200 Subject: [PATCH 36/58] monitor: add missing coroutine_fn annotation hmp_block_resize and hmp_screendump are defined as a ".coroutine = true" command, so they must be coroutine_fn. Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-4-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/monitor/block-hmp-cmds.c | 2 +- include/block/block-hmp-cmds.h | 2 +- include/monitor/hmp.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 939a520d17..b6135e9bfe 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/include/block/block-hmp-cmds.h b/include/block/block-hmp-cmds.h index 50ce0247c3..ba0593c440 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/monitor/hmp.h b/include/monitor/hmp.h index a9cf064ee8..dfbc0c9a2f 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); From 42f6ad79e38b0a64d154c2131c5adb09fd181eb1 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:51 +0200 Subject: [PATCH 37/58] ssh: add missing coroutine_fn annotation ssh_write is only called from ssh_co_writev. Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-5-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/ssh.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/block/ssh.c b/block/ssh.c index a2dc646536..ceb4f4c5bc 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; From 16bb776f5b48e045f5eb5d105ee74135bfc781dd Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:52 +0200 Subject: [PATCH 38/58] block: add missing coroutine_fn annotation to prototypes The functions are marked coroutine_fn in the definition. Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-6-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-io.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/block/block-io.h b/include/block/block-io.h index 492f95fc05..770ddeb7c8 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, From d63f006a58b32f6df51ca12ac9d9e87b648eb617 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:53 +0200 Subject: [PATCH 39/58] coroutine-lock: add missing coroutine_fn annotation to prototypes The functions are marked coroutine_fn in the definition. Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-7-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/qemu/coroutine.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index aae33cce17..d848489b65 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -276,7 +276,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 @@ -285,7 +285,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 @@ -293,20 +293,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; From 512ef174fb531a00e6762b007e42095631d2b48d Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:54 +0200 Subject: [PATCH 40/58] coroutine-io: add missing coroutine_fn annotation to prototypes Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-8-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/qemu/coroutine.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index d848489b65..06d323143c 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -378,8 +378,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) \ @@ -388,7 +389,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) \ From c2d7680893a53c3d1443dd729fa60814dd1b2bad Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:55 +0200 Subject: [PATCH 41/58] block: add missing coroutine_fn annotation to BlockDriverState callbacks Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-9-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qcow2.h | 14 +++++++------- include/block/block_int-common.h | 12 +++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/block/qcow2.h b/block/qcow2.h index 3e7c5e80b6..ad6e7f65bd 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -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/include/block/block_int-common.h b/include/block/block_int-common.h index c756b838e8..afce4f8c0a 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -745,13 +745,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) From 014688a1b56bab8f3e15810971667ac15acbaa88 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:36:56 +0200 Subject: [PATCH 42/58] qcow2: add coroutine_fn annotation for indirect-called functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-10-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qcow2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/block/qcow2.c b/block/qcow2.c index b47016cf61..a8bb7135a5 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -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) { From f72b38b67e49e1460c6a2dffd4ee97c0155347eb Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Oct 2022 14:36:57 +0200 Subject: [PATCH 43/58] blkdebug: add missing coroutine_fn annotation for indirect-called functions Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-11-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/blkdebug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 5fcfc8ac6f..4265ca125e 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -669,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); From ea4b80146e4a000b7e9452390f71dca030a8a1b0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Oct 2022 14:36:58 +0200 Subject: [PATCH 44/58] qcow: manually add more coroutine_fn annotations get_cluster_offset() and decompress_cluster() are only called from the read and write paths. The validity of these was double-checked with Alberto Faria's static analyzer. Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-12-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qcow.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/block/qcow.c b/block/qcow.c index 72ed4c3321..46bbabd2e3 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) { @@ -349,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; @@ -583,7 +585,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; From a1b4ecfd6f4e7d8bfba3a3990150cb92577d92d4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Oct 2022 14:36:59 +0200 Subject: [PATCH 45/58] qcow2: manually add more coroutine_fn annotations The validity of these was double-checked with Alberto Faria's static analyzer. Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-13-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qcow2-bitmap.c | 4 ++-- block/qcow2-cluster.c | 21 ++++++++++++--------- block/qcow2-refcount.c | 8 ++++---- block/qcow2.h | 18 +++++++++--------- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 7197754843..bcad567c0c 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 0f293950a1..097f6484ed 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; @@ -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 1fbb07ca77..40e3870887 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; @@ -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 = diff --git a/block/qcow2.h b/block/qcow2.h index ad6e7f65bd..2285f18a73 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); From c9d43d0deb367d35bb911a5b2cf8233a426699cd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Oct 2022 14:37:00 +0200 Subject: [PATCH 46/58] vmdk: manually add more coroutine_fn annotations The validity of these was double-checked with Alberto Faria's static analyzer. Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-14-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/vmdk.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index d75a87cb85..e5004945b0 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1403,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; @@ -1484,8 +1484,8 @@ 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 */ @@ -1536,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; From a06678874bf6ed4f437b384749c704e21a4e61b9 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:01 +0200 Subject: [PATCH 47/58] commit: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-15-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/commit.c b/block/commit.c index e210e86bac..0029b31944 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; } From ce47ff20b9f3c05b2b371539bedc437c531c0beb Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:02 +0200 Subject: [PATCH 48/58] block: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-16-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 2 +- block/io.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/block.c b/block.c index 5da15d0f4e..5311b21f8e 100644 --- a/block.c +++ b/block.c @@ -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"); diff --git a/block/io.c b/block/io.c index 236b12da2a..5518a9d1e6 100644 --- a/block/io.c +++ b/block/io.c @@ -2729,8 +2729,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; From 882762165a3be58cf6752330878a5c2b181183ce Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:03 +0200 Subject: [PATCH 49/58] mirror: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-17-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/mirror.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index f9432af3df..18bf2d4212 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; } From 50688be02a679b7e385a051da6ae18c09de77098 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:04 +0200 Subject: [PATCH 50/58] parallels: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-18-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/parallels.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index 27c81cee40..892e9ec8d9 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; @@ -504,8 +504,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++; @@ -600,12 +600,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; } From 58684155e42fe16a21d8c1fcc7927c5b96706ff4 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:05 +0200 Subject: [PATCH 51/58] qcow: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-19-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qcow.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/block/qcow.c b/block/qcow.c index 46bbabd2e3..bb25125b22 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -381,9 +381,9 @@ static int coroutine_fn 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 coroutine_fn 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 coroutine_fn 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 coroutine_fn 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 coroutine_fn 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 coroutine_fn 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; } @@ -597,7 +598,7 @@ static int coroutine_fn decompress_cluster(BlockDriverState *bs, 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, @@ -891,14 +892,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; } @@ -907,8 +908,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; From 38505e2a147c4f9fade382ede4a40e287c7bd846 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:06 +0200 Subject: [PATCH 52/58] qcow2: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-20-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qcow2-cluster.c | 8 ++++---- block/qcow2-refcount.c | 10 +++++----- block/qcow2-snapshot.c | 6 +++--- block/qcow2.c | 24 ++++++++++++------------ 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 097f6484ed..40ed847f97 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -48,14 +48,14 @@ int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, #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; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 40e3870887..81264740f0 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -118,8 +118,8 @@ int coroutine_fn 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; } @@ -3657,9 +3657,9 @@ int coroutine_fn 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 d1d46facbf..62e8a0335d 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 a8bb7135a5..4d6666d3ff 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; @@ -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; From 3aba34adb19131ebfb996f700c518a0352a489ac Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:07 +0200 Subject: [PATCH 53/58] qed: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-21-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qed-table.c | 2 +- block/qed.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/block/qed-table.c b/block/qed-table.c index 1cc844b1a5..aa203f2627 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 4627169348..d7f2c6fc7c 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); @@ -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; } From 3f6530282226587e9253e4129e2297e955cc90b2 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:08 +0200 Subject: [PATCH 54/58] vdi: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-22-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/vdi.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/block/vdi.c b/block/vdi.c index a9bafb5a9e..c0c111c4b9 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -663,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: @@ -708,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) { @@ -725,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; @@ -844,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; @@ -865,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; @@ -874,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; From eb342749c0cc8002fc19a5589fb5c3d836118185 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:09 +0200 Subject: [PATCH 55/58] vhdx: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-23-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/vhdx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/block/vhdx.c b/block/vhdx.c index ce1e75f5ec..a41db46294 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -2011,15 +2011,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; From a5c4e5be7e09d46bd57693c139e74f9cbab042a3 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:10 +0200 Subject: [PATCH 56/58] vmdk: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-24-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/vmdk.c | 54 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index e5004945b0..26376352b9 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1440,16 +1440,16 @@ static int coroutine_fn 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; @@ -1460,18 +1460,18 @@ static int coroutine_fn 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; @@ -1490,23 +1490,23 @@ static int coroutine_fn vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data, 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) { @@ -1623,11 +1623,10 @@ static int coroutine_fn 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; } @@ -1898,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; } @@ -2143,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; } @@ -2585,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; @@ -2593,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; } From 247cf5c035ee598a58386230ab9db551ca3c1b1d Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Thu, 13 Oct 2022 14:37:11 +0200 Subject: [PATCH 57/58] monitor: switch to *_co_* functions Signed-off-by: Alberto Faria Signed-off-by: Paolo Bonzini Message-Id: <20221013123711.620631-25-pbonzini@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 9dd88d14d0..3f1dec6242 100644 --- a/blockdev.c +++ b/blockdev.c @@ -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); From be8da05b5ed8fb546731b9edb997f303f272bad8 Mon Sep 17 00:00:00 2001 From: Emanuele Giuseppe Esposito Date: Thu, 27 Oct 2022 03:27:26 -0400 Subject: [PATCH 58/58] block/block-backend: blk_set_enable_write_cache is IO_CODE blk_set_enable_write_cache() is defined as GLOBAL_STATE_CODE but can be invoked from iothreads when handling scsi requests. This triggers an assertion failure: 0x00007fd6c3515ce1 in raise () from /lib/x86_64-linux-gnu/libc.so.6 0x00007fd6c34ff537 in abort () from /lib/x86_64-linux-gnu/libc.so.6 0x00007fd6c34ff40f in ?? () from /lib/x86_64-linux-gnu/libc.so.6 0x00007fd6c350e662 in __assert_fail () from /lib/x86_64-linux-gnu/libc.so.6 0x000056149e2cea03 in blk_set_enable_write_cache (wce=true, blk=0x5614a01c27f0) at ../src/block/block-backend.c:1949 0x000056149e2d0a67 in blk_set_enable_write_cache (blk=0x5614a01c27f0, wce=) at ../src/block/block-backend.c:1951 0x000056149dfe9c59 in scsi_disk_apply_mode_select (p=0x7fd6b400c00e "\004", page=, s=) at ../src/hw/scsi/scsi-disk.c:1520 mode_select_pages (change=true, len=18, p=0x7fd6b400c00e "\004", r=0x7fd6b4001ff0) at ../src/hw/scsi/scsi-disk.c:1570 scsi_disk_emulate_mode_select (inbuf=, r=0x7fd6b4001ff0) at ../src/hw/scsi/scsi-disk.c:1640 scsi_disk_emulate_write_data (req=0x7fd6b4001ff0) at ../src/hw/scsi/scsi-disk.c:1934 0x000056149e18ff16 in virtio_scsi_handle_cmd_req_submit (req=, req=, s=0x5614a12f16b0) at ../src/hw/scsi/virtio-scsi.c:719 virtio_scsi_handle_cmd_vq (vq=0x7fd6bab92140, s=0x5614a12f16b0) at ../src/hw/scsi/virtio-scsi.c:761 virtio_scsi_handle_cmd (vq=, vdev=) at ../src/hw/scsi/virtio-scsi.c:775 virtio_scsi_handle_cmd (vdev=0x5614a12f16b0, vq=0x7fd6bab92140) at ../src/hw/scsi/virtio-scsi.c:765 0x000056149e1a8aa6 in virtio_queue_notify_vq (vq=0x7fd6bab92140) at ../src/hw/virtio/virtio.c:2365 0x000056149e3ccea5 in aio_dispatch_handler (ctx=ctx@entry=0x5614a01babe0, node=) at ../src/util/aio-posix.c:369 0x000056149e3cd868 in aio_dispatch_ready_handlers (ready_list=0x7fd6c09b2680, ctx=0x5614a01babe0) at ../src/util/aio-posix.c:399 aio_poll (ctx=0x5614a01babe0, blocking=blocking@entry=true) at ../src/util/aio-posix.c:713 0x000056149e2a7796 in iothread_run (opaque=opaque@entry=0x56149ffde500) at ../src/iothread.c:67 0x000056149e3d0859 in qemu_thread_start (args=0x7fd6c09b26f0) at ../src/util/qemu-thread-posix.c:504 0x00007fd6c36b9ea7 in start_thread () from /lib/x86_64-linux-gnu/libpthread.so.0 0x00007fd6c35d9aef in clone () from /lib/x86_64-linux-gnu/libc.so.6 Changing GLOBAL_STATE_CODE in IO_CODE is allowed, since GSC callers are allowed to call IO_CODE. Resolves: #1272 Signed-off-by: Emanuele Giuseppe Esposito Message-Id: <20221027072726.2681500-1-eesposit@redhat.com> Reviewed-by: Kevin Wolf Reviewed-by: Hanna Reitz Tested-by: Antoine Damhet Signed-off-by: Kevin Wolf --- block/block-backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/block-backend.c b/block/block-backend.c index 705afef9b3..ec17dc49a9 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -1944,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; }