From 2c3feaf4f146cbf3656243f67143b246eee4765b Mon Sep 17 00:00:00 2001 From: Samuel Verschelde Date: Fri, 28 Mar 2025 17:31:13 +0100 Subject: [PATCH 01/20] Import blktap-3.55.5-2.xs8.src.rpm Signed-off-by: Samuel Verschelde --- SOURCES/blktap-3.55.4.tar.gz | 3 - SOURCES/blktap-3.55.5.tar.gz | 3 + ...ient_only_after_completing_handshake.patch | 80 +++++++++++++++++++ SPECS/blktap.spec | 20 +++-- 4 files changed, 98 insertions(+), 8 deletions(-) delete mode 100644 SOURCES/blktap-3.55.4.tar.gz create mode 100644 SOURCES/blktap-3.55.5.tar.gz create mode 100644 SOURCES/ca-404370__enable_nbd_client_only_after_completing_handshake.patch diff --git a/SOURCES/blktap-3.55.4.tar.gz b/SOURCES/blktap-3.55.4.tar.gz deleted file mode 100644 index f1bf78a..0000000 --- a/SOURCES/blktap-3.55.4.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:222dda0280d4221dc9af75bf86257bf37bd50f957fb35d6cd7485c6cd176ebc5 -size 389168 diff --git a/SOURCES/blktap-3.55.5.tar.gz b/SOURCES/blktap-3.55.5.tar.gz new file mode 100644 index 0000000..0567889 --- /dev/null +++ b/SOURCES/blktap-3.55.5.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c73ee301b0b668b4c956571cdf16d0672d2396b6bf4e31f4ce704cfec6c1ff4c +size 389274 diff --git a/SOURCES/ca-404370__enable_nbd_client_only_after_completing_handshake.patch b/SOURCES/ca-404370__enable_nbd_client_only_after_completing_handshake.patch new file mode 100644 index 0000000..b6e3aa7 --- /dev/null +++ b/SOURCES/ca-404370__enable_nbd_client_only_after_completing_handshake.patch @@ -0,0 +1,80 @@ +CA-404370: enable NBD client only after completing handshake + +From: Mark Syms + +Previously the client was being enabled as part of +`tapdisk_nbdserver_newclient_fd_new_fixed` after calling +`tapdisk_nbdserver_new_protocol_handshake`. This erroneously assumed +that protocol handshake was complete by the time that function +returned, which is not the case as it simply adds the fd to an event +for the scheduler. Protocol handshake is only complete when we reach +the end of the `tapdisk_nbdserver_handshake_cb` callback and enabling +the client before this means we have two events registered for the +same fd. + +This was causing quicktest to fail after blktap2 was removed and NBD +is used for all control domain access to the I/O datapath. + +Signed-off-by: Mark Syms + +diff --git a/drivers/tapdisk-nbdserver.c b/drivers/tapdisk-nbdserver.c +index 2c332c2..63ab4d5 100644 +--- a/drivers/tapdisk-nbdserver.c ++++ b/drivers/tapdisk-nbdserver.c +@@ -1029,6 +1029,7 @@ void + tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) + { + uint32_t cflags = 0; ++ int tmp_fd; + + td_nbdserver_client_t *client = (td_nbdserver_client_t*)data; + td_nbdserver_t *server = client->server; +@@ -1036,7 +1037,8 @@ tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) + int rc = recv_fully_or_fail(server->handshake_fd, &cflags, sizeof(cflags)); + if(rc < 0) { + ERR("Could not receive client flags"); +- return; ++ close(server->handshake_fd); ++ goto out; + } + + cflags = be32toh (cflags); +@@ -1048,6 +1050,15 @@ tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) + close(server->handshake_fd); + } + ++ INFO("About to enable client on fd %d", client->client_fd); ++ if (tapdisk_nbdserver_enable_client(client) < 0) { ++ ERR("Error enabling client"); ++ tmp_fd = client->client_fd; ++ tapdisk_nbdserver_free_client(client); ++ close(tmp_fd); ++ } ++ ++out: + tapdisk_server_unregister_event(id); + } + +@@ -1157,13 +1168,6 @@ tapdisk_nbdserver_newclient_fd_new_fixed(td_nbdserver_t *server, int new_fd) + close(new_fd); + return; + } +- +- INFO("About to enable client on fd %d", client->client_fd); +- if (tapdisk_nbdserver_enable_client(client) < 0) { +- ERR("Error enabling client"); +- tapdisk_nbdserver_free_client(client); +- close(new_fd); +- } + } + + static void +@@ -1402,7 +1406,7 @@ tapdisk_nbdserver_newclient_unix(event_id_t id, char mode, void *data) + return; + } + +- INFO("server: got connection\n"); ++ INFO("server: got connection fd = %d\n", new_fd); + + tapdisk_nbdserver_newclient_fd_new_fixed(server, new_fd); + } diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index d599548..db8af90 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -1,14 +1,18 @@ -%global package_speccommit 266dddc02a877617a4c52a36d4dd86ef515c45ec -%global package_srccommit v3.55.4 +%global package_speccommit e1853b343f35f18ca9d9baee8ca22a8e3378176f +%global usver 3.55.5 +%global xsver 2 +%global xsrel %{xsver}%{?xscount}%{?xshash} +%global package_srccommit v3.55.5 Summary: blktap user space utilities Name: blktap -Version: 3.55.4 -Release: 1%{?xsrel}%{?dist} +Version: 3.55.5 +Release: %{?xsrel}%{?dist} License: BSD Group: System/Hypervisor URL: https://github.com/xapi-project/blktap -Source0: blktap-3.55.4.tar.gz +Source0: blktap-3.55.5.tar.gz +Patch0: ca-404370__enable_nbd_client_only_after_completing_handshake.patch BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot Obsoletes: xen-blktap < 4 @@ -170,6 +174,12 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Tue Feb 11 2025 Mark Syms - 3.55.5-2 +- CA-404370: enable NBD client only after completing handshake + +* Mon Jan 06 2025 Mark Syms - 3.55.5-1 +- Add an option to never resolve parent path when vhd-util query is called + * Tue Dec 03 2024 Mark Syms - 3.55.4-1 - CA-392151 lcache.c uses wrong buffer size definition - CA-403297: when storing read-through do not mirror From 8d0e97e5ba5216f445a7a498984ba1706ed7179f Mon Sep 17 00:00:00 2001 From: Anthoine Date: Mon, 7 Apr 2025 15:47:23 +0200 Subject: [PATCH 02/20] Remove unneeded patch from blktap.spec Patch 8450aed "Add an option to never resolve parent path when vhd-util query is called" is already include in version v3.55.5. Signed-off-by: Anthoine Bourgeois --- ...-never-resolve-parent-path-when-vhd-.patch | 144 ------------------ ...use-backup-footer-when-vhd-util-que.patch} | 4 +- SPECS/blktap.spec | 8 +- 3 files changed, 5 insertions(+), 151 deletions(-) delete mode 100644 SOURCES/0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch rename SOURCES/{0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch => 0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch} (94%) diff --git a/SOURCES/0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch b/SOURCES/0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch deleted file mode 100644 index 4901067..0000000 --- a/SOURCES/0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch +++ /dev/null @@ -1,144 +0,0 @@ -From e371dd87ad668e156309992bc83b96ad34fb4d38 Mon Sep 17 00:00:00 2001 -From: Ronan Abhamon -Date: Thu, 16 Mar 2023 15:55:07 +0100 -Subject: [PATCH 1/2] Add an option to never resolve parent path when vhd-util - query is called - -Signed-off-by: Ronan Abhamon ---- - include/libvhd.h | 1 + - vhd/lib/libvhd.c | 21 +++++++++++++++++++-- - vhd/lib/vhd-util-query.c | 36 +++++++++++++++++++++--------------- - 3 files changed, 41 insertions(+), 17 deletions(-) - -diff --git a/include/libvhd.h b/include/libvhd.h -index 7365918..3932547 100644 ---- a/include/libvhd.h -+++ b/include/libvhd.h -@@ -341,6 +341,7 @@ int vhd_initialize_header_parent_name(vhd_context_t *, const char *); - int vhd_write_parent_locators(vhd_context_t *, const char *); - int vhd_parent_locator_count(vhd_context_t *); - int vhd_parent_locator_get(vhd_context_t *, char **); -+int vhd_parent_locator_unresolved_get(vhd_context_t *, char **); - int vhd_custom_parent_set(vhd_context_t *vhd, const char *parent); - - int vhd_parent_locator_read(vhd_context_t *, vhd_parent_locator_t *, char **); -diff --git a/vhd/lib/libvhd.c b/vhd/lib/libvhd.c -index 73be3ee..830e95a 100644 ---- a/vhd/lib/libvhd.c -+++ b/vhd/lib/libvhd.c -@@ -1782,8 +1782,8 @@ out: - return err; - } - --int --vhd_parent_locator_get(vhd_context_t *ctx, char **parent) -+static int -+vhd_parent_locator_get_impl(vhd_context_t *ctx, char **parent, bool resolve_parent) - { - int i, n, err; - char *name, *location; -@@ -1807,6 +1807,11 @@ vhd_parent_locator_get(vhd_context_t *ctx, char **parent) - if (_err) - continue; - -+ if (!resolve_parent) { -+ *parent = name; -+ return 0; -+ } -+ - err = vhd_find_parent(ctx, name, &location); - if (err) - VHDLOG("%s: couldn't find parent %s (%d)\n", -@@ -1822,6 +1827,18 @@ vhd_parent_locator_get(vhd_context_t *ctx, char **parent) - return err; - } - -+int -+vhd_parent_locator_get(vhd_context_t *ctx, char **parent) -+{ -+ return vhd_parent_locator_get_impl(ctx, parent, true); -+} -+ -+int -+vhd_parent_locator_unresolved_get(vhd_context_t *ctx, char **parent) -+{ -+ return vhd_parent_locator_get_impl(ctx, parent, false); -+} -+ - /** - * Overrides the parent with the supplied one. - * -diff --git a/vhd/lib/vhd-util-query.c b/vhd/lib/vhd-util-query.c -index 9aa131e..0cd0416 100644 ---- a/vhd/lib/vhd-util-query.c -+++ b/vhd/lib/vhd-util-query.c -@@ -46,25 +46,26 @@ vhd_util_query(int argc, char **argv) - char *name; - vhd_context_t vhd; - off64_t currsize; -- int ret, err, c, size, physize, parent, fields, depth, fastresize, marker, allocated; -- -- name = NULL; -- size = 0; -- physize = 0; -- parent = 0; -- fields = 0; -- depth = 0; -- fastresize = 0; -- marker = 0; -- allocated = 0; -- -+ int ret, err, c, size, physize, parent, fields, depth, fastresize, marker, allocated, resolve_parent; -+ -+ name = NULL; -+ size = 0; -+ physize = 0; -+ parent = 0; -+ fields = 0; -+ depth = 0; -+ fastresize = 0; -+ marker = 0; -+ allocated = 0; -+ resolve_parent = 1; -+ - if (!argc || !argv) { - err = -EINVAL; - goto usage; - } - - optind = 0; -- while ((c = getopt(argc, argv, "n:vspfdSmah")) != -1) { -+ while ((c = getopt(argc, argv, "n:vspfdSmauh")) != -1) { - switch (c) { - case 'n': - name = optarg; -@@ -93,6 +94,9 @@ vhd_util_query(int argc, char **argv) - case 'a': - allocated = 1; - break; -+ case 'u': -+ resolve_parent = 0; -+ break; - case 'h': - err = 0; - goto usage; -@@ -132,7 +136,7 @@ vhd_util_query(int argc, char **argv) - else { - char *pname; - -- ret = vhd_parent_locator_get(&vhd, &pname); -+ ret = resolve_parent ? vhd_parent_locator_get(&vhd, &pname) : vhd_parent_locator_unresolved_get(&vhd, &pname); - if (ret) - printf("query failed\n"); - else { -@@ -212,6 +216,8 @@ usage: - "[-s print physical utilization (bytes)] [-p print parent] " - "[-f print fields] [-m print marker] [-d print chain depth] " - "[-S print max virtual size (MB) for fast resize] " -- "[-a print allocated block count] [-h help]\n"); -+ "[-a print allocated block count] " -+ "[-u don't resolve parent path] " -+ "[-h help]\n"); - return err; - } diff --git a/SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch b/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch similarity index 94% rename from SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch rename to SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch index 3733839..63c87e4 100644 --- a/SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +++ b/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch @@ -1,7 +1,7 @@ -From 7c168ec7818aa74bf81ae27ddd808c227b1d2261 Mon Sep 17 00:00:00 2001 +From e3482730d74c78c8827becbdc5b4eb14593a1582 Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Wed, 18 Dec 2024 16:55:26 +0100 -Subject: [PATCH 2/2] Add an option to use backup footer when vhd-util query is +Subject: [PATCH] Add an option to use backup footer when vhd-util query is called This option already exists on td-util but it was not implemented diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index c59141d..958a385 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -34,11 +34,8 @@ Conflicts: sm < 3.0.1 Provides: blktap(nbd) = 2.0 # XCP-ng patches -# FIXME: drop unneeded patch -# Required by XOSTOR. Upstream PR: https://github.com/xapi-project/blktap/pull/378 -Patch1001: 0001-Add-an-option-to-never-resolve-parent-path-when-vhd-.patch # Required by sm (qcow2). Upstream PR: https://github.com/xapi-project/blktap/pull/417 -Patch1002: 0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +Patch1001: 0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch %description Blktap creates kernel block devices which realize I/O requests to @@ -183,7 +180,8 @@ without requiring other libraries %changelog * Fri Mar 28 2025 Samuel Verschelde - 3.55.5-2.1 - Sync with 3.55.5-2 -- TODO : mention dropped patch and remaining patch renamed +- Drop patch "Add an option to never resolve parent path when vhd-util query is +called" already included in upstream v3.55.5 - *** Upstream changelog *** * Tue Feb 11 2025 Mark Syms - 3.55.5-2 - CA-404370: enable NBD client only after completing handshake From 309768d337ba38a833320b81f5c26c99b61199e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Lehmann?= Date: Tue, 29 Apr 2025 15:48:12 +0200 Subject: [PATCH 03/20] Set team owner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: GaĆ«tan Lehmann --- .github/CODEOWNERS | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 75f1707..edd46bb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ -* @xcp-ng-rpms/storage -* @xcp-ng-rpms/os-platform-release +* @xcp-ng-rpms/storage @xcp-ng-rpms/os-platform-release From d39b3160bfe019196bcc015de3e6798807e1381f Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Tue, 29 Oct 2024 13:37:36 +0100 Subject: [PATCH 04/20] Drop useless autogen build-dep Signed-off-by: Yann Dirson --- SPECS/blktap.spec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 958a385..54fd4d2 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.1%{?dist} +Release: %{?xsrel}.2%{?dist} License: BSD Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -16,7 +16,7 @@ Patch0: ca-404370__enable_nbd_client_only_after_completing_handshake.patch BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot Obsoletes: xen-blktap < 4 -BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autogen, autoconf, automake, libtool, libuuid-devel +BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autoconf, automake, libtool, libuuid-devel BuildRequires: kernel-headers, xen-libs-devel, zlib-devel, libcmocka-devel, lcov, git BuildRequires: xs-openssl-devel >= 1.1.1 BuildRequires: devtoolset-11-gcc @@ -178,6 +178,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Fri Jul 4 2025 Yann Dirson - 3.55.5-2.2 +- Drop useless autogen build-dep + * Fri Mar 28 2025 Samuel Verschelde - 3.55.5-2.1 - Sync with 3.55.5-2 - Drop patch "Add an option to never resolve parent path when vhd-util query is From 286e5e174453ca49758b9bd6e5c6b7b8ecc83b45 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Thu, 10 Jul 2025 14:59:42 +0200 Subject: [PATCH 05/20] Import blktap-3.55.5-3.xs8.src.rpm Signed-off-by: Yann Dirson --- SPECS/blktap.spec | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index db8af90..cbd381e 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -1,6 +1,6 @@ -%global package_speccommit e1853b343f35f18ca9d9baee8ca22a8e3378176f +%global package_speccommit 13524843cce557fb6a3412a7f11bcffbfd9595a2 %global usver 3.55.5 -%global xsver 2 +%global xsver 3 %global xsrel %{xsver}%{?xscount}%{?xshash} %global package_srccommit v3.55.5 @@ -18,7 +18,7 @@ BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot Obsoletes: xen-blktap < 4 BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autogen, autoconf, automake, libtool, libuuid-devel BuildRequires: kernel-headers, xen-libs-devel, zlib-devel, libcmocka-devel, lcov, git -BuildRequires: xs-openssl-devel >= 1.1.1 +BuildRequires: openssl-devel >= 3.0.9 BuildRequires: devtoolset-11-gcc BuildRequires: devtoolset-11-binutils BuildRequires: devtoolset-11-liblsan-devel @@ -66,9 +66,9 @@ source /opt/rh/devtoolset-11/enable echo -n %{version} > VERSION sh autogen.sh # The following can be used for leak tracing -#%%configure LDFLAGS="$LDFLAGS -Wl,-rpath=/lib64/citrix -lrt -static-liblsan" CFLAGS="$CFLAGS -Wno-stringop-truncation -fsanitize=leak -ggdb -fno-omit-frame-pointer" -#%%configure LDFLAGS="$LDFLAGS -Wl,-rpath=/lib64/citrix" CFLAGS="$CFLAGS -Wno-stringop-truncation -Wno-error=analyzer-malloc-leak -Wno-error=analyzer-use-after-free -Wno-error=analyzer-double-free -Wno-error=analyzer-null-dereference -fanalyzer" -%configure LDFLAGS="$LDFLAGS -Wl,-rpath=/lib64/citrix" CFLAGS="$CFLAGS -Wno-stringop-truncation" +#%%configure LDFLAGS="$LDFLAGS -lrt -static-liblsan" CFLAGS="$CFLAGS -Wno-stringop-truncation -fsanitize=leak -ggdb -fno-omit-frame-pointer" +#%%configure CFLAGS="$CFLAGS -Wno-stringop-truncation -Wno-error=analyzer-malloc-leak -Wno-error=analyzer-use-after-free -Wno-error=analyzer-double-free -Wno-error=analyzer-null-dereference -fanalyzer" +%configure CFLAGS="$CFLAGS -Wno-stringop-truncation" %{?_cov_wrap} make %{?coverage:GCOV=true} %check @@ -174,6 +174,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Fri Feb 21 2025 Deli Zhang - 3.55.5-3 +- CP-50273: Move CCM dependency to OpenSSL 3 + * Tue Feb 11 2025 Mark Syms - 3.55.5-2 - CA-404370: enable NBD client only after completing handshake From d59c0b914a4aa38fac4710b92cc9b17a249c1e70 Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Thu, 31 Jul 2025 11:18:58 +0200 Subject: [PATCH 06/20] Release 3.55.5-2.3 Fix a bad integer conversion that interrupts valid coalesce calls on large VDIs: - Add 0002-CP-308382-fix-sign-conversion-in-coalesce.patch - Add 0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch Signed-off-by: Ronan Abhamon --- ...-use-backup-footer-when-vhd-util-que.patch | 17 +++-- ...8382-fix-sign-conversion-in-coalesce.patch | 72 +++++++++++++++++++ ...size-conversion-in-vhd-util-coalesce.patch | 65 +++++++++++++++++ SPECS/blktap.spec | 11 ++- 4 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 SOURCES/0002-CP-308382-fix-sign-conversion-in-coalesce.patch create mode 100644 SOURCES/0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch diff --git a/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch b/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch index 63c87e4..fb3c084 100644 --- a/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +++ b/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch @@ -1,4 +1,4 @@ -From e3482730d74c78c8827becbdc5b4eb14593a1582 Mon Sep 17 00:00:00 2001 +From c9237c52ffdf876c2d2e1a12b8c4a0cff68eafee Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Wed, 18 Dec 2024 16:55:26 +0100 Subject: [PATCH] Add an option to use backup footer when vhd-util query is @@ -9,11 +9,11 @@ on vhd-util. Signed-off-by: Ronan Abhamon --- - vhd/lib/vhd-util-query.c | 36 ++++++++++++++++++++---------------- - 1 file changed, 20 insertions(+), 16 deletions(-) + vhd/lib/vhd-util-query.c | 38 +++++++++++++++++++++----------------- + 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/vhd/lib/vhd-util-query.c b/vhd/lib/vhd-util-query.c -index 0cd0416..13bd563 100644 +index 0cd0416..3aca668 100644 --- a/vhd/lib/vhd-util-query.c +++ b/vhd/lib/vhd-util-query.c @@ -43,29 +43,29 @@ @@ -80,6 +80,15 @@ index 0cd0416..13bd563 100644 if (err) { printf("error opening %s: %d\n", name, err); return err; +@@ -207,7 +210,7 @@ vhd_util_query(int argc, char **argv) + max_size = vhd.header.max_bat_size << (VHD_BLOCK_SHIFT - 20); + printf("%"PRIu64"\n", max_size); + } +- ++ + vhd_close(&vhd); + return err; + @@ -218,6 +221,7 @@ usage: "[-S print max virtual size (MB) for fast resize] " "[-a print allocated block count] " diff --git a/SOURCES/0002-CP-308382-fix-sign-conversion-in-coalesce.patch b/SOURCES/0002-CP-308382-fix-sign-conversion-in-coalesce.patch new file mode 100644 index 0000000..935d9bb --- /dev/null +++ b/SOURCES/0002-CP-308382-fix-sign-conversion-in-coalesce.patch @@ -0,0 +1,72 @@ +From 44a1f8e88edf6fc09b2ba44f0902638c2f3ee7f5 Mon Sep 17 00:00:00 2001 +From: Mark Syms +Date: Tue, 17 Jun 2025 17:03:08 +0100 +Subject: [PATCH] CP-308382: fix sign conversion in coalesce + +A number of places in vhd-util-coalesce.c produce warnings when +compiled with -Wsign-conversion and might be contributing to the +coalesced_size being reported as a negative error. Ensure there are no +sign-conversion warnings in this code file. + +Signed-off-by: Mark Syms +--- + vhd/lib/vhd-util-coalesce.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/vhd/lib/vhd-util-coalesce.c b/vhd/lib/vhd-util-coalesce.c +index 41132c2..d3a85d6 100644 +--- a/vhd/lib/vhd-util-coalesce.c ++++ b/vhd/lib/vhd-util-coalesce.c +@@ -46,10 +46,10 @@ static int + __raw_io_write(int fd, char* buf, uint64_t sec, uint32_t secs) + { + off64_t off; +- size_t ret; ++ ssize_t ret; + + errno = 0; +- off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET); ++ off = lseek64(fd, (off64_t)vhd_sectors_to_bytes(sec), SEEK_SET); + if (off == (off64_t)-1) { + printf("raw parent: seek(0x%08"PRIx64") failed: %d\n", + vhd_sectors_to_bytes(sec), -errno); +@@ -78,7 +78,8 @@ static int64_t + vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent, + int parent_fd, uint64_t block) + { +- int i, err; ++ int err; ++ uint32_t i; + int64_t coalesced_size = 0; + char *buf; + char *map; +@@ -141,8 +142,8 @@ vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent, + if (err) + goto done; + +- coalesced_size += secs; +- i += secs; ++ coalesced_size += (int64_t)secs; ++ i += (uint32_t)secs; + } + + err = 0; +@@ -169,7 +170,8 @@ static int64_t + vhd_util_coalesce_onto(vhd_context_t *from, + vhd_context_t *to, int to_fd, int progress) + { +- int i, err; ++ int i; ++ int64_t err; + int64_t coalesced_size = 0; + + err = vhd_get_bat(from); +@@ -188,7 +190,7 @@ vhd_util_coalesce_onto(vhd_context_t *from, + ((float)i / (float)from->bat.entries) * 100.00); + fflush(stdout); + } +- err = vhd_util_coalesce_block(from, to, to_fd, i); ++ err = vhd_util_coalesce_block(from, to, to_fd, (uint64_t)i); + if (err < 0) + goto out; + diff --git a/SOURCES/0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch b/SOURCES/0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch new file mode 100644 index 0000000..d387ee2 --- /dev/null +++ b/SOURCES/0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch @@ -0,0 +1,65 @@ +From c5cd0420af4382551648be30f710cd75ba8ecc6f Mon Sep 17 00:00:00 2001 +From: Ronan Abhamon +Date: Wed, 30 Jul 2025 15:14:24 +0200 +Subject: [PATCH] Fix coalesced size conversion in vhd-util-coalesce + +If coalesced_size exceeds the max size of an int32, +it will be incorrectly converted from int64 to int32 +and result in a negative value during assignment of +the return from vhd_util_coalesce_onto. + +Signed-off-by: Ronan Abhamon +--- + vhd/lib/vhd-util-coalesce.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/vhd/lib/vhd-util-coalesce.c b/vhd/lib/vhd-util-coalesce.c +index d3a85d6..8bcfb79 100644 +--- a/vhd/lib/vhd-util-coalesce.c ++++ b/vhd/lib/vhd-util-coalesce.c +@@ -221,7 +221,8 @@ static int64_t + vhd_util_coalesce_parent(const char *name, int sparse, int progress) + { + char *pname; +- int err, parent_fd; ++ int64_t err; ++ int parent_fd; + vhd_context_t vhd, parent; + + parent_fd = -1; +@@ -229,7 +230,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY); + if (err) { +- printf("error opening %s: %d\n", name, err); ++ printf("error opening %s: %" PRId64 "\n", name, err); + return err; + } + +@@ -241,7 +242,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + + err = vhd_parent_locator_get(&vhd, &pname); + if (err) { +- printf("error finding %s parent: %d\n", name, err); ++ printf("error finding %s parent: %" PRId64 "\n", name, err); + vhd_close(&vhd); + return err; + } +@@ -250,7 +251,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + parent_fd = open_optional_odirect(pname, O_RDWR | O_DIRECT | O_LARGEFILE, 0644); + if (parent_fd == -1) { + err = -errno; +- printf("failed to open parent %s: %d\n", pname, err); ++ printf("failed to open parent %s: %" PRId64 "\n", pname, err); + free(pname); + vhd_close(&vhd); + return err; +@@ -260,7 +261,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + if (sparse) printf("opening for sparse writes\n"); + err = vhd_open(&parent, pname, VHD_OPEN_RDWR | flags); + if (err) { +- printf("error opening %s: %d\n", pname, err); ++ printf("error opening %s: %" PRId64 "\n", pname, err); + free(pname); + vhd_close(&vhd); + return err; diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 54fd4d2..709075d 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.2%{?dist} +Release: %{?xsrel}.3%{?dist} License: BSD Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -36,6 +36,8 @@ Provides: blktap(nbd) = 2.0 # XCP-ng patches # Required by sm (qcow2). Upstream PR: https://github.com/xapi-project/blktap/pull/417 Patch1001: 0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +Patch1002: 0002-CP-308382-fix-sign-conversion-in-coalesce.patch +Patch1003: 0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch %description Blktap creates kernel block devices which realize I/O requests to @@ -178,7 +180,12 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog -* Fri Jul 4 2025 Yann Dirson - 3.55.5-2.2 +* Thu Jul 31 2025 Ronan Abhamon - 3.55.5-2.3 +- Fix a bad integer conversion that interrupts valid coalesce calls on large VDIs +- Add 0002-CP-308382-fix-sign-conversion-in-coalesce.patch +- Add 0003-Fix-coalesced-size-conversion-in-vhd-util-coalesce.patch + +* Fri Jul 04 2025 Yann Dirson - 3.55.5-2.2 - Drop useless autogen build-dep * Fri Mar 28 2025 Samuel Verschelde - 3.55.5-2.1 From 5e0695cf1998ce317dde29d51b4608dc10b082a0 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Fri, 11 Jul 2025 16:27:40 +0200 Subject: [PATCH 07/20] Import mk/blktap.spec.in from v3.55.5 Signed-off-by: Yann Dirson --- SPECS/blktap.spec | 161 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 SPECS/blktap.spec diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec new file mode 100644 index 0000000..2da6fe2 --- /dev/null +++ b/SPECS/blktap.spec @@ -0,0 +1,161 @@ +Summary: blktap user space utilities +Name: @PACKAGE_NAME@ +Version: @PACKAGE_VERSION@ +Release: @BLKTAP_RELEASE@ +License: BSD +Group: System/Hypervisor +Source0: %{name}-%{version}.tar.bz2 +URL: https://github.com/xapi-project/blktap + +BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot +Obsoletes: xen-blktap +BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autogen, autoconf, automake, libtool, libuuid-devel +BuildRequires: xen-devel, kernel-headers, xen-dom0-libs-devel, zlib-devel, xen-libs-devel, libcmocka-devel, lcov, git +BuildRequires: openssl-devel >= 1.1.1 +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd + +%description +Blktap creates kernel block devices which realize I/O requests to +processes implementing virtual hard disk images entirely in user +space. + +Typical disk images may be implemented as files, in memory, or +stored on other hosts across the network. The image drivers included +with tapdisk can map disk I/O to sparse file images accessed through +Linux DIO/AIO and VHD images with snapshot functionality. + +This packages includes the control utilities needed to create +destroy and manipulate devices ('tap-ctl'), the 'tapdisk' driver +program to perform tap devices I/O, and a number of image drivers. + +%package devel +Summary: BlkTap Development Headers and Libraries +Requires: blktap = %{version} +Group: Development/Libraries +Obsoletes: xen-blktap + +%description devel +Blktap and VHD development files. + +%prep +%autosetup -p1 -S git + +%build +sh autogen.sh +%configure +%{?cov_wrap} make %{?coverage:GCOV=true} + +%check +make check || (find mockatests -name \*.log -print -exec cat {} \; && false) +./collect-test-results.sh %{buildroot}/testresults + +%install +rm -rf %{buildroot} +make install DESTDIR=%{buildroot} +mkdir -p %{buildroot}%{_localstatedir}/log/blktap +%if 0%{?coverage:1} +cd ../ && find -name "*.gcno" | grep -v '.libs/' | xargs -d "\n" tar -cvjSf %{buildroot}/%{name}-%{version}.gcno.tar.bz2 +%endif + +%triggerin -- mdadm +echo 'KERNEL=="td[a-z]*", GOTO="md_end"' > /etc/udev/rules.d/65-md-incremental.rules +cat /usr/lib/udev/rules.d/65-md-incremental.rules >> /etc/udev/rules.d/65-md-incremental.rules + +%files +%defattr(-,root,root,-) +%docdir /usr/share/doc/%{name} +/usr/share/doc/%{name} +%{_libdir}/*.so +%{_libdir}/*.so.* +%{_bindir}/vhd-util +%{_bindir}/vhd-index +%{_bindir}/tapback +%{_bindir}/cpumond +%{_sbindir}/cbt-util +%{_sbindir}/lvm-util +%{_sbindir}/tap-ctl +%{_sbindir}/td-util +%{_sbindir}/td-rated +%{_libexecdir}/tapdisk +%{_sysconfdir}/logrotate.d/blktap +%{_sysconfdir}/cron.daily/prune_tapdisk_logs +%{_sysconfdir}/xensource/bugtool/tapdisk-logs.xml +%{_sysconfdir}/xensource/bugtool/tapdisk-logs/description.xml +%{_localstatedir}/log/blktap +%{_unitdir}/tapback.service +%{_unitdir}/cpumond.service + +%files devel +%defattr(-,root,root,-) +%doc +%{_libdir}/*.a +%{_libdir}/*.la +%{_includedir}/vhd/* +%{_includedir}/blktap/* +%if 0%{?coverage:1} +/%{name}-%{version}.gcno.tar.bz2 +%endif + +%post +%systemd_post tapback.service +%systemd_post cpumond.service + +%preun +%systemd_preun tapback.service +%systemd_preun cpumond.service + +%postun +%systemd_postun tapback.service +%systemd_postun cpumond.service +if [ $1 -eq 0 ]; then + rm -f %{_sysconfdir}/udev/rules.d/65-md-incremental.rules +fi + +%changelog +* Fri May 25 2018 marksy - 3.7.0-1.0 +- CA-285194: ensure that tapdisk logs if it exits and also on opening its control + +* Fri May 25 2018 marksy - 3.6.0-1.0 +- Release 3.6.0 + +* Tue Apr 10 2018 marksy - 3.5.0-1.17 +- CA-277128: remove redundant, broken RRD code from tapdisk + +* Tue Mar 27 2018 marksy - 3.5.0-1.16 +- Gather the gcov coverage files during build + +* Fri Feb 16 2018 marksy - 3.5.0-1.15 +- CP-26852: Support building with upstream Linux + +* Tue Jan 30 2018 marksy - 3.5.0-1.14 +- Convert patch to use tabs for merge to github +- Reorder patchqueue with patches commited to github +- CA-220042: Add missing half of pull request 191 to patchqueue +- Update patchqueue patch to match the github pull request + +* Wed Dec 06 2017 marksy - 3.5.0-1.13 +- CP-20541 Enable conditional coverage build + +* Thu Oct 12 2017 marksy - 3.5.0-1.12 +- Patch cleanup + +* Wed Oct 11 2017 marksy - 3.5.0-1.11 +- CA-268288: Send logpath as an additional write + +* Wed Sep 27 2017 marksy - 3.5.0-xs.1+1.10 +- CP-23545: Extend tap-ctl create to consider CBT parameters +- CP-23920: [Unit test] Increase test coverage for cbt-util coalesce +- CP-24547: [Unit test] Increase test coverage for cbt-util set + + +%package testresults +Group: System/Hypervisor +Summary: test results for blktap package + +%description testresults +The package contains the build time test results for the blktap package + +%files testresults +/testresults From e23bbbd84a65c22b45c773cf57c596678c77edd7 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Mon, 8 Sep 2025 10:39:02 +0200 Subject: [PATCH 08/20] Import blktap-3.55.5-6.xs8.src.rpm Signed-off-by: Anthoine Bourgeois --- .../CP-308382_fix_sign_conversion_in_coalesce | 75 +++++++++++++++++++ ...uish_logging_for_long_nbd_operations.patch | 28 +++++++ ...size_conversion_in_vhd-util-coalesce.patch | 61 +++++++++++++++ SPECS/blktap.spec | 14 +++- 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 SOURCES/CP-308382_fix_sign_conversion_in_coalesce create mode 100644 SOURCES/ca-408175__distinguish_logging_for_long_nbd_operations.patch create mode 100644 SOURCES/fix_coalesced_size_conversion_in_vhd-util-coalesce.patch diff --git a/SOURCES/CP-308382_fix_sign_conversion_in_coalesce b/SOURCES/CP-308382_fix_sign_conversion_in_coalesce new file mode 100644 index 0000000..9372c83 --- /dev/null +++ b/SOURCES/CP-308382_fix_sign_conversion_in_coalesce @@ -0,0 +1,75 @@ +From f4ec626158a4803d4042f1161d39ffe1ca8a630a Mon Sep 17 00:00:00 2001 +From: Mark Syms +Date: Tue, 17 Jun 2025 17:03:08 +0100 +Subject: [PATCH] CP-308382: fix sign conversion in coalesce + +A number of places in vhd-util-coalesce.c produce warnings when +compiled with -Wsign-conversion and might be contributing to the +coalesced_size being reported as a negative error. Ensure there are no +sign-conversion warnings in this code file. + +Signed-off-by: Mark Syms +--- + vhd/lib/vhd-util-coalesce.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/vhd/lib/vhd-util-coalesce.c b/vhd/lib/vhd-util-coalesce.c +index 41132c2..d3a85d6 100644 +--- a/vhd/lib/vhd-util-coalesce.c ++++ b/vhd/lib/vhd-util-coalesce.c +@@ -46,10 +46,10 @@ static int + __raw_io_write(int fd, char* buf, uint64_t sec, uint32_t secs) + { + off64_t off; +- size_t ret; ++ ssize_t ret; + + errno = 0; +- off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET); ++ off = lseek64(fd, (off64_t)vhd_sectors_to_bytes(sec), SEEK_SET); + if (off == (off64_t)-1) { + printf("raw parent: seek(0x%08"PRIx64") failed: %d\n", + vhd_sectors_to_bytes(sec), -errno); +@@ -78,7 +78,8 @@ static int64_t + vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent, + int parent_fd, uint64_t block) + { +- int i, err; ++ int err; ++ uint32_t i; + int64_t coalesced_size = 0; + char *buf; + char *map; +@@ -141,8 +142,8 @@ vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent, + if (err) + goto done; + +- coalesced_size += secs; +- i += secs; ++ coalesced_size += (int64_t)secs; ++ i += (uint32_t)secs; + } + + err = 0; +@@ -169,7 +170,8 @@ static int64_t + vhd_util_coalesce_onto(vhd_context_t *from, + vhd_context_t *to, int to_fd, int progress) + { +- int i, err; ++ int i; ++ int64_t err; + int64_t coalesced_size = 0; + + err = vhd_get_bat(from); +@@ -188,7 +190,7 @@ vhd_util_coalesce_onto(vhd_context_t *from, + ((float)i / (float)from->bat.entries) * 100.00); + fflush(stdout); + } +- err = vhd_util_coalesce_block(from, to, to_fd, i); ++ err = vhd_util_coalesce_block(from, to, to_fd, (uint64_t)i); + if (err < 0) + goto out; + +-- +2.49.0 + diff --git a/SOURCES/ca-408175__distinguish_logging_for_long_nbd_operations.patch b/SOURCES/ca-408175__distinguish_logging_for_long_nbd_operations.patch new file mode 100644 index 0000000..7805faf --- /dev/null +++ b/SOURCES/ca-408175__distinguish_logging_for_long_nbd_operations.patch @@ -0,0 +1,28 @@ +CA-408175: distinguish logging for long NBD operations + +From: Mark Syms + +Signed-off-by: Mark Syms + +diff --git a/drivers/tapdisk-nbdserver.c b/drivers/tapdisk-nbdserver.c +index 63ab4d5..95ff964 100644 +--- a/drivers/tapdisk-nbdserver.c ++++ b/drivers/tapdisk-nbdserver.c +@@ -915,7 +915,7 @@ __tapdisk_nbdserver_structured_read_cb( + interval = timeval_to_us(&now) - timeval_to_us(&vreq->ts); + + if (interval > 20 * 1000 * 1000) { +- INFO("request took %llu microseconds to complete", interval); ++ INFO("Structured read took %llu microseconds to complete", interval); + } + + if (client->client_fd < 0) { +@@ -980,7 +980,7 @@ __tapdisk_nbdserver_request_cb(td_vbd_request_t *vreq, int error, + interval = timeval_to_us(&now) - timeval_to_us(&vreq->ts); + + if (interval > 20 * 1000 * 1000) { +- INFO("request took %llu microseconds to complete", interval); ++ INFO("Op %d request took %llu microseconds to complete", vreq->op, interval); + } + + if (client->client_fd < 0) { diff --git a/SOURCES/fix_coalesced_size_conversion_in_vhd-util-coalesce.patch b/SOURCES/fix_coalesced_size_conversion_in_vhd-util-coalesce.patch new file mode 100644 index 0000000..028fa05 --- /dev/null +++ b/SOURCES/fix_coalesced_size_conversion_in_vhd-util-coalesce.patch @@ -0,0 +1,61 @@ +Fix coalesced size conversion in vhd-util-coalesce + +From: Ronan Abhamon + +If coalesced_size exceeds the max size of an int32, +it will be incorrectly converted from int64 to int32 +and result in a negative value during assignment of +the return from vhd_util_coalesce_onto. + +Signed-off-by: Ronan Abhamon + +diff --git a/vhd/lib/vhd-util-coalesce.c b/vhd/lib/vhd-util-coalesce.c +index d3a85d6..8bcfb79 100644 +--- a/vhd/lib/vhd-util-coalesce.c ++++ b/vhd/lib/vhd-util-coalesce.c +@@ -221,7 +221,8 @@ static int64_t + vhd_util_coalesce_parent(const char *name, int sparse, int progress) + { + char *pname; +- int err, parent_fd; ++ int64_t err; ++ int parent_fd; + vhd_context_t vhd, parent; + + parent_fd = -1; +@@ -229,7 +230,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY); + if (err) { +- printf("error opening %s: %d\n", name, err); ++ printf("error opening %s: %" PRId64 "\n", name, err); + return err; + } + +@@ -241,7 +242,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + + err = vhd_parent_locator_get(&vhd, &pname); + if (err) { +- printf("error finding %s parent: %d\n", name, err); ++ printf("error finding %s parent: %" PRId64 "\n", name, err); + vhd_close(&vhd); + return err; + } +@@ -250,7 +251,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + parent_fd = open_optional_odirect(pname, O_RDWR | O_DIRECT | O_LARGEFILE, 0644); + if (parent_fd == -1) { + err = -errno; +- printf("failed to open parent %s: %d\n", pname, err); ++ printf("failed to open parent %s: %" PRId64 "\n", pname, err); + free(pname); + vhd_close(&vhd); + return err; +@@ -260,7 +261,7 @@ vhd_util_coalesce_parent(const char *name, int sparse, int progress) + if (sparse) printf("opening for sparse writes\n"); + err = vhd_open(&parent, pname, VHD_OPEN_RDWR | flags); + if (err) { +- printf("error opening %s: %d\n", pname, err); ++ printf("error opening %s: %" PRId64 "\n", pname, err); + free(pname); + vhd_close(&vhd); + return err; diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 6960a48..9b37425 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -1,6 +1,6 @@ -%global package_speccommit b2b1f8f158f3c539282a729b6527cf5f17d0861e +%global package_speccommit af72a3a53e2e66973dc542c79c50e5aed630b65c %global usver 3.55.5 -%global xsver 4 +%global xsver 6 %global xsrel %{xsver}%{?xscount}%{?xshash} %global package_srccommit v3.55.5 @@ -14,6 +14,9 @@ URL: https://github.com/xapi-project/blktap Source0: blktap-3.55.5.tar.gz Patch0: ca-404370__enable_nbd_client_only_after_completing_handshake.patch Patch1: cp_54256_log_eopnotsupp +Patch2: ca-408175__distinguish_logging_for_long_nbd_operations.patch +Patch3: CP-308382_fix_sign_conversion_in_coalesce +Patch4: fix_coalesced_size_conversion_in_vhd-util-coalesce.patch BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot Obsoletes: xen-blktap < 4 @@ -175,6 +178,13 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Thu Jul 31 2025 Mark Syms - 3.55.5-6 +- Fix coalesced size conversion in vhd-util-coalesce (CP-308382) + +* Mon Jun 23 2025 Mark Syms - 3.55.5-5 +- Backport fix for CA-408175 +- CP-308382: fix sign conversion in coalesce + * Mon Apr 07 2025 Mark Syms - 3.55.5-4 - CP-54256: log when reporting EOPNOTSUPP From 608163ef55eedd201c6d6a9c94ee629258977671 Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Thu, 8 Jan 2026 17:24:42 +0100 Subject: [PATCH 09/20] spec: Rebuild with openssl-3 This change rebuilds on xcp-ng-8.3 with openssl-devel-3.0.9-2.0.1.2.xcpng8.3.x86_64 Origin: https://github.com/xcp-ng-rpms/blktap/pull/13 Signed-off-by: Philippe Coval --- SPECS/blktap.spec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 3a0bdd4..efd6483 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.1%{?dist} +Release: %{?xsrel}.2%{?dist} License: BSD Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -22,7 +22,7 @@ BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot Obsoletes: xen-blktap < 4 BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autoconf, automake, libtool, libuuid-devel BuildRequires: kernel-headers, xen-libs-devel, zlib-devel, libcmocka-devel, lcov, git -BuildRequires: xs-openssl-devel >= 1.1.1 +BuildRequires: openssl-devel >= 3.0.9 BuildRequires: devtoolset-11-gcc BuildRequires: devtoolset-11-binutils BuildRequires: devtoolset-11-liblsan-devel @@ -182,6 +182,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Fri Feb 13 2026 Philippe Coval - 3.55.5-6.2 +- Rebuild with openssl-3 + * Mon Sep 08 2025 Anthoine Bourgeois - 3.55.5-6.1 - Sync with 3.55.5-6 - Revert openssl 3 dependency from XS specfile (no change compared to previous XCP-ng release) From fc4cb33f0234ee4b4bf3a05a4b265181b4505cd6 Mon Sep 17 00:00:00 2001 From: Mathieu Labourier Date: Fri, 19 Dec 2025 14:45:58 +0100 Subject: [PATCH 10/20] Prevent segfault of vhd-util scan on VHD with corrupt footer Signed-off-by: Mathieu Labourier --- ...-of-vhd-util-scan-on-VHD-with-corrup.patch | 59 +++++++++++++++++++ SPECS/blktap.spec | 8 ++- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch diff --git a/SOURCES/0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch b/SOURCES/0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch new file mode 100644 index 0000000..352fdc9 --- /dev/null +++ b/SOURCES/0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch @@ -0,0 +1,59 @@ +From c8c799c41f45bee68274332f7426e5525d4cbc4c Mon Sep 17 00:00:00 2001 +From: Mathieu Labourier +Date: Thu, 18 Dec 2025 19:41:52 +0100 +Subject: [PATCH] Prevent segfault of vhd-util scan on VHD with corrupt footer + +vhd-util scan can crash when ran against a VHD with a corrupt footer. + +Happens when the footer reports a VHD type that should have a parent +(like HD_TYPE_DIFF) while having none, causing the segfault. + +Signed-off-by: Mathieu Labourier +--- + vhd/lib/vhd-util-scan.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/vhd/lib/vhd-util-scan.c b/vhd/lib/vhd-util-scan.c +index eae9d68..17392ab 100644 +--- a/vhd/lib/vhd-util-scan.c ++++ b/vhd/lib/vhd-util-scan.c +@@ -46,6 +46,7 @@ + #include + #include + ++#include "debug.h" + #include "list.h" + #include "libvhd.h" + #include "lvm-util.h" +@@ -482,6 +483,14 @@ copy_name(char *dst, const char *src) + static int + vhd_util_scan_extract_volume_name(char *dst, const char *src, size_t size) + { ++ ASSERT(dst); ++ ASSERT(src); ++ ++ if (!*src) { ++ EPRINTF("parent name is empty\n"); ++ return -EINVAL; ++ } ++ + char copy[VHD_MAX_NAME_LEN], *name, *s, *c; + + name = strrchr(src, '/'); +@@ -509,6 +518,7 @@ vhd_util_scan_extract_volume_name(char *dst, const char *src, size_t size) + return -EINVAL; + } + ++ ASSERT(c && *c == '/'); + safe_strncpy(dst, ++c, size); + return 0; + } +@@ -542,7 +552,7 @@ found: + if (!err) + return copy_name(image->parent, name); + +- return 0; ++ return err; + } + + static int diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index efd6483..7b37cb2 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.2%{?dist} +Release: %{?xsrel}.3%{?dist} License: BSD Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -41,6 +41,9 @@ Provides: blktap(nbd) = 2.0 # Required by sm (qcow2). Upstream PR: https://github.com/xapi-project/blktap/pull/417 Patch1001: 0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +# Upstream commit: https://github.com/xapi-project/blktap/commit/b132675928ff991aa332d4fba3e95cad9dfb0aad +Patch1002: 0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch + %description Blktap creates kernel block devices which realize I/O requests to processes implementing virtual hard disk images entirely in user @@ -182,6 +185,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Thu Feb 26 2026 Mathieu Labourier - 3.55.5.6.3 +- Prevent segfault of vhd-util scan on VHD with corrupt footer + * Fri Feb 13 2026 Philippe Coval - 3.55.5-6.2 - Rebuild with openssl-3 From 4651c8c5790d0f266ea915679f2fc75aff8b8da5 Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Tue, 31 Mar 2026 15:57:57 +0200 Subject: [PATCH 11/20] Import blktap-3.55.5-7.xs8.src.rpm Signed-off-by: Philippe Coval Origin: https://www.xenserver.com/#blktap-3.55.5-7.xs8.src.rpm Checksums-Sha256: 0083b771cfe1275b45f7a2eb126681bb302fb7ad32455441f6005c35ce047e54 Checksums-Sha512: 0c1114ed3ac58fb9665790b6dbbe5c2205c51c2bfe0c3ac64790497e270002d0e37c20ed23e2eb44464ebff69ca9c0ef18c1c5cf16b1a9e8413dd435855ca56b --- ..._blkif_rsp_eopnotsupp_for_eopnotsupp.patch | 26 +++++++++++++++++++ SPECS/blktap.spec | 8 ++++-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 SOURCES/ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch diff --git a/SOURCES/ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch b/SOURCES/ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch new file mode 100644 index 0000000..6ce178c --- /dev/null +++ b/SOURCES/ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch @@ -0,0 +1,26 @@ +CA-416464: return BLKIF_RSP_EOPNOTSUPP for EOPNOTSUPP + +From: Mark Syms + +Returning BLKIF_RSP_ERROR if an operation is not supported does not +allow the frontend to exercise any discretion on how to handle the +response and may lead to an operating system crash. As different +backends may support different feature sets and we might, during a +migration, switch backends, an in-flight request might be issued (or +reissued) which is then not supported by this backend. + +Signed-off-by: Mark Syms + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 7b168dc..2a3fb05 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -531,6 +531,8 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + + if (likely(err == 0)) + _err = BLKIF_RSP_OKAY; ++ else if (err == EOPNOTSUPP) ++ _err = BLKIF_RSP_EOPNOTSUPP; + else + _err = BLKIF_RSP_ERROR; + diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 9b37425..ea9024e 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -1,6 +1,6 @@ -%global package_speccommit af72a3a53e2e66973dc542c79c50e5aed630b65c +%global package_speccommit 4dac0dd72bca87df4f25fc1e6d2f18713ecc53c1 %global usver 3.55.5 -%global xsver 6 +%global xsver 7 %global xsrel %{xsver}%{?xscount}%{?xshash} %global package_srccommit v3.55.5 @@ -17,6 +17,7 @@ Patch1: cp_54256_log_eopnotsupp Patch2: ca-408175__distinguish_logging_for_long_nbd_operations.patch Patch3: CP-308382_fix_sign_conversion_in_coalesce Patch4: fix_coalesced_size_conversion_in_vhd-util-coalesce.patch +Patch5: ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot Obsoletes: xen-blktap < 4 @@ -178,6 +179,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Thu Aug 28 2025 Mark Syms - 3.55.5-7 +- CA-416464: return BLKIF_RSP_EOPNOTSUPP for EOPNOTSUPP + * Thu Jul 31 2025 Mark Syms - 3.55.5-6 - Fix coalesced size conversion in vhd-util-coalesce (CP-308382) From fae7d5f39aa8fda11d43c8d37d56e979c82ac079 Mon Sep 17 00:00:00 2001 From: Damien Thenot Date: Thu, 27 Mar 2025 10:49:41 +0100 Subject: [PATCH 12/20] Add QCOW2 support Signed-off-by: Damien Thenot Signed-off-by: Anthoine Bourgeois --- ...-of-vhd-util-scan-on-VHD-with-corrup.patch | 59 + ...use-backup-footer-when-vhd-util-que.patch} | 4 +- ...k-deduplicate-double-assignment-code.patch | 24 + ...ix-a-typo-in-libaio-backend.h-header.patch | 31 + ...-final-param-in-__tapdisk_xenblkif_r.patch | 26 + ...pdisk_vbd_for_each_blkif-abstraction.patch | 25 + ...after-free-on-BLKIF_OP_WRITE_BARRIER.patch | 77 + ...ve-double-assignment-of-error-variab.patch | 33 + ...disk-replace-flag-number-by-its-name.patch | 57 + ...ric-TAPDISK_MESSAGE_MAX-limit-inside.patch | 45 + ...sk-remove-unused-file-tapdisk-diff.c.patch | 851 + ...dd-a-comment-on-memory-barrier-usage.patch | 28 + ...-tapback-Synchronise-usage-with-code.patch | 42 + ...ix-comments-of-tap_ctl_info-function.patch | 26 + ...ix-hardcoded-array-size-with-a-macro.patch | 59 + ...isk-Replace-structure-name-in-sizeof.patch | 28 + ...-RD-macros-are-defined-in-ring.h-sin.patch | 34 + ...-tapdisk-Fix-a-typo-in-util.h-header.patch | 23 + ...pdisk-rename-field-pool-to-pool_name.patch | 212 + ...0020-td-req-remove-unused-field-gref.patch | 37 + .../0021-td-req-rename-tapreq-as-req.patch | 433 + SOURCES/0022-td-req-remove-old-code.patch | 45 + .../0023-td-req-fix-typo-in-a-comment.patch | 27 + ...qcow2-manage-libqcow2-sources-import.patch | 413 + ...port-vanilla-sources-from-qemu-9.1.1.patch | 152808 +++++++++++++++ ...cow2-build-qcow2-library-for-tapdisk.patch | 9391 + ...port-for-old-components-gcc-glibc-gl.patch | 158 + ...8-tapdisk-protect-td_vbd_t-structure.patch | 516 + ...apdisk-protect-td_blktap_t-structure.patch | 117 + ...apdisk-protect-td_xenblkif-structure.patch | 271 + ...-tapdisk-protect-scheduler-structure.patch | 244 + ...ler-tests-according-mutex-protection.patch | 775 + ...disk-protect-td_nbdserver-structures.patch | 277 + ...-proper-cleanup-of-libqcow2-on-close.patch | 130 + ...ibqcow2-mask-signals-used-by-tapdisk.patch | 41 + ...replace-signals-handling-by-signalfd.patch | 138 + ...iver-support-qcow2-images-in-tapdisk.patch | 977 + ...ake-up-scheduler-to-force-check-ring.patch | 227 + ...9-blktap.spec-add-qcow2-dependencies.patch | 22 + ...0-tapdisk-support-new-commit-command.patch | 378 + .../0041-qcow2-support-commit-command.patch | 197 + ...42-tapdisk-support-new-query-command.patch | 370 + .../0043-qcow2-support-query-command.patch | 162 + ...4-tapdisk-support-new-cancel-command.patch | 312 + .../0045-qcow2-support-cancel-command.patch | 173 + ...qcow2-fix-abort-commit-without-crash.patch | 25 + ...of-vhd-util-scan-on-VHD-with-corrup.patch} | 0 SPECS/blktap.spec | 76 +- 48 files changed, 170403 insertions(+), 21 deletions(-) create mode 100644 SOURCES/0001-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch rename SOURCES/{0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch => 0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch} (96%) create mode 100644 SOURCES/0003-tapdisk-deduplicate-double-assignment-code.patch create mode 100644 SOURCES/0004-blktap-fix-a-typo-in-libaio-backend.h-header.patch create mode 100644 SOURCES/0005-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch create mode 100644 SOURCES/0006-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch create mode 100644 SOURCES/0007-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch create mode 100644 SOURCES/0008-tapdisk-vbd-remove-double-assignment-of-error-variab.patch create mode 100644 SOURCES/0009-tapdisk-replace-flag-number-by-its-name.patch create mode 100644 SOURCES/0010-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch create mode 100644 SOURCES/0011-tapdisk-remove-unused-file-tapdisk-diff.c.patch create mode 100644 SOURCES/0012-blkif-add-a-comment-on-memory-barrier-usage.patch create mode 100644 SOURCES/0013-tapback-Synchronise-usage-with-code.patch create mode 100644 SOURCES/0014-tap-ctl-fix-comments-of-tap_ctl_info-function.patch create mode 100644 SOURCES/0015-tapdisk-fix-hardcoded-array-size-with-a-macro.patch create mode 100644 SOURCES/0016-tapdisk-Replace-structure-name-in-sizeof.patch create mode 100644 SOURCES/0017-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch create mode 100644 SOURCES/0018-tapdisk-Fix-a-typo-in-util.h-header.patch create mode 100644 SOURCES/0019-tapdisk-rename-field-pool-to-pool_name.patch create mode 100644 SOURCES/0020-td-req-remove-unused-field-gref.patch create mode 100644 SOURCES/0021-td-req-rename-tapreq-as-req.patch create mode 100644 SOURCES/0022-td-req-remove-old-code.patch create mode 100644 SOURCES/0023-td-req-fix-typo-in-a-comment.patch create mode 100644 SOURCES/0024-libqcow2-manage-libqcow2-sources-import.patch create mode 100644 SOURCES/0025-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch create mode 100644 SOURCES/0026-libqcow2-build-qcow2-library-for-tapdisk.patch create mode 100644 SOURCES/0027-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch create mode 100644 SOURCES/0028-tapdisk-protect-td_vbd_t-structure.patch create mode 100644 SOURCES/0029-tapdisk-protect-td_blktap_t-structure.patch create mode 100644 SOURCES/0030-tapdisk-protect-td_xenblkif-structure.patch create mode 100644 SOURCES/0031-tapdisk-protect-scheduler-structure.patch create mode 100644 SOURCES/0032-mocka-fix-scheduler-tests-according-mutex-protection.patch create mode 100644 SOURCES/0033-tapdisk-protect-td_nbdserver-structures.patch create mode 100644 SOURCES/0034-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch create mode 100644 SOURCES/0035-libqcow2-mask-signals-used-by-tapdisk.patch create mode 100644 SOURCES/0036-tapdisk-replace-signals-handling-by-signalfd.patch create mode 100644 SOURCES/0037-qcow2-driver-support-qcow2-images-in-tapdisk.patch create mode 100644 SOURCES/0038-vbd-wake-up-scheduler-to-force-check-ring.patch create mode 100644 SOURCES/0039-blktap.spec-add-qcow2-dependencies.patch create mode 100644 SOURCES/0040-tapdisk-support-new-commit-command.patch create mode 100644 SOURCES/0041-qcow2-support-commit-command.patch create mode 100644 SOURCES/0042-tapdisk-support-new-query-command.patch create mode 100644 SOURCES/0043-qcow2-support-query-command.patch create mode 100644 SOURCES/0044-tapdisk-support-new-cancel-command.patch create mode 100644 SOURCES/0045-qcow2-support-cancel-command.patch create mode 100644 SOURCES/0046-libqcow2-fix-abort-commit-without-crash.patch rename SOURCES/{0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch => Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch} (100%) diff --git a/SOURCES/0001-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch b/SOURCES/0001-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch new file mode 100644 index 0000000..a9ef1f5 --- /dev/null +++ b/SOURCES/0001-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch @@ -0,0 +1,59 @@ +From c694c7ce5b1a598812e7134348a464c05d400d61 Mon Sep 17 00:00:00 2001 +From: Mathieu Labourier +Date: Thu, 18 Dec 2025 19:41:52 +0100 +Subject: [PATCH] Prevent segfault of vhd-util scan on VHD with corrupt footer + +vhd-util scan can crash when ran against a VHD with a corrupt footer. + +Happens when the footer reports a VHD type that should have a parent +(like HD_TYPE_DIFF) while having none, causing the segfault. + +Signed-off-by: Mathieu Labourier +--- + vhd/lib/vhd-util-scan.c | 12 +++++++++++- + 1 file changed, 11 insertions(+), 1 deletion(-) + +diff --git a/vhd/lib/vhd-util-scan.c b/vhd/lib/vhd-util-scan.c +index eae9d681..17392ab4 100644 +--- a/vhd/lib/vhd-util-scan.c ++++ b/vhd/lib/vhd-util-scan.c +@@ -46,6 +46,7 @@ + #include + #include + ++#include "debug.h" + #include "list.h" + #include "libvhd.h" + #include "lvm-util.h" +@@ -482,6 +483,14 @@ copy_name(char *dst, const char *src) + static int + vhd_util_scan_extract_volume_name(char *dst, const char *src, size_t size) + { ++ ASSERT(dst); ++ ASSERT(src); ++ ++ if (!*src) { ++ EPRINTF("parent name is empty\n"); ++ return -EINVAL; ++ } ++ + char copy[VHD_MAX_NAME_LEN], *name, *s, *c; + + name = strrchr(src, '/'); +@@ -509,6 +518,7 @@ vhd_util_scan_extract_volume_name(char *dst, const char *src, size_t size) + return -EINVAL; + } + ++ ASSERT(c && *c == '/'); + safe_strncpy(dst, ++c, size); + return 0; + } +@@ -542,7 +552,7 @@ found: + if (!err) + return copy_name(image->parent, name); + +- return 0; ++ return err; + } + + static int diff --git a/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch b/SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch similarity index 96% rename from SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch rename to SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch index fb3c084..751264d 100644 --- a/SOURCES/0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +++ b/SOURCES/0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch @@ -1,4 +1,4 @@ -From c9237c52ffdf876c2d2e1a12b8c4a0cff68eafee Mon Sep 17 00:00:00 2001 +From d8cda8eb7a7404102b10a05e1226129e5c424d0e Mon Sep 17 00:00:00 2001 From: Ronan Abhamon Date: Wed, 18 Dec 2024 16:55:26 +0100 Subject: [PATCH] Add an option to use backup footer when vhd-util query is @@ -13,7 +13,7 @@ Signed-off-by: Ronan Abhamon 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/vhd/lib/vhd-util-query.c b/vhd/lib/vhd-util-query.c -index 0cd0416..3aca668 100644 +index 0cd04166..3aca668c 100644 --- a/vhd/lib/vhd-util-query.c +++ b/vhd/lib/vhd-util-query.c @@ -43,29 +43,29 @@ diff --git a/SOURCES/0003-tapdisk-deduplicate-double-assignment-code.patch b/SOURCES/0003-tapdisk-deduplicate-double-assignment-code.patch new file mode 100644 index 0000000..87ee021 --- /dev/null +++ b/SOURCES/0003-tapdisk-deduplicate-double-assignment-code.patch @@ -0,0 +1,24 @@ +From da68419a7008aa37563ffe5b3c7fce5b455edb62 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:07 +0100 +Subject: [PATCH] tapdisk: deduplicate double assignment code + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-server.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/tapdisk-server.c b/drivers/tapdisk-server.c +index 27a42198..a165d2f2 100644 +--- a/drivers/tapdisk-server.c ++++ b/drivers/tapdisk-server.c +@@ -788,7 +788,6 @@ tapdisk_server_complete(void) + server.rw_backend = get_libaio_backend(); + server.ro_backend = get_libaio_backend(); + +- server.rw_backend = get_libaio_backend(); + err = tapdisk_server_init_aio(); + if (err) + goto fail; diff --git a/SOURCES/0004-blktap-fix-a-typo-in-libaio-backend.h-header.patch b/SOURCES/0004-blktap-fix-a-typo-in-libaio-backend.h-header.patch new file mode 100644 index 0000000..108b58a --- /dev/null +++ b/SOURCES/0004-blktap-fix-a-typo-in-libaio-backend.h-header.patch @@ -0,0 +1,31 @@ +From 45658a3d9f323a6f1bc512297bebf487287d5f67 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:11 +0100 +Subject: [PATCH] blktap: fix a typo in libaio-backend.h header + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/libaio-backend.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/libaio-backend.h b/drivers/libaio-backend.h +index 2759307d..21707c4a 100644 +--- a/drivers/libaio-backend.h ++++ b/drivers/libaio-backend.h +@@ -29,7 +29,7 @@ + */ + + #ifndef LIBAIO_BACKEND_H +-#define LIBAIO_BACLEND_H ++#define LIBAIO_BACKEND_H + + #include + +@@ -43,4 +43,4 @@ enum { + + struct backend* get_libaio_backend(); + +-#endif /* LIBAIO_BACLEND_H */ ++#endif /* LIBAIO_BACKEND_H */ diff --git a/SOURCES/0005-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch b/SOURCES/0005-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch new file mode 100644 index 0000000..889d1d9 --- /dev/null +++ b/SOURCES/0005-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch @@ -0,0 +1,26 @@ +From 2de5a44347e224ec3be404860d5d98e6fba1b1ff Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 20 Jan 2025 14:45:35 +0100 +Subject: [PATCH] tapdisk: document final param in + __tapdisk_xenblkif_request_cb comment + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index e1ce9fd7..178985f3 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -591,7 +591,7 @@ out: + * @param vreq the completed request + * @param error status of the request + * @param token token previously associated with this request +- * @param final TODO ? ++ * @param final controls whether the other end should be notified + */ + static inline void + __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, diff --git a/SOURCES/0006-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch b/SOURCES/0006-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch new file mode 100644 index 0000000..e7064ee --- /dev/null +++ b/SOURCES/0006-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch @@ -0,0 +1,25 @@ +From 27d453c5eb2f025b10e7c32ae9239e9d18ce66ab Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 15 Jan 2025 16:33:02 +0100 +Subject: [PATCH] tapdisk: use tapdisk_vbd_for_each_blkif abstraction + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-control.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index c8b9d826..506b203d 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -898,7 +898,7 @@ tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, + tapdisk_nbdserver_pause(vbd->nbdserver_new, true); + + err = 0; +- list_for_each_entry_safe(blkif, _blkif, &vbd->rings, entry) { ++ tapdisk_vbd_for_each_blkif(vbd, blkif, _blkif) { + + DPRINTF("implicitly disconnecting ring %p domid=%d, devid=%d\n", + blkif, blkif->domid, blkif->devid); diff --git a/SOURCES/0007-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch b/SOURCES/0007-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch new file mode 100644 index 0000000..eea2e42 --- /dev/null +++ b/SOURCES/0007-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch @@ -0,0 +1,77 @@ +From 6ed8043712dbc18635fa10d879a9a04eaba7eb2c Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 20 Jan 2025 18:22:07 +0100 +Subject: [PATCH] blkif: Avoid use-after-free on BLKIF_OP_WRITE_BARRIER request + +Function tapdisk_xenblkif_make_vbd_request will release +tapreq->msg and reinsert it in the reqs_free array if the +request is a BLKIF_OP_WRITE_BARRIER without any segment. +But tapdisk_xenblkif_queue_request will use it to check if +it must send the request to VBD layer. + +This bug triggers a crash when DEBUG is defined in td-reqs.c +because DEBUG will poison the msg memory. +Without DEBUG, the code reads freed memory that still contains +unchanged data. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 178985f3..c35b02c6 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -190,17 +190,21 @@ static void + tapdisk_xenblkif_free_request(struct td_xenblkif * const blkif, + struct td_xenblkif_req * const tapreq) + { ++ int put_bufcache; ++ + ASSERT(blkif); + ASSERT(tapreq); + ASSERT(blkif->n_reqs_free < blkif->ring_size); + ++ put_bufcache = tapreq->msg.nr_segments != 0; ++ + #ifdef DEBUG + memset(&tapreq->msg, BLKIF_MSG_POISON, sizeof(tapreq->msg)); + #endif + + blkif->reqs_free[blkif->ring_size - (++blkif->n_reqs_free)] = &tapreq->msg; + +- if (likely(tapreq->msg.nr_segments)) ++ if (likely(put_bufcache)) + td_xenblkif_bufcache_put(blkif, tapreq->vma); + } + +@@ -829,11 +833,19 @@ tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif, + blkif_request_t *msg, struct td_xenblkif_req *tapreq) + { + int err; ++ int queue_request; + + ASSERT(blkif); + ASSERT(msg); + ASSERT(tapreq); + ++ queue_request = tapreq->msg.nr_segments != 0; ++ ++ /* ++ * Do not use tapreq after tapdisk_xenblkif_make_vbd_request ++ * because this function can release tapreq->msg and reinsert it ++ * in the reqs_free array. ++ */ + err = tapdisk_xenblkif_make_vbd_request(blkif, tapreq); + if (unlikely(err)) { + /* TODO log error */ +@@ -841,7 +853,7 @@ tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif, + return err; + } + +- if (likely(tapreq->msg.nr_segments)) { ++ if (likely(queue_request)) { + err = tapdisk_vbd_queue_request(blkif->vbd, &tapreq->vreq); + if (unlikely(err)) { + /* TODO log error */ diff --git a/SOURCES/0008-tapdisk-vbd-remove-double-assignment-of-error-variab.patch b/SOURCES/0008-tapdisk-vbd-remove-double-assignment-of-error-variab.patch new file mode 100644 index 0000000..03dccce --- /dev/null +++ b/SOURCES/0008-tapdisk-vbd-remove-double-assignment-of-error-variab.patch @@ -0,0 +1,33 @@ +From a7c7447ed112f6106cbd270ba8067b4df2378f08 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 20 Jan 2025 14:48:57 +0100 +Subject: [PATCH] tapdisk-vbd: remove double assignment of error variable + +fail label already set vreq->error with err, so remove the +first assignment in both paths that lead to fail label. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-vbd.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 84385704..433375d6 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1527,13 +1527,11 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + + err = tapdisk_vbd_check_queue(vbd); + if (err) { +- vreq->error = err; + goto fail; + } + + err = tapdisk_image_check_request(image, vreq); + if (err) { +- vreq->error = err; + goto fail; + } + diff --git a/SOURCES/0009-tapdisk-replace-flag-number-by-its-name.patch b/SOURCES/0009-tapdisk-replace-flag-number-by-its-name.patch new file mode 100644 index 0000000..9b554b7 --- /dev/null +++ b/SOURCES/0009-tapdisk-replace-flag-number-by-its-name.patch @@ -0,0 +1,57 @@ +From 8edd8f5e5797a3d88b27b24fb85c90cd878aff34 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 21 Jan 2025 10:34:16 +0100 +Subject: [PATCH] tapdisk: replace flag number by its name + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-disktype.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/tapdisk-disktype.c b/drivers/tapdisk-disktype.c +index 8ab96c4f..4a60c756 100644 +--- a/drivers/tapdisk-disktype.c ++++ b/drivers/tapdisk-disktype.c +@@ -54,13 +54,13 @@ static const disk_info_t sync_disk = { + static const disk_info_t vmdk_disk = { + "vmdk", + "vmware image (vmdk)", +- 1, ++ DISK_TYPE_SINGLE_CONTROLLER, + }; + + static const disk_info_t vhdsync_disk = { + "vhdsync", + "virtual server image (vhd) - synchronous", +- 1, ++ DISK_TYPE_SINGLE_CONTROLLER, + }; + + static const disk_info_t vhd_disk = { +@@ -73,7 +73,7 @@ static const disk_info_t vhd_disk = { + static const disk_info_t ram_disk = { + "ram", + "ramdisk image (ram)", +- 1, ++ DISK_TYPE_SINGLE_CONTROLLER, + }; + + static const disk_info_t qcow_disk = { +@@ -85,13 +85,13 @@ static const disk_info_t qcow_disk = { + static const disk_info_t block_cache_disk = { + "bc", + "block cache image (bc)", +- 1, ++ DISK_TYPE_SINGLE_CONTROLLER, + }; + + static const disk_info_t vhd_index_disk = { + "vhdi", + "vhd index image (vhdi)", +- 1, ++ DISK_TYPE_SINGLE_CONTROLLER, + }; + + static const disk_info_t log_disk = { diff --git a/SOURCES/0010-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch b/SOURCES/0010-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch new file mode 100644 index 0000000..0dbef44 --- /dev/null +++ b/SOURCES/0010-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch @@ -0,0 +1,45 @@ +From 774d320fad1f9cc37491b9e7dd157335d6109244 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 13 Feb 2025 14:52:10 +0100 +Subject: [PATCH] tapdisk: set generic TAPDISK_MESSAGE_MAX limit inside the + enum + +This avoids to forget to update the TAPDISK_MESSAGE_MAX define. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-control.c | 2 +- + include/tapdisk-message.h | 3 +-- + 2 files changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 506b203d..91ffe097 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1382,7 +1382,7 @@ tapdisk_control_receive_request(struct tapdisk_ctl_conn *conn) + if (err) + goto invalid; + +- if (conn->request.type > TAPDISK_MESSAGE_EXIT) ++ if (conn->request.type >= TAPDISK_MESSAGE_MAX) + goto invalid; + + conn->info = &message_infos[conn->request.type]; +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index dceddbbe..0f73e0c9 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -233,10 +233,9 @@ enum tapdisk_message_id { + TAPDISK_MESSAGE_DISK_INFO, + TAPDISK_MESSAGE_DISK_INFO_RSP, + TAPDISK_MESSAGE_EXIT, ++ TAPDISK_MESSAGE_MAX /* This value must be the last. */ + }; + +-#define TAPDISK_MESSAGE_MAX TAPDISK_MESSAGE_EXIT +- + static inline char * + tapdisk_message_name(enum tapdisk_message_id id) + { diff --git a/SOURCES/0011-tapdisk-remove-unused-file-tapdisk-diff.c.patch b/SOURCES/0011-tapdisk-remove-unused-file-tapdisk-diff.c.patch new file mode 100644 index 0000000..7cc55a9 --- /dev/null +++ b/SOURCES/0011-tapdisk-remove-unused-file-tapdisk-diff.c.patch @@ -0,0 +1,851 @@ +From 3f2ca1e82015a9f44cb7c9c5524ee11f29d4d18c Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Oct 2025 18:43:42 +0200 +Subject: [PATCH] tapdisk: remove unused file tapdisk-diff.c + +Also remove request_list attribute of VBD structure only used in +tapdisk-diff.c. This file is not compiled anymore. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-diff.c | 815 ----------------------------------------- + drivers/tapdisk-vbd.h | 2 - + 2 files changed, 817 deletions(-) + delete mode 100644 drivers/tapdisk-diff.c + +diff --git a/drivers/tapdisk-diff.c b/drivers/tapdisk-diff.c +deleted file mode 100644 +index a616dd58..00000000 +--- a/drivers/tapdisk-diff.c ++++ /dev/null +@@ -1,815 +0,0 @@ +-/* +- * Copyright (c) 2016, Citrix Systems, Inc. +- * +- * All rights reserved. +- * +- * Redistribution and use in source and binary forms, with or without +- * modification, are permitted provided that the following conditions are met: +- * +- * 1. Redistributions of source code must retain the above copyright +- * notice, this list of conditions and the following disclaimer. +- * 2. Redistributions in binary form must reproduce the above copyright +- * notice, this list of conditions and the following disclaimer in the +- * documentation and/or other materials provided with the distribution. +- * 3. Neither the name of the copyright holder nor the names of its +- * contributors may be used to endorse or promote products derived from +- * this software without specific prior written permission. +- * +- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- */ +- +-#ifdef HAVE_CONFIG_H +-#include "config.h" +-#endif +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "list.h" +-#include "scheduler.h" +-#include "tapdisk-vbd.h" +-#include "tapdisk-server.h" +-#include "tapdisk-disktype.h" +-#include "timeout-math.h" +-#include "libvhd.h" +- +-#define POLL_READ 0 +-#define POLL_WRITE 1 +- +-#define SPB_SHIFT (VHD_BLOCK_SHIFT - SECTOR_SHIFT) +- +-/* +- * we have to use half the max number of requests because we're using the same +- * tapdisk server for both streams and all the parents will be shared. If we +- * issue more than MAX_REQUESTS/2 requests, the vhd_state will run out of +- * vhd_request's and return EBUSY, which we don't handle here. However, even +- * with MAX_REQUESTS/2 we can still run out of vhd_request's because of +- * splitting: if some sectors spanned by a segment are in a parent, a segment +- * could be split into at most N/2 vhd_request's, where N is the number of +- * sectors per segment. Therefore, if we use 11 segments, we need to divide +- * MAX_REQUESTS by 11/2=6 on top of that. If we don't, we'd have to handle +- * EBUSY by retrying here. +- */ +-#define MAX_SEGMENTS 8 +-#define MAX_STREAM_REQUESTS (MAX_REQUESTS / 2 / (MAX_SEGMENTS / 2)) +- +-struct tapdisk_stream_poll { +- int pipe[2]; +- int set; +-}; +- +-struct tapdisk_stream_request { +- uint64_t sec; +- uint32_t secs; +- uint64_t seqno; +- blkif_request_t blkif_req; +- struct list_head next; +-}; +- +-struct tapdisk_stream { +- td_vbd_t *vbd; +- +- unsigned int id; +- +- int err; +- +- uint64_t cur; +- uint64_t start; +- uint64_t end; +- +- uint64_t started; +- uint64_t completed; +- +- struct tapdisk_stream_poll poll; +- event_id_t enqueue_event_id; +- +- struct list_head free_list; +- struct list_head pending_list; +- struct list_head completed_list; +- +- struct tapdisk_stream_request requests[MAX_STREAM_REQUESTS]; +-}; +- +-static unsigned int tapdisk_stream_count; +- +-static void tapdisk_stream_close_image(struct tapdisk_stream *); +- +-static char *program; +-static struct tapdisk_stream stream1, stream2; +-static vhd_context_t vhd1; +- +-static void +-usage(FILE *stream) +-{ +- printf("usage: %s <-n type:/path/to/image> <-m type:/path/to/image>\n", +- program); +-} +- +-static int +-open_vhd(const char *path, vhd_context_t *vhd) +-{ +- int err; +- +- err = vhd_open(vhd, path, VHD_OPEN_RDONLY); +- if (err) { +- printf("error opening %s: %d\n", path, err); +- return err; +- } +- +- err = vhd_get_bat(vhd); +- if (err) +- { +- printf("error reading BAT for %s: %d\n", path, err); +- vhd_close(vhd); +- return err; +- } +- +- return 0; +-} +- +-static inline void +-tapdisk_stream_poll_initialize(struct tapdisk_stream_poll *p) +-{ +- p->set = 0; +- p->pipe[POLL_READ] = p->pipe[POLL_WRITE] = -1; +-} +- +-static int +-tapdisk_stream_poll_open(struct tapdisk_stream_poll *p) +-{ +- int err; +- +- tapdisk_stream_poll_initialize(p); +- +- err = pipe(p->pipe); +- if (err) +- return -errno; +- +- err = fcntl(p->pipe[POLL_READ], F_SETFL, O_NONBLOCK); +- if (err) +- goto out; +- +- err = fcntl(p->pipe[POLL_WRITE], F_SETFL, O_NONBLOCK); +- if (err) +- goto out; +- +- return 0; +- +-out: +- close(p->pipe[POLL_READ]); +- close(p->pipe[POLL_WRITE]); +- tapdisk_stream_poll_initialize(p); +- return -errno; +-} +- +-static void +-tapdisk_stream_poll_close(struct tapdisk_stream_poll *p) +-{ +- if (p->pipe[POLL_READ] != -1) +- close(p->pipe[POLL_READ]); +- if (p->pipe[POLL_WRITE] != -1) +- close(p->pipe[POLL_WRITE]); +- tapdisk_stream_poll_initialize(p); +-} +- +-static inline void +-tapdisk_stream_poll_clear(struct tapdisk_stream_poll *p) +-{ +- int gcc, dummy; +- +- gcc = read(p->pipe[POLL_READ], &dummy, sizeof(dummy)); +- p->set = 0; +-} +- +-static inline void +-tapdisk_stream_poll_set(struct tapdisk_stream_poll *p) +-{ +- int dummy = 0; +- +- if (!p->set) { +- int gcc = write(p->pipe[POLL_WRITE], &dummy, sizeof(dummy)); +- p->set = 1; +- } +-} +- +-static inline int +-tapdisk_stream_stop(struct tapdisk_stream *s) +-{ +- return ((s->cur == s->end || s->err) && +- list_empty(&s->pending_list) && +- list_empty(&s->completed_list)); +-} +- +-static inline void +-tapdisk_stream_initialize_request(struct tapdisk_stream_request *req) +-{ +- memset(req, 0, sizeof(*req)); +- INIT_LIST_HEAD(&req->next); +-} +- +-static inline int +-tapdisk_stream_request_idx(struct tapdisk_stream *s, +- struct tapdisk_stream_request *req) +-{ +- return (req - s->requests); +-} +- +-static inline struct tapdisk_stream_request * +-tapdisk_stream_get_request(struct tapdisk_stream *s) +-{ +- struct tapdisk_stream_request *req; +- +- if (list_empty(&s->free_list)) +- return NULL; +- +- req = list_entry(s->free_list.next, +- struct tapdisk_stream_request, next); +- +- list_del_init(&req->next); +- tapdisk_stream_initialize_request(req); +- +- return req; +-} +- +-static inline void +-tapdisk_stream_queue_completed(struct tapdisk_stream *s, +- struct tapdisk_stream_request *sreq) +-{ +- struct tapdisk_stream_request *itr; +- +- list_for_each_entry(itr, &s->completed_list, next) +- if (sreq->seqno < itr->seqno) { +- list_add_tail(&sreq->next, &itr->next); +- return; +- } +- +- list_add_tail(&sreq->next, &s->completed_list); +-} +- +-static int +-tapdisk_result_compare(struct tapdisk_stream_request *sreq1, +- struct tapdisk_stream_request *sreq2) +-{ +- unsigned long idx1, idx2; +- char *buf1, *buf2; +- int result; +- +- assert(sreq1->seqno == sreq2->seqno); +- assert(sreq1->secs == sreq2->secs); +- idx1 = (unsigned long)tapdisk_stream_request_idx(&stream1, +- sreq1); +- idx2 = (unsigned long)tapdisk_stream_request_idx(&stream2, +- sreq2); +- buf1 = (char *)MMAP_VADDR(stream1.vbd->ring.vstart, idx1, 0); +- buf2 = (char *)MMAP_VADDR(stream2.vbd->ring.vstart, idx2, 0); +- +- result = memcmp(buf1, buf2, sreq1->secs << SECTOR_SHIFT); +- return result; +-} +- +-static int +-tapdisk_stream_process_data(void) +-{ +- struct tapdisk_stream_request *sreq1, *sreq2, *tmp1, *tmp2; +- int advance_both; +- int result = 0; +- +- sreq1 = list_entry(stream1.completed_list.next, +- struct tapdisk_stream_request, next); +- sreq2 = list_entry(stream2.completed_list.next, +- struct tapdisk_stream_request, next); +- tmp1 = list_entry(sreq1->next.next, +- struct tapdisk_stream_request, next); +- tmp2 = list_entry(sreq2->next.next, +- struct tapdisk_stream_request, next); +- while (result == 0 && +- &sreq1->next != &stream1.completed_list && +- &sreq2->next != &stream2.completed_list) { +- //printf("checking: %llu|%llu\n", sreq1->seqno, sreq2->seqno); +- advance_both = 1; +- if (sreq1->seqno < sreq2->seqno) { +- advance_both = 0; +- goto advance1; +- } +- if (sreq1->seqno > sreq2->seqno) +- goto advance2; +- +- result = tapdisk_result_compare(sreq1, sreq2); +- +- stream1.completed++; +- stream2.completed++; +- +- list_del_init(&sreq1->next); +- list_add_tail(&sreq1->next, &stream1.free_list); +- list_del_init(&sreq2->next); +- list_add_tail(&sreq2->next, &stream2.free_list); +- +-advance1: +- sreq1 = tmp1; +- tmp1 = list_entry(tmp1->next.next, +- struct tapdisk_stream_request, next); +- if (!advance_both) +- continue; +-advance2: +- sreq2 = tmp2; +- tmp2 = list_entry(tmp2->next.next, +- struct tapdisk_stream_request, next); +- } +- +- return result; +-} +- +-static void +-tapdisk_stream_dequeue(void *arg, blkif_response_t *rsp) +-{ +- struct tapdisk_stream *s = (struct tapdisk_stream *)arg; +- struct tapdisk_stream_request *sreq = s->requests + rsp->id; +- +- list_del_init(&sreq->next); +- +- if (rsp->status == BLKIF_RSP_OKAY) +- tapdisk_stream_queue_completed(s, sreq); +- else { +- s->err = EIO; +- list_add_tail(&sreq->next, &s->free_list); +- fprintf(stderr, "error reading sector %llu (stream %d)\n", +- sreq->sec, (s == &stream2) + 1); +- } +- +- if (tapdisk_stream_process_data()) { +- fprintf(stderr, "mismatch at sector %llu\n", +- sreq->sec); +- stream1.err = EINVAL; +- stream2.err = EINVAL; +- } +- +- tapdisk_stream_poll_set(&stream1.poll); +- tapdisk_stream_poll_set(&stream2.poll); +-} +- +-static inline int +-tapdisk_stream_enqueue_copy(struct tapdisk_stream *s, +- struct tapdisk_stream_request *r) +-{ +- td_vbd_t *vbd; +- blkif_request_t *breq; +- td_vbd_request_t *vreq; +- struct tapdisk_stream_request *sreq; +- int idx; +- +- vbd = stream2.vbd; +- sreq = tapdisk_stream_get_request(s); +- if (!sreq) +- return 1; +- +- idx = tapdisk_stream_request_idx(s, sreq); +- +- sreq->sec = r->sec; +- sreq->secs = r->secs; +- sreq->seqno = r->seqno; +- +- breq = &sreq->blkif_req; +- breq->id = idx; +- breq->nr_segments = r->blkif_req.nr_segments; +- breq->sector_number = r->blkif_req.sector_number; +- breq->operation = BLKIF_OP_READ; +- +- for (int i = 0; i < r->blkif_req.nr_segments; i++) { +- struct blkif_request_segment *seg = breq->seg + i; +- seg->first_sect = r->blkif_req.seg[i].first_sect; +- seg->last_sect = r->blkif_req.seg[i].last_sect; +- } +- s->cur += sreq->secs; +- +- vreq = vbd->request_list + idx; +- assert(list_empty(&vreq->next)); +- assert(vreq->secs_pending == 0); +- +- memcpy(&vreq->req, breq, sizeof(*breq)); +- s->started++; +- vbd->received++; +- vreq->vbd = vbd; +- +- tapdisk_vbd_move_request(vreq, &vbd->new_requests); +- list_add_tail(&sreq->next, &s->pending_list); +- +- return 0; +-} +- +-static void +-tapdisk_stream_enqueue1(void) +-{ +- td_vbd_t *vbd; +- int i, idx, psize, blk; +- struct tapdisk_stream *s = &stream1; +- +- vbd = s->vbd; +- psize = getpagesize(); +- +- while (s->cur < s->end && !s->err) { +- blkif_request_t *breq; +- td_vbd_request_t *vreq; +- struct tapdisk_stream_request *sreq; +- +- /* skip any blocks that are not present in this image */ +- blk = s->cur >> SPB_SHIFT; +- while (s->cur < s->end && vhd1.bat.bat[blk] == DD_BLK_UNUSED) { +- //printf("skipping block %d\n", blk); +- blk++; +- s->cur = blk << SPB_SHIFT; +- } +- +- if (s->cur >= s->end) +- break; +- +- sreq = tapdisk_stream_get_request(s); +- if (!sreq) +- break; +- +- idx = tapdisk_stream_request_idx(s, sreq); +- +- sreq->sec = s->cur; +- sreq->secs = 0; +- sreq->seqno = s->started++; +- +- breq = &sreq->blkif_req; +- breq->id = idx; +- breq->nr_segments = 0; +- breq->sector_number = sreq->sec; +- breq->operation = BLKIF_OP_READ; +- +- for (i = 0; i < MAX_SEGMENTS; i++) { +- uint32_t secs; +- struct blkif_request_segment *seg = breq->seg + i; +- +- secs = MIN(s->end - s->cur, psize >> SECTOR_SHIFT); +- secs = MIN(((blk + 1) << SPB_SHIFT) - s->cur, secs); +- if (!secs) +- break; +- +- sreq->secs += secs; +- s->cur += secs; +- +- seg->first_sect = 0; +- seg->last_sect = secs - 1; +- breq->nr_segments++; +- } +- +- vreq = vbd->request_list + idx; +- +- assert(list_empty(&vreq->next)); +- assert(vreq->secs_pending == 0); +- +- memcpy(&vreq->req, breq, sizeof(*breq)); +- vbd->received++; +- vreq->vbd = vbd; +- +- tapdisk_vbd_move_request(vreq, &vbd->new_requests); +- list_add_tail(&sreq->next, &s->pending_list); +- } +- +- tapdisk_vbd_issue_requests(vbd); +-} +- +-static void +-tapdisk_stream_enqueue2(void) +-{ +- td_vbd_t *vbd; +- int i, blk; +- struct tapdisk_stream_request *itr; +- struct tapdisk_stream *s = &stream2; +- +- vbd = s->vbd; +- +- /* issue the same requests that we issued on stream1 */ +- list_for_each_entry(itr, &stream1.completed_list, next) { +- if (itr->sec < s->cur) +- continue; +- if (tapdisk_stream_enqueue_copy(s, itr)) +- goto done; +- } +- +- list_for_each_entry(itr, &stream1.pending_list, next) { +- if (itr->sec < s->cur) +- continue; +- if (tapdisk_stream_enqueue_copy(s, itr)) +- goto done; +- } +- +- stream2.cur = stream1.cur; +- +-done: +- tapdisk_vbd_issue_requests(vbd); +-} +- +-static inline int +-tapdisk_diff_done(void) +-{ +- return (tapdisk_stream_stop(&stream1) && tapdisk_stream_stop(&stream2)); +-} +- +-static void +-tapdisk_diff_stop(void) +-{ +- tapdisk_stream_close_image(&stream1); +- tapdisk_stream_close_image(&stream2); +-} +- +-static void +-tapdisk_stream_enqueue(event_id_t id, char mode, void *arg) +-{ +- struct tapdisk_stream *s = (struct tapdisk_stream *)arg; +- +- tapdisk_stream_poll_clear(&s->poll); +- +- if (tapdisk_diff_done()) { +- tapdisk_diff_stop(); +- return; +- } +- +- if (s == &stream1) +- tapdisk_stream_enqueue1(); +- else if (s == &stream2) +- tapdisk_stream_enqueue2(); +- else +- assert(0); +- +- if (tapdisk_diff_done()) { +- // we have to check again for the case when stream1 had no +- // blocks at all +- tapdisk_diff_stop(); +- return; +- } +-} +- +-static int +-tapdisk_stream_open_image(struct tapdisk_stream *s, const char *name) +-{ +- int err; +- td_disk_info_t info; +- +- s->id = tapdisk_stream_count++; +- +- err = tapdisk_vbd_initialize(-1, -1, s->id); +- if (err) +- goto out; +- +- s->vbd = tapdisk_server_get_vbd(s->id); +- if (!s->vbd) { +- err = ENODEV; +- goto out; +- } +- +- tapdisk_vbd_set_callback(s->vbd, tapdisk_stream_dequeue, s); +- +- err = tapdisk_vbd_open_vdi(s->vbd, name, TD_OPEN_RDONLY, -1); +- if (err) +- goto out; +- +- err = tapdisk_vbd_get_disk_info(s->vbd, &info); +- if (err) { +- fprintf(stderr, "failed getting image size: %d\n", err); +- return err; +- } +- +- s->start = 0; +- s->cur = s->start; +- s->end = info.size; +- +- err = 0; +- +-out: +- if (err) +- fprintf(stderr, "failed to open image %s: %d\n", name, err); +- return err; +-} +- +-static void +-tapdisk_stream_close_image(struct tapdisk_stream *s) +-{ +- td_vbd_t *vbd; +- +- vbd = tapdisk_server_get_vbd(s->id); +- if (vbd) { +- tapdisk_vbd_close_vdi(vbd); +- tapdisk_server_remove_vbd(vbd); +- free((void *)vbd->ring.vstart); +- tapdisk_vbd_free(vbd); +- s->vbd = NULL; +- } +-} +- +-static int +-tapdisk_stream_initialize_requests(struct tapdisk_stream *s) +-{ +- size_t size; +- td_ring_t *ring; +- int err, i, psize; +- +- ring = &s->vbd->ring; +- psize = getpagesize(); +- size = psize * BLKTAP_MMAP_REGION_SIZE; +- +- /* sneaky -- set up ring->vstart so tapdisk_vbd will use our buffers */ +- err = posix_memalign((void **)&ring->vstart, psize, size); +- if (err) { +- fprintf(stderr, "failed to allocate buffers: %d\n", err); +- ring->vstart = 0; +- return err; +- } +- +- for (i = 0; i < MAX_STREAM_REQUESTS; i++) { +- struct tapdisk_stream_request *req = s->requests + i; +- tapdisk_stream_initialize_request(req); +- list_add_tail(&req->next, &s->free_list); +- } +- +- return 0; +-} +- +-static int +-tapdisk_stream_register_enqueue_event(struct tapdisk_stream *s) +-{ +- int err; +- struct tapdisk_stream_poll *p = &s->poll; +- +- err = tapdisk_stream_poll_open(p); +- if (err) +- goto out; +- +- err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD, +- p->pipe[POLL_READ], TV_ZERO, +- tapdisk_stream_enqueue, s); +- if (err < 0) +- goto out; +- +- s->enqueue_event_id = err; +- err = 0; +- +-out: +- if (err) +- fprintf(stderr, "failed to register event: %d\n", err); +- return err; +-} +- +-static void +-tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream *s) +-{ +- if (s->enqueue_event_id) { +- tapdisk_server_unregister_event(s->enqueue_event_id); +- s->enqueue_event_id = 0; +- } +- tapdisk_stream_poll_close(&s->poll); +-} +- +-static inline void +-tapdisk_stream_initialize(struct tapdisk_stream *s) +-{ +- memset(s, 0, sizeof(*s)); +- INIT_LIST_HEAD(&s->free_list); +- INIT_LIST_HEAD(&s->pending_list); +- INIT_LIST_HEAD(&s->completed_list); +-} +- +-static int +-tapdisk_stream_open(struct tapdisk_stream *s, const char *arg) +-{ +- int err; +- +- tapdisk_stream_initialize(s); +- +- err = tapdisk_stream_open_image(s, arg); +- if (err) +- return err; +- +- err = tapdisk_stream_initialize_requests(s); +- if (err) +- return err; +- +- err = tapdisk_stream_register_enqueue_event(s); +- if (err) +- return err; +- +- tapdisk_stream_enqueue(s->enqueue_event_id, +- SCHEDULER_POLL_READ_FD, s); +- +- return 0; +-} +- +-static void +-tapdisk_stream_release(struct tapdisk_stream *s) +-{ +- tapdisk_stream_close_image(s); +- tapdisk_stream_unregister_enqueue_event(s); +-} +- +-static int +-tapdisk_stream_run(struct tapdisk_stream *s) +-{ +- tapdisk_stream_enqueue(s->enqueue_event_id, SCHEDULER_POLL_READ_FD, s); +- tapdisk_server_run(); +- return s->err; +-} +- +-int +-main(int argc, char *argv[]) +-{ +- int c, err, type1; +- const char *arg1 = NULL, *arg2 = NULL; +- const disk_info_t *info; +- const char *path1; +- +- err = 0; +- +- program = basename(argv[0]); +- +- while ((c = getopt(argc, argv, "n:m:h")) != -1) { +- switch (c) { +- case 'n': +- arg1 = optarg; +- break; +- case 'm': +- arg2 = optarg; +- break; +- case 'h': +- usage(stdout); +- return 0; +- default: +- goto fail_usage; +- } +- } +- +- if (!arg1 || !arg2) +- goto fail_usage; +- +- type1 = tapdisk_disktype_parse_params(arg1, &path1); +- if (type1 < 0) +- return type1; +- +- if (type1 != DISK_TYPE_VHD) { +- printf("error: first VDI is not VHD\n"); +- return EINVAL; +- } +- +- err = open_vhd(path1, &vhd1); +- if (err) +- return err; +- +- tapdisk_start_logging("tapdisk-diff", "daemon"); +- +- err = tapdisk_server_initialize(NULL, NULL); +- if (err) +- goto out; +- +- err = tapdisk_stream_open(&stream1, arg1); +- if (err) { +- fprintf(stderr, "Failed to open %s: %s\n", +- arg1, strerror(-err)); +- goto out; +- } +- +- err = tapdisk_stream_open(&stream2, arg2); +- if (err) { +- fprintf(stderr, "Failed to open %s: %s\n", +- arg2, strerror(-err)); +- goto out1; +- } +- +- if (stream1.end != stream2.end) { +- fprintf(stderr, "Image sizes differ: %"PRIu64" != %"PRIu64"\n", +- stream1.end, stream2.end); +- err = EINVAL; +- goto out2; +- } +- +- tapdisk_server_run(); +- +-out2: +- tapdisk_stream_release(&stream2); +-out1: +- tapdisk_stream_release(&stream1); +-out: +- vhd_close(&vhd1); +- tapdisk_stop_logging(); +- +- return err ? : stream1.err; +- +-fail_usage: +- usage(stderr); +- return 1; +-} +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index f877b51a..23ca7289 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -135,8 +135,6 @@ struct td_vbd_handle { + struct list_head failed_requests; + struct list_head completed_requests; + +- td_vbd_request_t request_list[MAX_REQUESTS]; /* XXX */ +- + struct list_head next; + + uint16_t req_timeout; /* in seconds */ diff --git a/SOURCES/0012-blkif-add-a-comment-on-memory-barrier-usage.patch b/SOURCES/0012-blkif-add-a-comment-on-memory-barrier-usage.patch new file mode 100644 index 0000000..f4a2513 --- /dev/null +++ b/SOURCES/0012-blkif-add-a-comment-on-memory-barrier-usage.patch @@ -0,0 +1,28 @@ +From a46de7a8960adb6e5bd74dc5d9326e6db3278b4c Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 27 Feb 2025 17:41:36 +0100 +Subject: [PATCH] blkif: add a comment on memory barrier usage + +The memory barrier is mandatory to see the queued requests from blkif +frontend. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-ctx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/td-ctx.c b/drivers/td-ctx.c +index a1da30f6..8b75dc48 100644 +--- a/drivers/td-ctx.c ++++ b/drivers/td-ctx.c +@@ -220,7 +220,7 @@ __xenio_blkif_get_requests(struct td_xenblkif * const blkif, + ring = &blkif->rings.common; + + rp = ring->sring->req_prod; +- xen_rmb(); /* TODO why? */ ++ xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ + + for (rc = ring->req_cons, n = 0, barrier = false; + rc != rp && n < count && !barrier; diff --git a/SOURCES/0013-tapback-Synchronise-usage-with-code.patch b/SOURCES/0013-tapback-Synchronise-usage-with-code.patch new file mode 100644 index 0000000..b886c4b --- /dev/null +++ b/SOURCES/0013-tapback-Synchronise-usage-with-code.patch @@ -0,0 +1,42 @@ +From 49eb6fc28cb80f7e1f14fb1add074a36b7457476 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Sat, 15 Mar 2025 12:25:11 +0100 +Subject: [PATCH] tapback: Synchronise usage with code + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + tapback/tapback.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/tapback/tapback.c b/tapback/tapback.c +index fe04d177..71dc72f4 100644 +--- a/tapback/tapback.c ++++ b/tapback/tapback.c +@@ -510,10 +510,12 @@ usage(FILE * const stream, const char * const prog) + fprintf(stream, + "usage: %s\n" + "\t[-d|--debug]\n" +- "\t[-h|--help]\n" ++ "\t[-h|--help]\n" + "\t[-v|--verbose]\n" +- "\t[-b]--nobarrier]\n" +- "\t[-n|--name]\n", prog); ++ "\t[-p|--pidfile PIDfile]\n" ++ "\t[-x|--domain domainID]\n" ++ "\t[-b|--nobarrier]\n" ++ "\t[-n|--name backend_name (default: vbd3)]\n", prog); + } + + extern char *optarg; +@@ -612,8 +614,7 @@ int main(int argc, char **argv) + {"name", 0, NULL, 'n'}, + {"pidfile", 0, NULL, 'p'}, + {"domain", 0, NULL, 'x'}, +- {"nobarrier", 0, NULL, 'b'}, +- ++ {"nobarrier", 0, NULL, 'b'}, + }; + int c; + diff --git a/SOURCES/0014-tap-ctl-fix-comments-of-tap_ctl_info-function.patch b/SOURCES/0014-tap-ctl-fix-comments-of-tap_ctl_info-function.patch new file mode 100644 index 0000000..f5d1888 --- /dev/null +++ b/SOURCES/0014-tap-ctl-fix-comments-of-tap_ctl_info-function.patch @@ -0,0 +1,26 @@ +From 862a1d549ac9e5ce8750d31bd1ce3027b050d240 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Sat, 15 Mar 2025 12:26:27 +0100 +Subject: [PATCH] tap-ctl: fix comments of tap_ctl_info function + +No functional change. + +Signed-off-by: Anthoine Bourgeois +--- + include/tap-ctl.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index adf75695..f2db49fe 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -186,7 +186,8 @@ int tap_ctl_disconnect_xenblkif(const pid_t pid, const domid_t domid, + * @param pid the process ID of the tapdisk process + * @param sectors output parameter that receives the number of sectors + * @param sector_size output parameter that receives the size of the sector +- * @param info TODO ? ++ * @param info output parameter that receives the vdisk info flags VDISK_???, ++ * defined in include/xen/interface/io/blkif.h + * @param minor + * + */ diff --git a/SOURCES/0015-tapdisk-fix-hardcoded-array-size-with-a-macro.patch b/SOURCES/0015-tapdisk-fix-hardcoded-array-size-with-a-macro.patch new file mode 100644 index 0000000..50b6065 --- /dev/null +++ b/SOURCES/0015-tapdisk-fix-hardcoded-array-size-with-a-macro.patch @@ -0,0 +1,59 @@ +From a2ff9b3636d8b30040d96d13b1f9a08706804ec3 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 17 Mar 2025 14:48:06 +0100 +Subject: [PATCH] tapdisk: fix hardcoded array size with a macro + +The macro MAX_RING_PAGES is already defined. The comments are +fixed accordingly. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-blkif.h | 6 +++--- + include/tapdisk-message.h | 6 ++++-- + 2 files changed, 7 insertions(+), 5 deletions(-) + +diff --git a/drivers/td-blkif.h b/drivers/td-blkif.h +index b50fa8a5..d96888bd 100644 +--- a/drivers/td-blkif.h ++++ b/drivers/td-blkif.h +@@ -99,10 +99,10 @@ struct td_xenblkif { + blkif_back_rings_t rings; + + /** +- * TODO Why 8 specifically? +- * TODO Do we really need to keep it around? ++ * Grant references of the ring that holds the request descriptors. ++ * See ring_n_pages below to know the number of used refs in this array. + */ +- grant_ref_t ring_ref[8]; ++ grant_ref_t ring_ref[MAX_RING_PAGES]; + + /** + * Number of pages in the ring that holds the request descriptors. +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index 0f73e0c9..fe1b3066 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -34,6 +34,8 @@ + #include + #include + ++#include "xen_blkif.h" ++ + /* + * TODO This is quite small since we don't allow path bigger than 256 chars. If + * we ever increase this, make sure tapdisk_message_t structures are not +@@ -128,9 +130,9 @@ typedef struct tapdisk_message_blkif { + + /** + * Grant references for the shared ring. +- * TODO Why 8 specifically? ++ * See order below to know the number of used refs in this array. + */ +- uint32_t gref[8]; ++ uint32_t gref[MAX_RING_PAGES]; + + /** + * Number of pages in the ring, expressed as a page order. diff --git a/SOURCES/0016-tapdisk-Replace-structure-name-in-sizeof.patch b/SOURCES/0016-tapdisk-Replace-structure-name-in-sizeof.patch new file mode 100644 index 0000000..a10a7b5 --- /dev/null +++ b/SOURCES/0016-tapdisk-Replace-structure-name-in-sizeof.patch @@ -0,0 +1,28 @@ +From 54c7fafd406c72c9f56893bc5e82af311b29de80 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 18 Mar 2025 19:28:22 +0100 +Subject: [PATCH] tapdisk: Replace structure name in sizeof + +xenio_blkif_req structure doesn't exist anymore, blkif_request_t is the +right structure here. Hopefully a pointer is a pointer in any case. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index c35b02c6..7b421d87 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -943,7 +943,7 @@ tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif) + } + + td_blkif->reqs_free = +- malloc(td_blkif->ring_size * sizeof(struct xenio_blkif_req *)); ++ malloc(td_blkif->ring_size * sizeof(struct blkif_request_t *)); + if (!td_blkif->reqs_free) { + err = -errno; + goto fail; diff --git a/SOURCES/0017-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch b/SOURCES/0017-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch new file mode 100644 index 0000000..7430dd7 --- /dev/null +++ b/SOURCES/0017-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch @@ -0,0 +1,34 @@ +From ea55c11cee3c9e77349c0fa7ea19e7e8202634b9 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 14 Mar 2025 15:10:25 +0100 +Subject: [PATCH] tapdisk: check if RD macros are defined in ring.h (since + xen-4.18) + +If the RD* macros are defined, use the upstream definitions introduced +in xen-4.18. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-blktap.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/tapdisk-blktap.c b/drivers/tapdisk-blktap.c +index d706724c..cf72b095 100644 +--- a/drivers/tapdisk-blktap.c ++++ b/drivers/tapdisk-blktap.c +@@ -61,11 +61,13 @@ + #define WARN(_f, _a...) tlog_syslog(TLOG_WARN, "WARNING: "_f "in %s:%d", \ + ##_a, __func__, __LINE__) + ++#ifndef __RD2 + #define __RD2(_x) (((_x) & 0x00000002) ? 0x2 : ((_x) & 0x1)) + #define __RD4(_x) (((_x) & 0x0000000c) ? __RD2((_x)>>2)<<2 : __RD2(_x)) + #define __RD8(_x) (((_x) & 0x000000f0) ? __RD4((_x)>>4)<<4 : __RD4(_x)) + #define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x)>>8)<<8 : __RD8(_x)) + #define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x)>>16)<<16 : __RD16(_x)) ++#endif + + #define BLKTAP_RD32(_n) __RD32(_n) + #define BLKTAP_RING_SIZE __BLKTAP_RING_SIZE(PAGE_SIZE) diff --git a/SOURCES/0018-tapdisk-Fix-a-typo-in-util.h-header.patch b/SOURCES/0018-tapdisk-Fix-a-typo-in-util.h-header.patch new file mode 100644 index 0000000..73ae479 --- /dev/null +++ b/SOURCES/0018-tapdisk-Fix-a-typo-in-util.h-header.patch @@ -0,0 +1,23 @@ +From b19df24862e0e322a10a13b186425a73fdfb712a Mon Sep 17 00:00:00 2001 +From: Emmanuel Varagnat +Date: Thu, 8 Jan 2026 19:06:03 +0100 +Subject: [PATCH] tapdisk: Fix a typo in util.h header + +Signed-off-by: Emmanuel Varagnat +--- + include/util.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/util.h b/include/util.h +index e2529a51..06cce42d 100644 +--- a/include/util.h ++++ b/include/util.h +@@ -29,7 +29,7 @@ + */ + + #ifndef __TAPDISK_UTIL_H__ +-#define __TAPDISK_UTIL_H_ ++#define __TAPDISK_UTIL_H__ + + #include + #include diff --git a/SOURCES/0019-tapdisk-rename-field-pool-to-pool_name.patch b/SOURCES/0019-tapdisk-rename-field-pool-to-pool_name.patch new file mode 100644 index 0000000..bd9890f --- /dev/null +++ b/SOURCES/0019-tapdisk-rename-field-pool-to-pool_name.patch @@ -0,0 +1,212 @@ +From e7bebd4424888e750116f32952e6b9c726558482 Mon Sep 17 00:00:00 2001 +From: Emmanuel Varagnat +Date: Mon, 5 Jan 2026 16:17:57 +0100 +Subject: [PATCH] tapdisk: rename field pool to pool_name + +Signed-off-by: Emmanuel Varagnat +--- + control/tap-ctl-xen.c | 12 ++++++------ + drivers/tapdisk-control.c | 14 +++++++------- + drivers/td-ctx.c | 22 +++++++++++----------- + drivers/td-ctx.h | 4 ++-- + drivers/td-stats.c | 2 +- + include/tapdisk-message.h | 4 ++-- + 6 files changed, 29 insertions(+), 29 deletions(-) + +diff --git a/control/tap-ctl-xen.c b/control/tap-ctl-xen.c +index f09b76ea..14c60ec4 100644 +--- a/control/tap-ctl-xen.c ++++ b/control/tap-ctl-xen.c +@@ -53,7 +53,7 @@ int + tap_ctl_connect_xenblkif(const pid_t pid, const domid_t domid, const int devid, int poll_duration, + int poll_idle_threshold, + const grant_ref_t * grefs, const int order, const evtchn_port_t port, +- int proto, const char *pool, const int minor) ++ int proto, const char *pool_name, const int minor) + { + tapdisk_message_t message; + int i, err; +@@ -71,14 +71,14 @@ tap_ctl_connect_xenblkif(const pid_t pid, const domid_t domid, const int devid, + message.u.blkif.proto = proto; + message.u.blkif.poll_duration = poll_duration; + message.u.blkif.poll_idle_threshold = poll_idle_threshold; +- if (pool) { +- if (unlikely(strlen(pool) > (sizeof(message.u.blkif.pool) - 1))) { +- EPRINTF("pool name too long: %s\n", pool); ++ if (pool_name) { ++ if (unlikely(strlen(pool_name) > (sizeof(message.u.blkif.pool_name) - 1))) { ++ EPRINTF("pool name too long: %s\n", pool_name); + return -ENAMETOOLONG; + } +- safe_strncpy(message.u.blkif.pool, pool, sizeof(message.u.blkif.pool)); ++ safe_strncpy(message.u.blkif.pool_name, pool_name, sizeof(message.u.blkif.pool_name)); + } else { +- message.u.blkif.pool[0] = 0; ++ message.u.blkif.pool_name[0] = '\0'; + } + + err = tap_ctl_connect_send_and_receive(pid, &message, NULL); +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 91ffe097..39e7ab0e 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1201,7 +1201,7 @@ tapdisk_control_xenblkif_connect( + tapdisk_message_blkif_t *blkif; + + td_vbd_t *vbd = NULL; +- const char *pool; ++ const char *pool_name; + size_t len; + int err; + int minor = -1; +@@ -1219,20 +1219,20 @@ tapdisk_control_xenblkif_connect( + } + + blkif = &request->u.blkif; +- len = strnlen(blkif->pool, sizeof(blkif->pool)); ++ len = strnlen(blkif->pool_name, sizeof(blkif->pool_name)); + if (!len) +- pool = NULL; +- else if (len >= sizeof(blkif->pool)) { ++ pool_name = NULL; ++ else if (len >= sizeof(blkif->pool_name)) { + err = -EINVAL; + goto out; + } else +- pool = blkif->pool; ++ pool_name = blkif->pool_name; + + DPRINTF("connecting VBD %d domid=%d, devid=%d, pool %s, evt %d, poll duration %d, poll idle threshold %d\n", +- vbd->uuid, blkif->domid, blkif->devid, pool, blkif->port, blkif->poll_duration, blkif->poll_idle_threshold); ++ vbd->uuid, blkif->domid, blkif->devid, pool_name, blkif->port, blkif->poll_duration, blkif->poll_idle_threshold); + + err = tapdisk_xenblkif_connect(blkif->domid, blkif->devid, blkif->gref, +- blkif->order, blkif->port, blkif->proto, blkif->poll_duration, blkif->poll_idle_threshold, pool, vbd); ++ blkif->order, blkif->port, blkif->proto, blkif->poll_duration, blkif->poll_idle_threshold, pool_name, vbd); + + out: + response->cookie = request->cookie; +diff --git a/drivers/td-ctx.c b/drivers/td-ctx.c +index 8b75dc48..e344a62f 100644 +--- a/drivers/td-ctx.c ++++ b/drivers/td-ctx.c +@@ -407,13 +407,13 @@ tapdisk_xenio_ctx_ring_event(event_id_t id __attribute__((unused)), + * TODO The pool is ignored, we always open the default pool. + */ + static inline int +-tapdisk_xenio_ctx_open(const char *pool) ++tapdisk_xenio_ctx_open(const char *pool_name) + { + struct td_xenio_ctx *ctx; + int fd, err; + + /* zero-length pool names are not allowed */ +- if (pool && !strlen(pool)) ++ if (pool_name && !strlen(pool_name)) + return -EINVAL; + + ctx = calloc(1, sizeof(*ctx)); +@@ -425,8 +425,8 @@ tapdisk_xenio_ctx_open(const char *pool) + + ctx->ring_event = -1; /* TODO is there a special value? */ + ctx->gntdev_fd = -1; +- ctx->pool = TD_XENBLKIF_DEFAULT_POOL; +- INIT_LIST_HEAD(&ctx->blkifs); ++ ctx->pool_name = TD_XENBLKIF_DEFAULT_POOL; ++ INIT_LIST_HEAD(&ctx->blkifs); + list_add(&ctx->entry, &_td_xenio_ctxs); + + ctx->gntdev_fd = open("/dev/xen/gntdev", O_NONBLOCK); +@@ -483,14 +483,14 @@ fail: + * against the default pool. Note that NULL is valid pool name value. + */ + static inline int +-__td_xenio_ctx_match(struct td_xenio_ctx * ctx, const char *pool) ++__td_xenio_ctx_match(struct td_xenio_ctx * ctx, const char *pool_name) + { +- if (unlikely(!pool)) { ++ if (unlikely(!pool_name)) { + assert(TD_XENBLKIF_DEFAULT_POOL); +- return !strcmp(ctx->pool, TD_XENBLKIF_DEFAULT_POOL); ++ return !strcmp(ctx->pool_name, TD_XENBLKIF_DEFAULT_POOL); + } + +- return !strcmp(ctx->pool, pool); ++ return !strcmp(ctx->pool_name, pool_name); + } + + #define tapdisk_xenio_find_ctx(_ctx, _cond) \ +@@ -507,19 +507,19 @@ __td_xenio_ctx_match(struct td_xenio_ctx * ctx, const char *pool) + } while (0) + + int +-tapdisk_xenio_ctx_get(const char *pool, struct td_xenio_ctx ** _ctx) ++tapdisk_xenio_ctx_get(const char *pool_name, struct td_xenio_ctx ** _ctx) + { + struct td_xenio_ctx *ctx; + int err = 0; + + do { +- tapdisk_xenio_find_ctx(ctx, __td_xenio_ctx_match(ctx, pool)); ++ tapdisk_xenio_find_ctx(ctx, __td_xenio_ctx_match(ctx, pool_name)); + if (ctx) { + *_ctx = ctx; + return 0; + } + +- err = tapdisk_xenio_ctx_open(pool); ++ err = tapdisk_xenio_ctx_open(pool_name); + } while (!err); + + return err; +diff --git a/drivers/td-ctx.h b/drivers/td-ctx.h +index c03912e9..b58edbbe 100644 +--- a/drivers/td-ctx.h ++++ b/drivers/td-ctx.h +@@ -50,7 +50,7 @@ + * unnecessary. + */ + struct td_xenio_ctx { +- char *pool; /* TODO rename to pool_name */ ++ char *pool_name; + + /** + * Handle to the grant table driver. +@@ -88,7 +88,7 @@ struct td_xenio_ctx { + * @returns 0 on success, -errno on error + */ + int +-tapdisk_xenio_ctx_get(const char *pool, struct td_xenio_ctx ** _ctx); ++tapdisk_xenio_ctx_get(const char *pool_name, struct td_xenio_ctx ** _ctx); + + /** + * Releases the pool, only if there is no block interface using it. +diff --git a/drivers/td-stats.c b/drivers/td-stats.c +index e75752d8..f8982624 100644 +--- a/drivers/td-stats.c ++++ b/drivers/td-stats.c +@@ -42,7 +42,7 @@ tapdisk_xenblkif_stats(struct td_xenblkif * blkif, td_stats_t * st) + ASSERT(st); + ASSERT(blkif->ctx); + +- tapdisk_stats_field(st, "pool", "s", blkif->ctx->pool); ++ tapdisk_stats_field(st, "pool", "s", blkif->ctx->pool_name); + tapdisk_stats_field(st, "domid", "d", blkif->domid); + tapdisk_stats_field(st, "devid", "d", blkif->devid); + +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index fe1b3066..7c54fb3b 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -146,9 +146,9 @@ typedef struct tapdisk_message_blkif { + uint32_t proto; + + /** +- * TODO Page pool? Can be NULL. ++ * Page pool name? Can be empty (ie "") + */ +- char pool[TAPDISK_MESSAGE_STRING_LENGTH]; ++ char pool_name[TAPDISK_MESSAGE_STRING_LENGTH]; + + /** + * The event channel port. diff --git a/SOURCES/0020-td-req-remove-unused-field-gref.patch b/SOURCES/0020-td-req-remove-unused-field-gref.patch new file mode 100644 index 0000000..880144a --- /dev/null +++ b/SOURCES/0020-td-req-remove-unused-field-gref.patch @@ -0,0 +1,37 @@ +From a4a059f2ed4b648ff06902f58d98778265423a56 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 27 Nov 2025 11:16:51 +0100 +Subject: [PATCH] td-req: remove unused field gref + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 1 - + drivers/td-req.h | 1 - + 2 files changed, 2 deletions(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 7b421d87..52b014ba 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -645,7 +645,6 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + + for (i = 0; i < req->msg.nr_segments; i++) { + struct blkif_request_segment *seg = &req->msg.seg[i]; +- req->gref[i] = seg->gref; + + /* + * Note that first and last may be equal, which means only one sector +diff --git a/drivers/td-req.h b/drivers/td-req.h +index dad40f29..25eeb503 100644 +--- a/drivers/td-req.h ++++ b/drivers/td-req.h +@@ -82,7 +82,6 @@ struct td_xenblkif_req { + */ + struct td_iovec iov[BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST]; + +- grant_ref_t gref[BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST]; + int prot; + + struct gntdev_grant_copy_segment diff --git a/SOURCES/0021-td-req-rename-tapreq-as-req.patch b/SOURCES/0021-td-req-rename-tapreq-as-req.patch new file mode 100644 index 0000000..c8e1773 --- /dev/null +++ b/SOURCES/0021-td-req-rename-tapreq-as-req.patch @@ -0,0 +1,433 @@ +From 244d446e5fc4250dbefc20f94481469dec6a16dd Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 28 Nov 2025 12:45:43 +0100 +Subject: [PATCH] td-req: rename tapreq as req + +Remove the associated TODO statements. + +No functional change expected. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 150 +++++++++++++++++++++++------------------------ + 1 file changed, 75 insertions(+), 75 deletions(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 52b014ba..b271d3bf 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -184,28 +184,28 @@ td_xenblkif_bufcache_put(struct td_xenblkif * const blkif, void *buf) + * Puts the request back to the free list of this block interface. + * + * @param blkif the block interface +- * @param tapreq the request to give back ++ * @param req the request to give back + */ + static void + tapdisk_xenblkif_free_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req * const tapreq) ++ struct td_xenblkif_req * const req) + { + int put_bufcache; + + ASSERT(blkif); +- ASSERT(tapreq); ++ ASSERT(req); + ASSERT(blkif->n_reqs_free < blkif->ring_size); + +- put_bufcache = tapreq->msg.nr_segments != 0; ++ put_bufcache = req->msg.nr_segments != 0; + + #ifdef DEBUG +- memset(&tapreq->msg, BLKIF_MSG_POISON, sizeof(tapreq->msg)); ++ memset(&req->msg, BLKIF_MSG_POISON, sizeof(req->msg)); + #endif + +- blkif->reqs_free[blkif->ring_size - (++blkif->n_reqs_free)] = &tapreq->msg; ++ blkif->reqs_free[blkif->ring_size - (++blkif->n_reqs_free)] = &req->msg; + + if (likely(put_bufcache)) +- td_xenblkif_bufcache_put(blkif, tapreq->vma); ++ td_xenblkif_bufcache_put(blkif, req->vma); + } + + /** +@@ -359,7 +359,7 @@ blkif_rq_data(blkif_request_t const * const msg) + + static int + guest_copy2(struct td_xenblkif * const blkif, +- struct td_xenblkif_req * const tapreq /* TODO rename to req */) { ++ struct td_xenblkif_req * const req) { + + int i = 0; + long err = 0; +@@ -367,18 +367,18 @@ guest_copy2(struct td_xenblkif * const blkif, + + ASSERT(blkif); + ASSERT(blkif->ctx); +- ASSERT(tapreq); +- ASSERT(blkif_rq_data(&tapreq->msg)); +- ASSERT(tapreq->msg.nr_segments > 0); +- ASSERT(tapreq->msg.nr_segments <= ARRAY_SIZE(tapreq->gcopy_segs)); +- +- for (i = 0; i < tapreq->msg.nr_segments; i++) { +- struct blkif_request_segment *blkif_seg = &tapreq->msg.seg[i]; +- struct gntdev_grant_copy_segment *gcopy_seg = &tapreq->gcopy_segs[i]; ++ ASSERT(req); ++ ASSERT(blkif_rq_data(&req->msg)); ++ ASSERT(req->msg.nr_segments > 0); ++ ASSERT(req->msg.nr_segments <= ARRAY_SIZE(req->gcopy_segs)); ++ ++ for (i = 0; i < req->msg.nr_segments; i++) { ++ struct blkif_request_segment *blkif_seg = &req->msg.seg[i]; ++ struct gntdev_grant_copy_segment *gcopy_seg = &req->gcopy_segs[i]; + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +- if (blkif_rq_wr(&tapreq->msg)) { ++ if (blkif_rq_wr(&req->msg)) { + /* copy from guest */ +- gcopy_seg->dest.virt = tapreq->vma + (i << PAGE_SHIFT) ++ gcopy_seg->dest.virt = req->vma + (i << PAGE_SHIFT) + + (blkif_seg->first_sect << SECTOR_SHIFT); + gcopy_seg->source.foreign.ref = blkif_seg->gref; + gcopy_seg->source.foreign.offset = blkif_seg->first_sect << SECTOR_SHIFT; +@@ -386,7 +386,7 @@ guest_copy2(struct td_xenblkif * const blkif, + gcopy_seg->flags = GNTCOPY_source_gref; + } else { + /* copy to guest */ +- gcopy_seg->source.virt = tapreq->vma + (i << PAGE_SHIFT) ++ gcopy_seg->source.virt = req->vma + (i << PAGE_SHIFT) + + (blkif_seg->first_sect << SECTOR_SHIFT); + gcopy_seg->dest.foreign.ref = blkif_seg->gref; + gcopy_seg->dest.foreign.offset = blkif_seg->first_sect << SECTOR_SHIFT; +@@ -400,7 +400,7 @@ guest_copy2(struct td_xenblkif * const blkif, + << SECTOR_SHIFT; + } + #else +- gcopy_seg->iov.iov_base = tapreq->vma + (i << PAGE_SHIFT) ++ gcopy_seg->iov.iov_base = req->vma + (i << PAGE_SHIFT) + + (blkif_seg->first_sect << SECTOR_SHIFT); + gcopy_seg->iov.iov_len = (blkif_seg->last_sect + - blkif_seg->first_sect +@@ -410,23 +410,23 @@ guest_copy2(struct td_xenblkif * const blkif, + gcopy_seg->offset = blkif_seg->first_sect << SECTOR_SHIFT; + } + +- gcopy.dir = blkif_rq_wr(&tapreq->msg); ++ gcopy.dir = blkif_rq_wr(&req->msg); + gcopy.domid = blkif->domid; + #endif +- gcopy.count = tapreq->msg.nr_segments; +- gcopy.segments = tapreq->gcopy_segs; ++ gcopy.count = req->msg.nr_segments; ++ gcopy.segments = req->gcopy_segs; + + err = -ioctl(blkif->ctx->gntdev_fd, IOCTL_GNTDEV_GRANT_COPY, &gcopy); + if (err) { + err = -errno; + RING_ERR(blkif, "failed to grant-copy request %"PRIu64" " +- "(%d segments): %s\n", tapreq->msg.id, +- tapreq->msg.nr_segments, strerror(-err)); ++ "(%d segments): %s\n", req->msg.id, ++ req->msg.nr_segments, strerror(-err)); + goto out; + } + +- for (i = 0; i < tapreq->msg.nr_segments; i++) { +- struct gntdev_grant_copy_segment *gcopy_seg = &tapreq->gcopy_segs[i]; ++ for (i = 0; i < req->msg.nr_segments; i++) { ++ struct gntdev_grant_copy_segment *gcopy_seg = &req->gcopy_segs[i]; + if (gcopy_seg->status != GNTST_okay) { + /* + * TODO use gnttabop_error for reporting errors, defined in +@@ -434,7 +434,7 @@ guest_copy2(struct td_xenblkif * const blkif, + * user space) + */ + RING_ERR(blkif, "req %lu: failed to grant-copy segment %d: %d\n", +- tapreq->msg.id, i, gcopy_seg->status); ++ req->msg.id, i, gcopy_seg->status); + err = -EIO; + goto out; + } +@@ -451,13 +451,13 @@ out: + * any more. + * + * @blkif the VBD the request belongs belongs to +- * @tapreq the request to complete TODO rename to req ++ * @req the request to complete + * @error completion status of the request + * @final controls whether the other end should be notified + */ + void + tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req* tapreq, int err, const int final) ++ struct td_xenblkif_req* req, int err, const int final) + { + int _err; + long long *max = NULL, *sum = NULL, *cnt = NULL; +@@ -466,13 +466,13 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + uint64_t *ticks = NULL; + + ASSERT(blkif); +- ASSERT(tapreq); ++ ASSERT(req); + ASSERT(depth >= 0); + + depth++; + + processing_barrier_message = +- tapreq->msg.operation == BLKIF_OP_WRITE_BARRIER; ++ req->msg.operation == BLKIF_OP_WRITE_BARRIER; + + /* + * If a barrier request completes, check whether it's an I/O completion +@@ -484,8 +484,8 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + * called again passing the barrier request. + */ + if (unlikely(processing_barrier_message)) { +- ASSERT(blkif->barrier.msg == &tapreq->msg); +- if (tapreq->msg.nr_segments && !blkif->barrier.io_done) { ++ ASSERT(blkif->barrier.msg == &req->msg); ++ if (req->msg.nr_segments && !blkif->barrier.io_done) { + blkif->barrier.io_err = err; + blkif->barrier.io_done = true; + } +@@ -494,7 +494,7 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + } + + if (likely(!blkif->dead)) { +- if (blkif_rq_rd(&tapreq->msg)) { ++ if (blkif_rq_rd(&req->msg)) { + /* + * TODO stats should be collected after grant-copy for better + * accuracy +@@ -507,14 +507,14 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + blkif->vbd_stats.stats->read_reqs_completed++; + ticks = &blkif->vbd_stats.stats->read_total_ticks; + if (likely(!err)) { +- _err = guest_copy2(blkif, tapreq); ++ _err = guest_copy2(blkif, req); + if (unlikely(_err)) { + err = _err; + RING_ERR(blkif, "req %lu: failed to copy from/to guest: " +- "%s\n", tapreq->msg.id, strerror(-err)); ++ "%s\n", req->msg.id, strerror(-err)); + } + } +- } else if (blkif_rq_wr(&tapreq->msg)) { ++ } else if (blkif_rq_wr(&req->msg)) { + if (likely(blkif->stats.xenvbd)) { + cnt = &blkif->stats.xenvbd->st_wr_cnt; + sum = &blkif->stats.xenvbd->st_wr_sum_usecs; +@@ -528,7 +528,7 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + struct timeval now; + long long interval; + gettimeofday(&now, NULL); +- interval = timeval_to_us(&now) - timeval_to_us(&tapreq->ts); ++ interval = timeval_to_us(&now) - timeval_to_us(&req->ts); + *ticks += interval; + if (interval > *max) + *max = interval; +@@ -542,10 +542,10 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + else + _err = BLKIF_RSP_ERROR; + +- xenio_blkif_put_response(blkif, tapreq, _err, final); ++ xenio_blkif_put_response(blkif, req, _err, final); + } + +- tapdisk_xenblkif_free_request(blkif, tapreq); ++ tapdisk_xenblkif_free_request(blkif, req); + + blkif->stats.reqs.out++; + if (final) +@@ -601,13 +601,13 @@ static inline void + __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, + const int error, void * const token, const int final) + { +- struct td_xenblkif_req *tapreq; ++ struct td_xenblkif_req *req; + struct td_xenblkif * const blkif = token; + + ASSERT(vreq); + ASSERT(blkif); + +- tapreq = container_of(vreq, struct td_xenblkif_req, vreq); ++ req = container_of(vreq, struct td_xenblkif_req, vreq); + + if (error) { + if (likely(!blkif->dead)) { +@@ -616,7 +616,7 @@ __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, + } + } + +- tapdisk_xenblkif_complete_request(blkif, tapreq, error, final); ++ tapdisk_xenblkif_complete_request(blkif, req, error, final); + } + + +@@ -659,7 +659,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + } + + /* +- * Vectorises the request: creates the struct iovec (in tapreq->iov) that ++ * Vectorises the request: creates the struct iovec (in req->iov) that + * describes each segment to be transferred. Also, merges consecutive + * segments. + * +@@ -736,32 +736,32 @@ out: + * processing. + * + * @param blkif the block interface +- * @param tapreq the request to prepare TODO rename to req ++ * @param req the request to prepare + * @returns 0 on success + * + * XXX only called by tapdisk_xenblkif_queue_request + */ + static inline int + tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req * const tapreq) ++ struct td_xenblkif_req * const req) + { + int err = 0; + td_vbd_request_t *vreq; + +- ASSERT(tapreq); ++ ASSERT(req); + +- vreq = &tapreq->vreq; ++ vreq = &req->vreq; + ASSERT(vreq); + memset(vreq, 0, sizeof(*vreq)); + +- tapreq->vma = NULL; +- switch (tapreq->msg.operation) { ++ req->vma = NULL; ++ switch (req->msg.operation) { + case BLKIF_OP_READ: + if (likely(blkif->stats.xenvbd)) + blkif->stats.xenvbd->st_rd_req++; + if (likely(blkif->vbd_stats.stats)) + blkif->vbd_stats.stats->read_reqs_submitted++; +- tapreq->prot = PROT_WRITE; ++ req->prot = PROT_WRITE; + vreq->op = TD_OP_READ; + break; + case BLKIF_OP_WRITE: +@@ -770,32 +770,32 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, + blkif->stats.xenvbd->st_wr_req++; + if (likely(blkif->vbd_stats.stats)) + blkif->vbd_stats.stats->write_reqs_submitted++; +- tapreq->prot = PROT_READ; ++ req->prot = PROT_READ; + vreq->op = TD_OP_WRITE; + break; + default: + RING_ERR(blkif, "req %lu: invalid request type %d\n", +- tapreq->msg.id, tapreq->msg.operation); ++ req->msg.id, req->msg.operation); + err = EOPNOTSUPP; + goto out; + } + /* Timestamp before the requests leave the blkif layer */ +- gettimeofday(&tapreq->ts, NULL); ++ gettimeofday(&req->ts, NULL); + + /* + * Check that the number of segments is sane. + */ +- if (unlikely((tapreq->msg.nr_segments == 0 && +- tapreq->msg.operation != BLKIF_OP_WRITE_BARRIER) || +- tapreq->msg.nr_segments > BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST)) { ++ if (unlikely((req->msg.nr_segments == 0 && ++ req->msg.operation != BLKIF_OP_WRITE_BARRIER) || ++ req->msg.nr_segments > BLKIF_MAX_BUFFER_SEGMENTS_PER_REQUEST)) { + RING_ERR(blkif, "req %lu: bad number of segments in request (%d)\n", +- tapreq->msg.id, tapreq->msg.nr_segments); ++ req->msg.id, req->msg.nr_segments); + err = EINVAL; + goto out; + } + +- if (likely(tapreq->msg.nr_segments)) +- err = tapdisk_xenblkif_parse_request(blkif, tapreq); ++ if (likely(req->msg.nr_segments)) ++ err = tapdisk_xenblkif_parse_request(blkif, req); + /* + * If we only got one request from the ring and that was a barrier one, + * check whether the barrier requests completion conditions are satisfied +@@ -820,32 +820,32 @@ out: + * + * @param blkif the block interface + * @param msg the ring request +- * @param tapreq the intermediate request TODO rename to req ++ * @param req the intermediate request + * + * TODO don't really need to supply the ring request since it's either way +- * contained in the tapreq ++ * contained in the req + * + * XXX only called by tapdisk_xenblkif_queue_requests + */ + static inline int + tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif, +- blkif_request_t *msg, struct td_xenblkif_req *tapreq) ++ blkif_request_t *msg, struct td_xenblkif_req *req) + { + int err; + int queue_request; + + ASSERT(blkif); + ASSERT(msg); +- ASSERT(tapreq); ++ ASSERT(req); + +- queue_request = tapreq->msg.nr_segments != 0; ++ queue_request = req->msg.nr_segments != 0; + + /* +- * Do not use tapreq after tapdisk_xenblkif_make_vbd_request +- * because this function can release tapreq->msg and reinsert it ++ * Do not use req after tapdisk_xenblkif_make_vbd_request ++ * because this function can release req->msg and reinsert it + * in the reqs_free array. + */ +- err = tapdisk_xenblkif_make_vbd_request(blkif, tapreq); ++ err = tapdisk_xenblkif_make_vbd_request(blkif, req); + if (unlikely(err)) { + /* TODO log error */ + blkif->stats.errors.map++; +@@ -853,7 +853,7 @@ tapdisk_xenblkif_queue_request(struct td_xenblkif * const blkif, + } + + if (likely(queue_request)) { +- err = tapdisk_vbd_queue_request(blkif->vbd, &tapreq->vreq); ++ err = tapdisk_vbd_queue_request(blkif->vbd, &req->vreq); + if (unlikely(err)) { + /* TODO log error */ + blkif->stats.errors.vbd++; +@@ -879,19 +879,19 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, + + for (i = 0; i < nr_reqs; i++) { /* for each request in the ring... */ + blkif_request_t *msg = reqs[i]; +- struct td_xenblkif_req *tapreq; ++ struct td_xenblkif_req *req; + + ASSERT(msg); + +- tapreq = msg_to_tapreq(msg); ++ req = msg_to_tapreq(msg); + +- ASSERT(tapreq); ++ ASSERT(req); + +- err = tapdisk_xenblkif_queue_request(blkif, msg, tapreq); ++ err = tapdisk_xenblkif_queue_request(blkif, msg, req); + if (err) { + /* TODO log error */ + nr_errors++; +- tapdisk_xenblkif_complete_request(blkif, tapreq, err, 1); ++ tapdisk_xenblkif_complete_request(blkif, req, err, 1); + } + } + diff --git a/SOURCES/0022-td-req-remove-old-code.patch b/SOURCES/0022-td-req-remove-old-code.patch new file mode 100644 index 0000000..4baec66 --- /dev/null +++ b/SOURCES/0022-td-req-remove-old-code.patch @@ -0,0 +1,45 @@ +From e5007807b3dfd1629fafa8a3952794cf3841f874 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 28 Nov 2025 12:44:55 +0100 +Subject: [PATCH] td-req: remove old code + +Remove code running on linux kernel 4.4 or below. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 15 --------------- + 1 file changed, 15 deletions(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index b271d3bf..68760dd0 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -375,7 +375,6 @@ guest_copy2(struct td_xenblkif * const blkif, + for (i = 0; i < req->msg.nr_segments; i++) { + struct blkif_request_segment *blkif_seg = &req->msg.seg[i]; + struct gntdev_grant_copy_segment *gcopy_seg = &req->gcopy_segs[i]; +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + if (blkif_rq_wr(&req->msg)) { + /* copy from guest */ + gcopy_seg->dest.virt = req->vma + (i << PAGE_SHIFT) +@@ -399,20 +398,6 @@ guest_copy2(struct td_xenblkif * const blkif, + + 1) + << SECTOR_SHIFT; + } +-#else +- gcopy_seg->iov.iov_base = req->vma + (i << PAGE_SHIFT) +- + (blkif_seg->first_sect << SECTOR_SHIFT); +- gcopy_seg->iov.iov_len = (blkif_seg->last_sect +- - blkif_seg->first_sect +- + 1) +- << SECTOR_SHIFT; +- gcopy_seg->ref = blkif_seg->gref; +- gcopy_seg->offset = blkif_seg->first_sect << SECTOR_SHIFT; +- } +- +- gcopy.dir = blkif_rq_wr(&req->msg); +- gcopy.domid = blkif->domid; +-#endif + gcopy.count = req->msg.nr_segments; + gcopy.segments = req->gcopy_segs; + diff --git a/SOURCES/0023-td-req-fix-typo-in-a-comment.patch b/SOURCES/0023-td-req-fix-typo-in-a-comment.patch new file mode 100644 index 0000000..e2b8ec2 --- /dev/null +++ b/SOURCES/0023-td-req-fix-typo-in-a-comment.patch @@ -0,0 +1,27 @@ +From 1660d6c774c44398512c8a84882faa1652bd001a Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 17 Mar 2026 16:52:55 +0100 +Subject: [PATCH] td-req: fix typo in a comment + +Fix 'fist_sect' to 'first_sect'. + +No functional change. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-req.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 68760dd0..028e4bf6 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -669,7 +669,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + iov++; + iov->base = next; + iov->secs = size; +- } else /* The "else" is true if fist_sect is 0. */ ++ } else /* The "else" is true if first_sect is 0. */ + iov->secs += size; + + last = iov->base + (iov->secs << SECTOR_SHIFT); diff --git a/SOURCES/0024-libqcow2-manage-libqcow2-sources-import.patch b/SOURCES/0024-libqcow2-manage-libqcow2-sources-import.patch new file mode 100644 index 0000000..3f9acc8 --- /dev/null +++ b/SOURCES/0024-libqcow2-manage-libqcow2-sources-import.patch @@ -0,0 +1,413 @@ +From 464fc64b4b228412e3a0314a32ded34db4a0840a Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:16 +0100 +Subject: [PATCH] libqcow2: manage libqcow2 sources import + +This commit includes a list of files imported from qemu to build +the qcow2 library for tapdisk. +A small script helps to import files and diff them to ease the +sources management. + +Signed-off-by: Anthoine Bourgeois +--- + manage-qemu-sources.sh | 74 ++++++++++ + qemu-files.lst | 309 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 383 insertions(+) + create mode 100755 manage-qemu-sources.sh + create mode 100644 qemu-files.lst + +diff --git a/manage-qemu-sources.sh b/manage-qemu-sources.sh +new file mode 100755 +index 00000000..7f6babc8 +--- /dev/null ++++ b/manage-qemu-sources.sh +@@ -0,0 +1,74 @@ ++#!/bin/bash ++ ++usage_function () { ++ echo "Usage: $0 qemu_directory [diff|sync]" ++ exit 0 ++} ++ ++if [ $# -ne 2 ]; then ++ usage_function ++fi ++ ++qemudir=${1} ++cmd=${2} ++ ++if [ ${cmd} != "diff" -a ${cmd} != "sync" ]; then ++ echo "Wring command: choose 'diff' or 'sync'" ++ usage_function ++fi ++ ++diff_function () { ++ if [ ! -f ${qemudir}/${src_prefix}/${1} ]; then ++ echo "${qemudir}/${src_prefix}/${1} doesn't exist." ++ exit 0 ++ fi ++ if [ ! -f ${dst_prefix}/${1} ]; then ++ echo "${dst_prefix}/${1} doesn't exist." ++ exit 0 ++ fi ++ diff -q ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1} ++ if [ $? -ne 0 ]; then ++ diff -Npur ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1} ++ fi ++} ++ ++sync_warning () { ++ echo "This command will erase all the modifications you did" ++ echo "in all the qemu files you imported." ++ echo -n "Are you sure you want to sync all the files ? (N/y) " ++ read ok ++ if [ "$ok" == "y" ]; then ++ echo "Fine! Start in:" ++ for i in `seq 5`; do ++ echo -n "$((6 - i)) " ++ sleep 1 ++ done ++ echo "Let's go!" ++ else ++ echo "Abort!" ++ exit 0 ++ fi ++} ++ ++sync_function () { ++ echo "cp ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1}" ++ mkdir -p $(dirname "${dst_prefix}/${1}") ++ cp -f "${qemudir}/${src_prefix}/${1}" "${dst_prefix}/${1}" ++} ++ ++if [ ${cmd} == "sync" ]; then ++ sync_warning ++fi ++ ++while read f; do ++ if [ ${f:0:1} == '#' ]; then ++ src_prefix=`echo ${f:1} | awk -F ':' '{print $1}'` ++ dst_prefix=`echo ${f:1} | awk -F ':' '{print $2}'` ++ continue ++ fi ++ if [ ${cmd} == "diff" ]; then ++ diff_function ${f} ${src_prefix} ${dst_prefix} ++ elif [ ${cmd} == "sync" ]; then ++ sync_function ${f} ${src_prefix} ${dst_prefix} ++ fi ++done < qemu-files.lst +diff --git a/qemu-files.lst b/qemu-files.lst +new file mode 100644 +index 00000000..441c2180 +--- /dev/null ++++ b/qemu-files.lst +@@ -0,0 +1,309 @@ ++#.:. ++include/authz/base.h ++include/block/accounting.h ++include/block/aio.h ++include/block/aio-wait.h ++include/block/aio_task.h ++include/block/block-common.h ++include/block/block-global-state.h ++include/block/block-io.h ++include/block/block.h ++include/block/block_int-common.h ++include/block/block_int-global-state.h ++include/block/block_int-io.h ++include/block/block_int.h ++include/block/blockjob.h ++include/block/blockjob_int.h ++include/block/dirty-bitmap.h ++include/block/export.h ++include/block/graph-lock.h ++include/block/nbd.h ++include/block/qapi.h ++include/block/qdict.h ++include/block/raw-aio.h ++include/block/snapshot.h ++include/block/thread-pool.h ++include/crypto/hash.h ++include/crypto/tlscreds.h ++include/crypto/tlscredsanon.h ++include/crypto/tlscredspsk.h ++include/crypto/tlscredsx509.h ++include/crypto/tlssession.h ++include/exec/hwaddr.h ++include/glib-compat.h ++include/hw/block/block.h ++include/hw/qdev-core.h ++include/io/channel.h ++include/io/channel-file.h ++include/io/channel-socket.h ++include/io/channel-tls.h ++include/io/channel-util.h ++include/io/channel-watch.h ++include/io/task.h ++include/qapi/clone-visitor.h ++include/qapi/compat-policy.h ++include/qapi/dealloc-visitor.h ++include/qapi/error.h ++include/qapi/qmp-event.h ++include/qapi/qmp/dispatch.h ++include/qapi/qmp/json-parser.h ++include/qapi/qmp/json-writer.h ++include/qapi/qmp/qbool.h ++include/qapi/qmp/qdict.h ++include/qapi/qmp/qerror.h ++include/qapi/qmp/qjson.h ++include/qapi/qmp/qlist.h ++include/qapi/qmp/qnull.h ++include/qapi/qmp/qnum.h ++include/qapi/qmp/qobject.h ++include/qapi/qmp/qstring.h ++include/qapi/qobject-input-visitor.h ++include/qapi/qobject-output-visitor.h ++include/qapi/string-input-visitor.h ++include/qapi/string-output-visitor.h ++include/qapi/util.h ++include/qapi/visitor-impl.h ++include/qapi/visitor.h ++include/qemu/atomic.h ++include/qemu/bitmap.h ++include/qemu/bitops.h ++include/qemu/bswap.h ++include/qemu/clang-tsa.h ++include/qemu/compiler.h ++include/qemu/config-file.h ++include/qemu/coroutine-core.h ++include/qemu/coroutine-tls.h ++include/qemu/coroutine.h ++include/qemu/coroutine_int.h ++include/qemu/ctype.h ++include/qemu/cutils.h ++include/qemu/defer-call.h ++include/qemu/error-report.h ++include/qemu/event_notifier.h ++include/qemu/futex.h ++include/qemu/hbitmap.h ++include/qemu/help_option.h ++include/qemu/host-utils.h ++include/qemu/id.h ++include/qemu/iov.h ++include/qemu/job.h ++include/qemu/lockable.h ++include/qemu/main-loop.h ++include/qemu/memalign.h ++include/qemu/module.h ++include/qemu/notify.h ++include/qemu/option.h ++include/qemu/option_int.h ++include/qemu/osdep.h ++include/qemu/processor.h ++include/qemu/progress_meter.h ++include/qemu/qdist.h ++include/qemu/qemu-print.h ++include/qemu/qemu-progress.h ++include/qemu/qsp.h ++include/qemu/queue.h ++include/qemu/range.h ++include/qemu/ratelimit.h ++include/qemu/rcu.h ++include/qemu/rcu_queue.h ++include/qemu/sockets.h ++include/qemu/stats64.h ++include/qemu/sys_membarrier.h ++include/qemu/thread-posix.h ++include/qemu/thread.h ++include/qemu/timed-average.h ++include/qemu/timer.h ++include/qemu/transactions.h ++include/qemu/typedefs.h ++include/qemu/unicode.h ++include/qemu/units.h ++include/qemu/uri.h ++include/qemu/yank.h ++include/qom/object.h ++include/qom/qom-qobject.h ++include/sysemu/block-backend-common.h ++include/sysemu/block-backend-global-state.h ++include/sysemu/block-backend-io.h ++include/sysemu/block-backend.h ++include/sysemu/blockdev.h ++include/sysemu/cpu-timers.h ++include/sysemu/os-posix.h ++#block:include ++qcow2.h ++#build:include ++block/module_block.h ++qapi/qapi-builtin-types.h ++qapi/qapi-builtin-visit.h ++qapi/qapi-commands-block.h ++qapi/qapi-commands-block-core.h ++qapi/qapi-commands-common.h ++qapi/qapi-commands-job.h ++qapi/qapi-events-block-core.h ++qapi/qapi-events-common.h ++qapi/qapi-events-job.h ++qapi/qapi-types-block.h ++qapi/qapi-types-block-core.h ++qapi/qapi-types-block-export.h ++qapi/qapi-types-common.h ++qapi/qapi-types-compat.h ++qapi/qapi-types-crypto.h ++qapi/qapi-types-error.h ++qapi/qapi-types-job.h ++qapi/qapi-types-run-state.h ++qapi/qapi-types-sockets.h ++qapi/qapi-types-yank.h ++qapi/qapi-visit-block-core.h ++qapi/qapi-visit-common.h ++qapi/qapi-visit-crypto.h ++qapi/qapi-visit-job.h ++qapi/qapi-visit-sockets.h ++qapi/qapi-visit-yank.h ++#.:qcow2/lib ++authz/base.c ++block.c ++blockdev.c ++blockjob.c ++block/accounting.c ++block/aio_task.c ++block/block-backend.c ++block/block-gen.h ++block/commit.c ++block/coroutines.h ++block/dirty-bitmap.c ++block/file-posix.c ++block/graph-lock.c ++block/io.c ++block/linux-aio.c ++block/mirror.c ++block/monitor/bitmap-qmp-cmds.c ++block/nbd.c ++block/progress_meter.c ++block/qapi.c ++block/raw-format.c ++block/snapshot.c ++crypto/hash.c ++crypto/tlscredspriv.h ++crypto/tlscreds.c ++crypto/tlssession.c ++hw/block/block.c ++hw/block/hd-geometry.c ++io/channel.c ++io/channel-file.c ++io/channel-socket.c ++io/channel-tls.c ++io/channel-util.c ++io/channel-watch.c ++io/task.c ++job.c ++job-qmp.c ++nbd/client.c ++nbd/client-connection.c ++nbd/common.c ++nbd/nbd-internal.h ++qapi/qapi-clone-visitor.c ++qapi/qapi-dealloc-visitor.c ++qapi/qapi-util.c ++qapi/qapi-visit-core.c ++qapi/qobject-input-visitor.c ++qapi/qobject-output-visitor.c ++qapi/string-input-visitor.c ++qapi/string-output-visitor.c ++qobject/block-qdict.c ++qobject/json-lexer.c ++qobject/json-parser.c ++qobject/json-parser-int.h ++qobject/json-streamer.c ++qobject/json-writer.c ++qobject/qbool.c ++qobject/qdict.c ++qobject/qjson.c ++qobject/qlist.c ++qobject/qnull.c ++qobject/qnum.c ++qobject/qobject-internal.h ++qobject/qobject.c ++qobject/qstring.c ++qom/container.c ++qom/object.c ++qom/qom-qobject.c ++system/cpus.c ++util/aio-posix.c ++util/aio-posix.h ++util/aio-wait.c ++util/aiocb.c ++util/async.c ++util/bitmap.c ++util/bitops.c ++util/bufferiszero.c ++util/compatfd.c ++util/coroutine-ucontext.c ++util/cutils.c ++util/defer-call.c ++util/error-report.c ++util/error.c ++util/event_notifier-posix.c ++util/fdmon-epoll.c ++util/fdmon-poll.c ++util/hbitmap.c ++util/host-utils.c ++util/id.c ++util/iov.c ++util/lockcnt.c ++util/main-loop.c ++util/memalign.c ++util/module.c ++util/notify.c ++util/osdep.c ++util/oslib-posix.c ++util/qemu-coroutine-lock.c ++util/qemu-coroutine.c ++util/qemu-option.c ++util/qemu-print.c ++util/qemu-sockets.c ++util/qemu-thread-common.h ++util/qemu-thread-posix.c ++util/qemu-timer.c ++util/qsp.c ++util/range.c ++util/rcu.c ++util/stats64.c ++util/thread-pool.c ++util/timed-average.c ++util/transactions.c ++util/unicode.c ++util/uri.c ++util/yank.c ++qapi/qmp-dispatch.c ++qapi/qmp-event.c ++#block:qcow2/lib ++qcow2-bitmap.c ++qcow2-cache.c ++qcow2-cluster.c ++qcow2-refcount.c ++qcow2-snapshot.c ++qcow2-threads.c ++qcow2.c ++#build:qcow2/lib ++block/block-gen.c ++qapi/qapi-emit-events.h ++qapi/qapi-events-block-core.c ++qapi/qapi-events-job.c ++qapi/qapi-types-block-core.c ++qapi/qapi-types-common.c ++qapi/qapi-types-crypto.c ++qapi/qapi-types-job.c ++qapi/qapi-types-sockets.c ++qapi/qapi-types-yank.c ++qapi/qapi-visit-block-core.c ++qapi/qapi-visit-common.c ++qapi/qapi-visit-crypto.c ++qapi/qapi-visit-job.c ++qapi/qapi-visit-sockets.c ++qapi/qapi-visit-yank.c ++#.:qcow2 ++qemu-img.c ++#util:qcow2 ++qemu-config.c ++qemu-progress.c ++#build:qcow2 ++qemu-img-cmds.h diff --git a/SOURCES/0025-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch b/SOURCES/0025-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch new file mode 100644 index 0000000..8055a99 --- /dev/null +++ b/SOURCES/0025-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch @@ -0,0 +1,152808 @@ +From 1d37593e9ec78dba3d43b4121729206d8e7e55de Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:22 +0100 +Subject: [PATCH] libqcow2: import vanilla sources from qemu 9.1.1 + +All libqcow2 sources come from qemu-9.1.1. +This code is GPLv2. + +Signed-off-by: Anthoine Bourgeois +--- + include/authz/base.h | 101 + + include/block/accounting.h | 124 + + include/block/aio-wait.h | 152 + + include/block/aio.h | 723 ++ + include/block/aio_task.h | 52 + + include/block/block-common.h | 567 ++ + include/block/block-global-state.h | 305 + + include/block/block-io.h | 455 + + include/block/block.h | 32 + + include/block/block_int-common.h | 1318 +++ + include/block/block_int-global-state.h | 326 + + include/block/block_int-io.h | 194 + + include/block/block_int.h | 33 + + include/block/blockjob.h | 238 + + include/block/blockjob_int.h | 168 + + include/block/dirty-bitmap.h | 130 + + include/block/export.h | 91 + + include/block/graph-lock.h | 285 + + include/block/module_block.h | 19 + + include/block/nbd.h | 509 + + include/block/qapi.h | 52 + + include/block/qdict.h | 35 + + include/block/raw-aio.h | 94 + + include/block/snapshot.h | 113 + + include/block/thread-pool.h | 43 + + include/crypto/hash.h | 192 + + include/crypto/tlscreds.h | 67 + + include/crypto/tlscredsanon.h | 100 + + include/crypto/tlscredspsk.h | 95 + + include/crypto/tlscredsx509.h | 104 + + include/crypto/tlssession.h | 356 + + include/exec/hwaddr.h | 26 + + include/glib-compat.h | 131 + + include/hw/block/block.h | 110 + + include/hw/qdev-core.h | 1111 +++ + include/io/channel-file.h | 110 + + include/io/channel-socket.h | 265 + + include/io/channel-tls.h | 146 + + include/io/channel-util.h | 75 + + include/io/channel-watch.h | 90 + + include/io/channel.h | 1005 ++ + include/io/task.h | 350 + + include/qapi/clone-visitor.h | 62 + + include/qapi/compat-policy.h | 45 + + include/qapi/dealloc-visitor.h | 28 + + include/qapi/error.h | 540 ++ + include/qapi/qapi-builtin-types.h | 184 + + include/qapi/qapi-builtin-visit.h | 68 + + include/qapi/qapi-commands-block-core.h | 103 + + include/qapi/qapi-commands-block.h | 38 + + include/qapi/qapi-commands-common.h | 19 + + include/qapi/qapi-commands-job.h | 33 + + include/qapi/qapi-events-block-core.h | 43 + + include/qapi/qapi-events-common.h | 19 + + include/qapi/qapi-events-job.h | 21 + + include/qapi/qapi-types-block-core.h | 3626 +++++++ + include/qapi/qapi-types-block-export.h | 274 + + include/qapi/qapi-types-block.h | 161 + + include/qapi/qapi-types-common.h | 163 + + include/qapi/qapi-types-compat.h | 57 + + include/qapi/qapi-types-crypto.h | 538 ++ + include/qapi/qapi-types-error.h | 32 + + include/qapi/qapi-types-job.h | 140 + + include/qapi/qapi-types-run-state.h | 312 + + include/qapi/qapi-types-sockets.h | 220 + + include/qapi/qapi-types-yank.h | 83 + + include/qapi/qapi-visit-block-core.h | 960 ++ + include/qapi/qapi-visit-common.h | 55 + + include/qapi/qapi-visit-crypto.h | 165 + + include/qapi/qapi-visit-job.h | 51 + + include/qapi/qapi-visit-sockets.h | 91 + + include/qapi/qapi-visit-yank.h | 45 + + include/qapi/qmp-event.h | 18 + + include/qapi/qmp/dispatch.h | 67 + + include/qapi/qmp/json-parser.h | 46 + + include/qapi/qmp/json-writer.h | 35 + + include/qapi/qmp/qbool.h | 31 + + include/qapi/qmp/qdict.h | 71 + + include/qapi/qmp/qerror.h | 32 + + include/qapi/qmp/qjson.h | 31 + + include/qapi/qmp/qlist.h | 69 + + include/qapi/qmp/qnull.h | 33 + + include/qapi/qmp/qnum.h | 75 + + include/qapi/qmp/qobject.h | 144 + + include/qapi/qmp/qstring.h | 33 + + include/qapi/qobject-input-visitor.h | 82 + + include/qapi/qobject-output-visitor.h | 56 + + include/qapi/string-input-visitor.h | 27 + + include/qapi/string-output-visitor.h | 35 + + include/qapi/util.h | 72 + + include/qapi/visitor-impl.h | 137 + + include/qapi/visitor.h | 713 ++ + include/qcow2.h | 1074 +++ + include/qemu/atomic.h | 284 + + include/qemu/bitmap.h | 281 + + include/qemu/bitops.h | 634 ++ + include/qemu/bswap.h | 436 + + include/qemu/clang-tsa.h | 114 + + include/qemu/compiler.h | 276 + + include/qemu/config-file.h | 31 + + include/qemu/coroutine-core.h | 154 + + include/qemu/coroutine-tls.h | 165 + + include/qemu/coroutine.h | 311 + + include/qemu/coroutine_int.h | 77 + + include/qemu/ctype.h | 27 + + include/qemu/cutils.h | 308 + + include/qemu/defer-call.h | 16 + + include/qemu/error-report.h | 77 + + include/qemu/event_notifier.h | 46 + + include/qemu/futex.h | 41 + + include/qemu/hbitmap.h | 353 + + include/qemu/help_option.h | 33 + + include/qemu/host-utils.h | 861 ++ + include/qemu/id.h | 15 + + include/qemu/iov.h | 250 + + include/qemu/job.h | 731 ++ + include/qemu/lockable.h | 184 + + include/qemu/main-loop.h | 411 + + include/qemu/memalign.h | 61 + + include/qemu/module.h | 192 + + include/qemu/notify.h | 78 + + include/qemu/option.h | 151 + + include/qemu/option_int.h | 54 + + include/qemu/osdep.h | 831 ++ + include/qemu/processor.h | 25 + + include/qemu/progress_meter.h | 62 + + include/qemu/qdist.h | 61 + + include/qemu/qemu-print.h | 23 + + include/qemu/qemu-progress.h | 8 + + include/qemu/qsp.h | 27 + + include/qemu/queue.h | 576 ++ + include/qemu/range.h | 247 + + include/qemu/ratelimit.h | 97 + + include/qemu/rcu.h | 198 + + include/qemu/rcu_queue.h | 309 + + include/qemu/sockets.h | 156 + + include/qemu/stats64.h | 199 + + include/qemu/sys_membarrier.h | 27 + + include/qemu/thread-posix.h | 48 + + include/qemu/thread.h | 407 + + include/qemu/timed-average.h | 63 + + include/qemu/timer.h | 1038 ++ + include/qemu/transactions.h | 66 + + include/qemu/typedefs.h | 134 + + include/qemu/unicode.h | 7 + + include/qemu/units.h | 20 + + include/qemu/uri.h | 99 + + include/qemu/yank.h | 87 + + include/qom/object.h | 2058 ++++ + include/qom/qom-qobject.h | 43 + + include/sysemu/block-backend-common.h | 103 + + include/sysemu/block-backend-global-state.h | 133 + + include/sysemu/block-backend-io.h | 230 + + include/sysemu/block-backend.h | 21 + + include/sysemu/blockdev.h | 64 + + include/sysemu/cpu-timers.h | 104 + + include/sysemu/os-posix.h | 101 + + qcow2/LICENSE-gpl-2.0.txt | 338 + + qcow2/lib/authz/base.c | 83 + + qcow2/lib/block.c | 8432 +++++++++++++++++ + qcow2/lib/block/accounting.c | 298 + + qcow2/lib/block/aio_task.c | 126 + + qcow2/lib/block/block-backend.c | 2899 ++++++ + qcow2/lib/block/block-gen.c | 2636 ++++++ + qcow2/lib/block/block-gen.h | 46 + + qcow2/lib/block/commit.c | 600 ++ + qcow2/lib/block/coroutines.h | 92 + + qcow2/lib/block/dirty-bitmap.c | 890 ++ + qcow2/lib/block/file-posix.c | 4565 +++++++++ + qcow2/lib/block/graph-lock.c | 281 + + qcow2/lib/block/io.c | 3755 ++++++++ + qcow2/lib/block/linux-aio.c | 507 + + qcow2/lib/block/mirror.c | 2082 ++++ + qcow2/lib/block/monitor/bitmap-qmp-cmds.c | 317 + + qcow2/lib/block/nbd.c | 2235 +++++ + qcow2/lib/block/progress_meter.c | 66 + + qcow2/lib/block/qapi.c | 1012 ++ + qcow2/lib/block/raw-format.c | 682 ++ + qcow2/lib/block/snapshot.c | 780 ++ + qcow2/lib/blockdev.c | 3649 +++++++ + qcow2/lib/blockjob.c | 630 ++ + qcow2/lib/crypto/hash.c | 144 + + qcow2/lib/crypto/tlscreds.c | 294 + + qcow2/lib/crypto/tlscredspriv.h | 86 + + qcow2/lib/crypto/tlssession.c | 717 ++ + qcow2/lib/hw/block/block.c | 278 + + qcow2/lib/hw/block/hd-geometry.c | 168 + + qcow2/lib/io/channel-file.c | 324 + + qcow2/lib/io/channel-socket.c | 985 ++ + qcow2/lib/io/channel-tls.c | 507 + + qcow2/lib/io/channel-util.c | 62 + + qcow2/lib/io/channel-watch.c | 347 + + qcow2/lib/io/channel.c | 808 ++ + qcow2/lib/io/task.c | 241 + + qcow2/lib/job-qmp.c | 188 + + qcow2/lib/job.c | 1264 +++ + qcow2/lib/nbd/client-connection.c | 422 + + qcow2/lib/nbd/client.c | 1589 ++++ + qcow2/lib/nbd/common.c | 266 + + qcow2/lib/nbd/nbd-internal.h | 77 + + qcow2/lib/qapi/qapi-clone-visitor.c | 182 + + qcow2/lib/qapi/qapi-dealloc-visitor.c | 143 + + qcow2/lib/qapi/qapi-emit-events.h | 83 + + qcow2/lib/qapi/qapi-events-block-core.c | 323 + + qcow2/lib/qapi/qapi-events-job.c | 53 + + qcow2/lib/qapi/qapi-types-block-core.c | 2484 +++++ + qcow2/lib/qapi/qapi-types-common.c | 141 + + qcow2/lib/qapi/qapi-types-crypto.c | 413 + + qcow2/lib/qapi/qapi-types-job.c | 91 + + qcow2/lib/qapi/qapi-types-sockets.c | 209 + + qcow2/lib/qapi/qapi-types-yank.c | 80 + + qcow2/lib/qapi/qapi-util.c | 154 + + qcow2/lib/qapi/qapi-visit-block-core.c | 9398 +++++++++++++++++++ + qcow2/lib/qapi/qapi-visit-common.c | 176 + + qcow2/lib/qapi/qapi-visit-core.c | 444 + + qcow2/lib/qapi/qapi-visit-crypto.c | 1150 +++ + qcow2/lib/qapi/qapi-visit-job.c | 186 + + qcow2/lib/qapi/qapi-visit-sockets.c | 569 ++ + qcow2/lib/qapi/qapi-visit-yank.c | 186 + + qcow2/lib/qapi/qmp-dispatch.c | 296 + + qcow2/lib/qapi/qmp-event.c | 42 + + qcow2/lib/qapi/qobject-input-visitor.c | 776 ++ + qcow2/lib/qapi/qobject-output-visitor.c | 280 + + qcow2/lib/qapi/string-input-visitor.c | 414 + + qcow2/lib/qapi/string-output-visitor.c | 451 + + qcow2/lib/qcow2-bitmap.c | 1809 ++++ + qcow2/lib/qcow2-cache.c | 462 + + qcow2/lib/qcow2-cluster.c | 2562 +++++ + qcow2/lib/qcow2-refcount.c | 3750 ++++++++ + qcow2/lib/qcow2-snapshot.c | 1076 +++ + qcow2/lib/qcow2-threads.c | 527 ++ + qcow2/lib/qcow2.c | 6214 ++++++++++++ + qcow2/lib/qobject/block-qdict.c | 738 ++ + qcow2/lib/qobject/json-lexer.c | 365 + + qcow2/lib/qobject/json-parser-int.h | 54 + + qcow2/lib/qobject/json-parser.c | 590 ++ + qcow2/lib/qobject/json-streamer.c | 134 + + qcow2/lib/qobject/json-writer.c | 247 + + qcow2/lib/qobject/qbool.c | 63 + + qcow2/lib/qobject/qdict.c | 449 + + qcow2/lib/qobject/qjson.c | 232 + + qcow2/lib/qobject/qlist.c | 189 + + qcow2/lib/qobject/qnull.c | 36 + + qcow2/lib/qobject/qnum.c | 246 + + qcow2/lib/qobject/qobject-internal.h | 39 + + qcow2/lib/qobject/qobject.c | 72 + + qcow2/lib/qobject/qstring.c | 107 + + qcow2/lib/qom/container.c | 52 + + qcow2/lib/qom/object.c | 2899 ++++++ + qcow2/lib/qom/qom-qobject.c | 45 + + qcow2/lib/system/.dirstamp | 0 + qcow2/lib/system/cpus.c | 878 ++ + qcow2/lib/util/aio-posix.c | 789 ++ + qcow2/lib/util/aio-posix.h | 82 + + qcow2/lib/util/aio-wait.c | 86 + + qcow2/lib/util/aiocb.c | 55 + + qcow2/lib/util/async.c | 760 ++ + qcow2/lib/util/bitmap.c | 534 ++ + qcow2/lib/util/bitops.c | 157 + + qcow2/lib/util/bufferiszero.c | 126 + + qcow2/lib/util/compatfd.c | 89 + + qcow2/lib/util/coroutine-ucontext.c | 359 + + qcow2/lib/util/cutils.c | 1218 +++ + qcow2/lib/util/defer-call.c | 156 + + qcow2/lib/util/error-report.c | 394 + + qcow2/lib/util/error.c | 308 + + qcow2/lib/util/event_notifier-posix.c | 142 + + qcow2/lib/util/fdmon-epoll.c | 156 + + qcow2/lib/util/fdmon-poll.c | 107 + + qcow2/lib/util/hbitmap.c | 955 ++ + qcow2/lib/util/host-utils.c | 448 + + qcow2/lib/util/id.c | 69 + + qcow2/lib/util/iov.c | 712 ++ + qcow2/lib/util/lockcnt.c | 399 + + qcow2/lib/util/main-loop.c | 653 ++ + qcow2/lib/util/memalign.c | 92 + + qcow2/lib/util/module.c | 405 + + qcow2/lib/util/notify.c | 77 + + qcow2/lib/util/osdep.c | 615 ++ + qcow2/lib/util/oslib-posix.c | 933 ++ + qcow2/lib/util/qemu-coroutine-lock.c | 469 + + qcow2/lib/util/qemu-coroutine.c | 401 + + qcow2/lib/util/qemu-option.c | 1226 +++ + qcow2/lib/util/qemu-print.c | 70 + + qcow2/lib/util/qemu-sockets.c | 1475 +++ + qcow2/lib/util/qemu-thread-common.h | 54 + + qcow2/lib/util/qemu-thread-posix.c | 686 ++ + qcow2/lib/util/qemu-timer.c | 713 ++ + qcow2/lib/util/qsp.c | 813 ++ + qcow2/lib/util/range.c | 123 + + qcow2/lib/util/rcu.c | 472 + + qcow2/lib/util/stats64.c | 148 + + qcow2/lib/util/thread-pool.c | 381 + + qcow2/lib/util/timed-average.c | 231 + + qcow2/lib/util/transactions.c | 100 + + qcow2/lib/util/unicode.c | 156 + + qcow2/lib/util/uri.c | 1466 +++ + qcow2/lib/util/yank.c | 199 + + qcow2/qemu-config.c | 295 + + qcow2/qemu-img-cmds.h | 46 + + qcow2/qemu-img.c | 5621 +++++++++++ + qcow2/qemu-progress.c | 162 + + 302 files changed, 150383 insertions(+) + create mode 100644 include/authz/base.h + create mode 100644 include/block/accounting.h + create mode 100644 include/block/aio-wait.h + create mode 100644 include/block/aio.h + create mode 100644 include/block/aio_task.h + create mode 100644 include/block/block-common.h + create mode 100644 include/block/block-global-state.h + create mode 100644 include/block/block-io.h + create mode 100644 include/block/block.h + create mode 100644 include/block/block_int-common.h + create mode 100644 include/block/block_int-global-state.h + create mode 100644 include/block/block_int-io.h + create mode 100644 include/block/block_int.h + create mode 100644 include/block/blockjob.h + create mode 100644 include/block/blockjob_int.h + create mode 100644 include/block/dirty-bitmap.h + create mode 100644 include/block/export.h + create mode 100644 include/block/graph-lock.h + create mode 100644 include/block/module_block.h + create mode 100644 include/block/nbd.h + create mode 100644 include/block/qapi.h + create mode 100644 include/block/qdict.h + create mode 100644 include/block/raw-aio.h + create mode 100644 include/block/snapshot.h + create mode 100644 include/block/thread-pool.h + create mode 100644 include/crypto/hash.h + create mode 100644 include/crypto/tlscreds.h + create mode 100644 include/crypto/tlscredsanon.h + create mode 100644 include/crypto/tlscredspsk.h + create mode 100644 include/crypto/tlscredsx509.h + create mode 100644 include/crypto/tlssession.h + create mode 100644 include/exec/hwaddr.h + create mode 100644 include/glib-compat.h + create mode 100644 include/hw/block/block.h + create mode 100644 include/hw/qdev-core.h + create mode 100644 include/io/channel-file.h + create mode 100644 include/io/channel-socket.h + create mode 100644 include/io/channel-tls.h + create mode 100644 include/io/channel-util.h + create mode 100644 include/io/channel-watch.h + create mode 100644 include/io/channel.h + create mode 100644 include/io/task.h + create mode 100644 include/qapi/clone-visitor.h + create mode 100644 include/qapi/compat-policy.h + create mode 100644 include/qapi/dealloc-visitor.h + create mode 100644 include/qapi/error.h + create mode 100644 include/qapi/qapi-builtin-types.h + create mode 100644 include/qapi/qapi-builtin-visit.h + create mode 100644 include/qapi/qapi-commands-block-core.h + create mode 100644 include/qapi/qapi-commands-block.h + create mode 100644 include/qapi/qapi-commands-common.h + create mode 100644 include/qapi/qapi-commands-job.h + create mode 100644 include/qapi/qapi-events-block-core.h + create mode 100644 include/qapi/qapi-events-common.h + create mode 100644 include/qapi/qapi-events-job.h + create mode 100644 include/qapi/qapi-types-block-core.h + create mode 100644 include/qapi/qapi-types-block-export.h + create mode 100644 include/qapi/qapi-types-block.h + create mode 100644 include/qapi/qapi-types-common.h + create mode 100644 include/qapi/qapi-types-compat.h + create mode 100644 include/qapi/qapi-types-crypto.h + create mode 100644 include/qapi/qapi-types-error.h + create mode 100644 include/qapi/qapi-types-job.h + create mode 100644 include/qapi/qapi-types-run-state.h + create mode 100644 include/qapi/qapi-types-sockets.h + create mode 100644 include/qapi/qapi-types-yank.h + create mode 100644 include/qapi/qapi-visit-block-core.h + create mode 100644 include/qapi/qapi-visit-common.h + create mode 100644 include/qapi/qapi-visit-crypto.h + create mode 100644 include/qapi/qapi-visit-job.h + create mode 100644 include/qapi/qapi-visit-sockets.h + create mode 100644 include/qapi/qapi-visit-yank.h + create mode 100644 include/qapi/qmp-event.h + create mode 100644 include/qapi/qmp/dispatch.h + create mode 100644 include/qapi/qmp/json-parser.h + create mode 100644 include/qapi/qmp/json-writer.h + create mode 100644 include/qapi/qmp/qbool.h + create mode 100644 include/qapi/qmp/qdict.h + create mode 100644 include/qapi/qmp/qerror.h + create mode 100644 include/qapi/qmp/qjson.h + create mode 100644 include/qapi/qmp/qlist.h + create mode 100644 include/qapi/qmp/qnull.h + create mode 100644 include/qapi/qmp/qnum.h + create mode 100644 include/qapi/qmp/qobject.h + create mode 100644 include/qapi/qmp/qstring.h + create mode 100644 include/qapi/qobject-input-visitor.h + create mode 100644 include/qapi/qobject-output-visitor.h + create mode 100644 include/qapi/string-input-visitor.h + create mode 100644 include/qapi/string-output-visitor.h + create mode 100644 include/qapi/util.h + create mode 100644 include/qapi/visitor-impl.h + create mode 100644 include/qapi/visitor.h + create mode 100644 include/qcow2.h + create mode 100644 include/qemu/atomic.h + create mode 100644 include/qemu/bitmap.h + create mode 100644 include/qemu/bitops.h + create mode 100644 include/qemu/bswap.h + create mode 100644 include/qemu/clang-tsa.h + create mode 100644 include/qemu/compiler.h + create mode 100644 include/qemu/config-file.h + create mode 100644 include/qemu/coroutine-core.h + create mode 100644 include/qemu/coroutine-tls.h + create mode 100644 include/qemu/coroutine.h + create mode 100644 include/qemu/coroutine_int.h + create mode 100644 include/qemu/ctype.h + create mode 100644 include/qemu/cutils.h + create mode 100644 include/qemu/defer-call.h + create mode 100644 include/qemu/error-report.h + create mode 100644 include/qemu/event_notifier.h + create mode 100644 include/qemu/futex.h + create mode 100644 include/qemu/hbitmap.h + create mode 100644 include/qemu/help_option.h + create mode 100644 include/qemu/host-utils.h + create mode 100644 include/qemu/id.h + create mode 100644 include/qemu/iov.h + create mode 100644 include/qemu/job.h + create mode 100644 include/qemu/lockable.h + create mode 100644 include/qemu/main-loop.h + create mode 100644 include/qemu/memalign.h + create mode 100644 include/qemu/module.h + create mode 100644 include/qemu/notify.h + create mode 100644 include/qemu/option.h + create mode 100644 include/qemu/option_int.h + create mode 100644 include/qemu/osdep.h + create mode 100644 include/qemu/processor.h + create mode 100644 include/qemu/progress_meter.h + create mode 100644 include/qemu/qdist.h + create mode 100644 include/qemu/qemu-print.h + create mode 100644 include/qemu/qemu-progress.h + create mode 100644 include/qemu/qsp.h + create mode 100644 include/qemu/queue.h + create mode 100644 include/qemu/range.h + create mode 100644 include/qemu/ratelimit.h + create mode 100644 include/qemu/rcu.h + create mode 100644 include/qemu/rcu_queue.h + create mode 100644 include/qemu/sockets.h + create mode 100644 include/qemu/stats64.h + create mode 100644 include/qemu/sys_membarrier.h + create mode 100644 include/qemu/thread-posix.h + create mode 100644 include/qemu/thread.h + create mode 100644 include/qemu/timed-average.h + create mode 100644 include/qemu/timer.h + create mode 100644 include/qemu/transactions.h + create mode 100644 include/qemu/typedefs.h + create mode 100644 include/qemu/unicode.h + create mode 100644 include/qemu/units.h + create mode 100644 include/qemu/uri.h + create mode 100644 include/qemu/yank.h + create mode 100644 include/qom/object.h + create mode 100644 include/qom/qom-qobject.h + create mode 100644 include/sysemu/block-backend-common.h + create mode 100644 include/sysemu/block-backend-global-state.h + create mode 100644 include/sysemu/block-backend-io.h + create mode 100644 include/sysemu/block-backend.h + create mode 100644 include/sysemu/blockdev.h + create mode 100644 include/sysemu/cpu-timers.h + create mode 100644 include/sysemu/os-posix.h + create mode 100644 qcow2/LICENSE-gpl-2.0.txt + create mode 100644 qcow2/lib/authz/base.c + create mode 100644 qcow2/lib/block.c + create mode 100644 qcow2/lib/block/accounting.c + create mode 100644 qcow2/lib/block/aio_task.c + create mode 100644 qcow2/lib/block/block-backend.c + create mode 100644 qcow2/lib/block/block-gen.c + create mode 100644 qcow2/lib/block/block-gen.h + create mode 100644 qcow2/lib/block/commit.c + create mode 100644 qcow2/lib/block/coroutines.h + create mode 100644 qcow2/lib/block/dirty-bitmap.c + create mode 100644 qcow2/lib/block/file-posix.c + create mode 100644 qcow2/lib/block/graph-lock.c + create mode 100644 qcow2/lib/block/io.c + create mode 100644 qcow2/lib/block/linux-aio.c + create mode 100644 qcow2/lib/block/mirror.c + create mode 100644 qcow2/lib/block/monitor/bitmap-qmp-cmds.c + create mode 100644 qcow2/lib/block/nbd.c + create mode 100644 qcow2/lib/block/progress_meter.c + create mode 100644 qcow2/lib/block/qapi.c + create mode 100644 qcow2/lib/block/raw-format.c + create mode 100644 qcow2/lib/block/snapshot.c + create mode 100644 qcow2/lib/blockdev.c + create mode 100644 qcow2/lib/blockjob.c + create mode 100644 qcow2/lib/crypto/hash.c + create mode 100644 qcow2/lib/crypto/tlscreds.c + create mode 100644 qcow2/lib/crypto/tlscredspriv.h + create mode 100644 qcow2/lib/crypto/tlssession.c + create mode 100644 qcow2/lib/hw/block/block.c + create mode 100644 qcow2/lib/hw/block/hd-geometry.c + create mode 100644 qcow2/lib/io/channel-file.c + create mode 100644 qcow2/lib/io/channel-socket.c + create mode 100644 qcow2/lib/io/channel-tls.c + create mode 100644 qcow2/lib/io/channel-util.c + create mode 100644 qcow2/lib/io/channel-watch.c + create mode 100644 qcow2/lib/io/channel.c + create mode 100644 qcow2/lib/io/task.c + create mode 100644 qcow2/lib/job-qmp.c + create mode 100644 qcow2/lib/job.c + create mode 100644 qcow2/lib/nbd/client-connection.c + create mode 100644 qcow2/lib/nbd/client.c + create mode 100644 qcow2/lib/nbd/common.c + create mode 100644 qcow2/lib/nbd/nbd-internal.h + create mode 100644 qcow2/lib/qapi/qapi-clone-visitor.c + create mode 100644 qcow2/lib/qapi/qapi-dealloc-visitor.c + create mode 100644 qcow2/lib/qapi/qapi-emit-events.h + create mode 100644 qcow2/lib/qapi/qapi-events-block-core.c + create mode 100644 qcow2/lib/qapi/qapi-events-job.c + create mode 100644 qcow2/lib/qapi/qapi-types-block-core.c + create mode 100644 qcow2/lib/qapi/qapi-types-common.c + create mode 100644 qcow2/lib/qapi/qapi-types-crypto.c + create mode 100644 qcow2/lib/qapi/qapi-types-job.c + create mode 100644 qcow2/lib/qapi/qapi-types-sockets.c + create mode 100644 qcow2/lib/qapi/qapi-types-yank.c + create mode 100644 qcow2/lib/qapi/qapi-util.c + create mode 100644 qcow2/lib/qapi/qapi-visit-block-core.c + create mode 100644 qcow2/lib/qapi/qapi-visit-common.c + create mode 100644 qcow2/lib/qapi/qapi-visit-core.c + create mode 100644 qcow2/lib/qapi/qapi-visit-crypto.c + create mode 100644 qcow2/lib/qapi/qapi-visit-job.c + create mode 100644 qcow2/lib/qapi/qapi-visit-sockets.c + create mode 100644 qcow2/lib/qapi/qapi-visit-yank.c + create mode 100644 qcow2/lib/qapi/qmp-dispatch.c + create mode 100644 qcow2/lib/qapi/qmp-event.c + create mode 100644 qcow2/lib/qapi/qobject-input-visitor.c + create mode 100644 qcow2/lib/qapi/qobject-output-visitor.c + create mode 100644 qcow2/lib/qapi/string-input-visitor.c + create mode 100644 qcow2/lib/qapi/string-output-visitor.c + create mode 100644 qcow2/lib/qcow2-bitmap.c + create mode 100644 qcow2/lib/qcow2-cache.c + create mode 100644 qcow2/lib/qcow2-cluster.c + create mode 100644 qcow2/lib/qcow2-refcount.c + create mode 100644 qcow2/lib/qcow2-snapshot.c + create mode 100644 qcow2/lib/qcow2-threads.c + create mode 100644 qcow2/lib/qcow2.c + create mode 100644 qcow2/lib/qobject/block-qdict.c + create mode 100644 qcow2/lib/qobject/json-lexer.c + create mode 100644 qcow2/lib/qobject/json-parser-int.h + create mode 100644 qcow2/lib/qobject/json-parser.c + create mode 100644 qcow2/lib/qobject/json-streamer.c + create mode 100644 qcow2/lib/qobject/json-writer.c + create mode 100644 qcow2/lib/qobject/qbool.c + create mode 100644 qcow2/lib/qobject/qdict.c + create mode 100644 qcow2/lib/qobject/qjson.c + create mode 100644 qcow2/lib/qobject/qlist.c + create mode 100644 qcow2/lib/qobject/qnull.c + create mode 100644 qcow2/lib/qobject/qnum.c + create mode 100644 qcow2/lib/qobject/qobject-internal.h + create mode 100644 qcow2/lib/qobject/qobject.c + create mode 100644 qcow2/lib/qobject/qstring.c + create mode 100644 qcow2/lib/qom/container.c + create mode 100644 qcow2/lib/qom/object.c + create mode 100644 qcow2/lib/qom/qom-qobject.c + create mode 100644 qcow2/lib/system/.dirstamp + create mode 100644 qcow2/lib/system/cpus.c + create mode 100644 qcow2/lib/util/aio-posix.c + create mode 100644 qcow2/lib/util/aio-posix.h + create mode 100644 qcow2/lib/util/aio-wait.c + create mode 100644 qcow2/lib/util/aiocb.c + create mode 100644 qcow2/lib/util/async.c + create mode 100644 qcow2/lib/util/bitmap.c + create mode 100644 qcow2/lib/util/bitops.c + create mode 100644 qcow2/lib/util/bufferiszero.c + create mode 100644 qcow2/lib/util/compatfd.c + create mode 100644 qcow2/lib/util/coroutine-ucontext.c + create mode 100644 qcow2/lib/util/cutils.c + create mode 100644 qcow2/lib/util/defer-call.c + create mode 100644 qcow2/lib/util/error-report.c + create mode 100644 qcow2/lib/util/error.c + create mode 100644 qcow2/lib/util/event_notifier-posix.c + create mode 100644 qcow2/lib/util/fdmon-epoll.c + create mode 100644 qcow2/lib/util/fdmon-poll.c + create mode 100644 qcow2/lib/util/hbitmap.c + create mode 100644 qcow2/lib/util/host-utils.c + create mode 100644 qcow2/lib/util/id.c + create mode 100644 qcow2/lib/util/iov.c + create mode 100644 qcow2/lib/util/lockcnt.c + create mode 100644 qcow2/lib/util/main-loop.c + create mode 100644 qcow2/lib/util/memalign.c + create mode 100644 qcow2/lib/util/module.c + create mode 100644 qcow2/lib/util/notify.c + create mode 100644 qcow2/lib/util/osdep.c + create mode 100644 qcow2/lib/util/oslib-posix.c + create mode 100644 qcow2/lib/util/qemu-coroutine-lock.c + create mode 100644 qcow2/lib/util/qemu-coroutine.c + create mode 100644 qcow2/lib/util/qemu-option.c + create mode 100644 qcow2/lib/util/qemu-print.c + create mode 100644 qcow2/lib/util/qemu-sockets.c + create mode 100644 qcow2/lib/util/qemu-thread-common.h + create mode 100644 qcow2/lib/util/qemu-thread-posix.c + create mode 100644 qcow2/lib/util/qemu-timer.c + create mode 100644 qcow2/lib/util/qsp.c + create mode 100644 qcow2/lib/util/range.c + create mode 100644 qcow2/lib/util/rcu.c + create mode 100644 qcow2/lib/util/stats64.c + create mode 100644 qcow2/lib/util/thread-pool.c + create mode 100644 qcow2/lib/util/timed-average.c + create mode 100644 qcow2/lib/util/transactions.c + create mode 100644 qcow2/lib/util/unicode.c + create mode 100644 qcow2/lib/util/uri.c + create mode 100644 qcow2/lib/util/yank.c + create mode 100644 qcow2/qemu-config.c + create mode 100644 qcow2/qemu-img-cmds.h + create mode 100644 qcow2/qemu-img.c + create mode 100644 qcow2/qemu-progress.c + +diff --git a/include/authz/base.h b/include/authz/base.h +new file mode 100644 +index 00000000..b53e4e45 +--- /dev/null ++++ b/include/authz/base.h +@@ -0,0 +1,101 @@ ++/* ++ * QEMU authorization framework base class ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QAUTHZ_BASE_H ++#define QAUTHZ_BASE_H ++ ++#include "qapi/error.h" ++#include "qom/object.h" ++ ++ ++#define TYPE_QAUTHZ "authz" ++ ++OBJECT_DECLARE_TYPE(QAuthZ, QAuthZClass, ++ QAUTHZ) ++ ++ ++/** ++ * QAuthZ: ++ * ++ * The QAuthZ class defines an API contract to be used ++ * for providing an authorization driver for services ++ * with user identities. ++ */ ++ ++struct QAuthZ { ++ Object parent_obj; ++}; ++ ++ ++struct QAuthZClass { ++ ObjectClass parent_class; ++ ++ bool (*is_allowed)(QAuthZ *authz, ++ const char *identity, ++ Error **errp); ++}; ++ ++ ++/** ++ * qauthz_is_allowed: ++ * @authz: the authorization object ++ * @identity: the user identity to authorize ++ * @errp: pointer to a NULL initialized error object ++ * ++ * Check if a user @identity is authorized. If an error ++ * occurs this method will return false to indicate ++ * denial, as well as setting @errp to contain the details. ++ * Callers are recommended to treat the denial and error ++ * scenarios identically. Specifically the error info in ++ * @errp should never be fed back to the user being ++ * authorized, it is merely for benefit of administrator ++ * debugging. ++ * ++ * Returns: true if @identity is authorized, false if denied or if ++ * an error occurred. ++ */ ++bool qauthz_is_allowed(QAuthZ *authz, ++ const char *identity, ++ Error **errp); ++ ++ ++/** ++ * qauthz_is_allowed_by_id: ++ * @authzid: ID of the authorization object ++ * @identity: the user identity to authorize ++ * @errp: pointer to a NULL initialized error object ++ * ++ * Check if a user @identity is authorized. If an error ++ * occurs this method will return false to indicate ++ * denial, as well as setting @errp to contain the details. ++ * Callers are recommended to treat the denial and error ++ * scenarios identically. Specifically the error info in ++ * @errp should never be fed back to the user being ++ * authorized, it is merely for benefit of administrator ++ * debugging. ++ * ++ * Returns: true if @identity is authorized, false if denied or if ++ * an error occurred. ++ */ ++bool qauthz_is_allowed_by_id(const char *authzid, ++ const char *identity, ++ Error **errp); ++ ++#endif /* QAUTHZ_BASE_H */ +diff --git a/include/block/accounting.h b/include/block/accounting.h +new file mode 100644 +index 00000000..a59e39f4 +--- /dev/null ++++ b/include/block/accounting.h +@@ -0,0 +1,124 @@ ++/* ++ * QEMU System Emulator block accounting ++ * ++ * Copyright (c) 2011 Christoph Hellwig ++ * Copyright (c) 2015 Igalia, S.L. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_ACCOUNTING_H ++#define BLOCK_ACCOUNTING_H ++ ++#include "qemu/timed-average.h" ++#include "qemu/thread.h" ++#include "qapi/qapi-types-common.h" ++ ++typedef struct BlockAcctTimedStats BlockAcctTimedStats; ++typedef struct BlockAcctStats BlockAcctStats; ++ ++enum BlockAcctType { ++ BLOCK_ACCT_NONE = 0, ++ BLOCK_ACCT_READ, ++ BLOCK_ACCT_WRITE, ++ BLOCK_ACCT_FLUSH, ++ BLOCK_ACCT_ZONE_APPEND, ++ BLOCK_ACCT_UNMAP, ++ BLOCK_MAX_IOTYPE, ++}; ++ ++struct BlockAcctTimedStats { ++ BlockAcctStats *stats; ++ TimedAverage latency[BLOCK_MAX_IOTYPE]; ++ unsigned interval_length; /* in seconds */ ++ QSLIST_ENTRY(BlockAcctTimedStats) entries; ++}; ++ ++typedef struct BlockLatencyHistogram { ++ /* The following histogram is represented like this: ++ * ++ * 5| * ++ * 4| * ++ * 3| * * ++ * 2| * * * ++ * 1| * * * * ++ * +------------------ ++ * 10 50 100 ++ * ++ * BlockLatencyHistogram histogram = { ++ * .nbins = 4, ++ * .boundaries = {10, 50, 100}, ++ * .bins = {3, 1, 5, 2}, ++ * }; ++ * ++ * @boundaries array define histogram intervals as follows: ++ * [0, boundaries[0]), [boundaries[0], boundaries[1]), ... ++ * [boundaries[nbins-2], +inf) ++ * ++ * So, for example above, histogram intervals are: ++ * [0, 10), [10, 50), [50, 100), [100, +inf) ++ */ ++ int nbins; ++ uint64_t *boundaries; /* @nbins-1 numbers here ++ (all boundaries, except 0 and +inf) */ ++ uint64_t *bins; ++} BlockLatencyHistogram; ++ ++struct BlockAcctStats { ++ QemuMutex lock; ++ uint64_t nr_bytes[BLOCK_MAX_IOTYPE]; ++ uint64_t nr_ops[BLOCK_MAX_IOTYPE]; ++ uint64_t invalid_ops[BLOCK_MAX_IOTYPE]; ++ uint64_t failed_ops[BLOCK_MAX_IOTYPE]; ++ uint64_t total_time_ns[BLOCK_MAX_IOTYPE]; ++ uint64_t merged[BLOCK_MAX_IOTYPE]; ++ int64_t last_access_time_ns; ++ QSLIST_HEAD(, BlockAcctTimedStats) intervals; ++ bool account_invalid; ++ bool account_failed; ++ BlockLatencyHistogram latency_histogram[BLOCK_MAX_IOTYPE]; ++}; ++ ++typedef struct BlockAcctCookie { ++ int64_t bytes; ++ int64_t start_time_ns; ++ enum BlockAcctType type; ++} BlockAcctCookie; ++ ++void block_acct_init(BlockAcctStats *stats); ++void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, ++ enum OnOffAuto account_failed); ++void block_acct_cleanup(BlockAcctStats *stats); ++void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length); ++BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, ++ BlockAcctTimedStats *s); ++void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, ++ int64_t bytes, enum BlockAcctType type); ++void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie); ++void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie); ++void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type); ++void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, ++ int num_requests); ++int64_t block_acct_idle_time_ns(BlockAcctStats *stats); ++double block_acct_queue_depth(BlockAcctTimedStats *stats, ++ enum BlockAcctType type); ++int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, ++ uint64List *boundaries); ++void block_latency_histograms_clear(BlockAcctStats *stats); ++ ++#endif +diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h +new file mode 100644 +index 00000000..cf5e8bde +--- /dev/null ++++ b/include/block/aio-wait.h +@@ -0,0 +1,152 @@ ++/* ++ * AioContext wait support ++ * ++ * Copyright (C) 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_AIO_WAIT_H ++#define QEMU_AIO_WAIT_H ++ ++#include "block/aio.h" ++#include "qemu/main-loop.h" ++ ++/** ++ * AioWait: ++ * ++ * An object that facilitates synchronous waiting on a condition. A single ++ * global AioWait object (global_aio_wait) is used internally. ++ * ++ * The main loop can wait on an operation running in an IOThread as follows: ++ * ++ * AioContext *ctx = ...; ++ * MyWork work = { .done = false }; ++ * schedule_my_work_in_iothread(ctx, &work); ++ * AIO_WAIT_WHILE(ctx, !work.done); ++ * ++ * The IOThread must call aio_wait_kick() to notify the main loop when ++ * work.done changes: ++ * ++ * static void do_work(...) ++ * { ++ * ... ++ * work.done = true; ++ * aio_wait_kick(); ++ * } ++ */ ++typedef struct { ++ /* Number of waiting AIO_WAIT_WHILE() callers. Accessed with atomic ops. */ ++ unsigned num_waiters; ++} AioWait; ++ ++extern AioWait global_aio_wait; ++ ++/** ++ * AIO_WAIT_WHILE_INTERNAL: ++ * @ctx: the aio context, or NULL if multiple aio contexts (for which the ++ * caller does not hold a lock) are involved in the polling condition. ++ * @cond: wait while this conditional expression is true ++ * ++ * Wait while a condition is true. Use this to implement synchronous ++ * operations that require event loop activity. ++ * ++ * The caller must be sure that something calls aio_wait_kick() when the value ++ * of @cond might have changed. ++ * ++ * The caller's thread must be the IOThread that owns @ctx or the main loop ++ * thread (with @ctx acquired exactly once). This function cannot be used to ++ * wait on conditions between two IOThreads since that could lead to deadlock, ++ * go via the main loop instead. ++ */ ++#define AIO_WAIT_WHILE_INTERNAL(ctx, cond) ({ \ ++ bool waited_ = false; \ ++ AioWait *wait_ = &global_aio_wait; \ ++ AioContext *ctx_ = (ctx); \ ++ /* Increment wait_->num_waiters before evaluating cond. */ \ ++ qatomic_inc(&wait_->num_waiters); \ ++ /* Paired with smp_mb in aio_wait_kick(). */ \ ++ smp_mb__after_rmw(); \ ++ if (ctx_ && in_aio_context_home_thread(ctx_)) { \ ++ while ((cond)) { \ ++ aio_poll(ctx_, true); \ ++ waited_ = true; \ ++ } \ ++ } else { \ ++ assert(qemu_get_current_aio_context() == \ ++ qemu_get_aio_context()); \ ++ while ((cond)) { \ ++ aio_poll(qemu_get_aio_context(), true); \ ++ waited_ = true; \ ++ } \ ++ } \ ++ qatomic_dec(&wait_->num_waiters); \ ++ waited_; }) ++ ++#define AIO_WAIT_WHILE(ctx, cond) \ ++ AIO_WAIT_WHILE_INTERNAL(ctx, cond) ++ ++/* TODO replace this with AIO_WAIT_WHILE() in a future patch */ ++#define AIO_WAIT_WHILE_UNLOCKED(ctx, cond) \ ++ AIO_WAIT_WHILE_INTERNAL(ctx, cond) ++ ++/** ++ * aio_wait_kick: ++ * Wake up the main thread if it is waiting on AIO_WAIT_WHILE(). During ++ * synchronous operations performed in an IOThread, the main thread lets the ++ * IOThread's event loop run, waiting for the operation to complete. A ++ * aio_wait_kick() call will wake up the main thread. ++ */ ++void aio_wait_kick(void); ++ ++/** ++ * aio_wait_bh_oneshot: ++ * @ctx: the aio context ++ * @cb: the BH callback function ++ * @opaque: user data for the BH callback function ++ * ++ * Run a BH in @ctx and wait for it to complete. ++ * ++ * Must be called from the main loop thread without @ctx acquired. ++ * Note that main loop event processing may occur. ++ */ ++void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque); ++ ++/** ++ * in_aio_context_home_thread: ++ * @ctx: the aio context ++ * ++ * Return whether we are running in the thread that normally runs @ctx. Note ++ * that acquiring/releasing ctx does not affect the outcome, each AioContext ++ * still only has one home thread that is responsible for running it. ++ */ ++static inline bool in_aio_context_home_thread(AioContext *ctx) ++{ ++ if (ctx == qemu_get_current_aio_context()) { ++ return true; ++ } ++ ++ if (ctx == qemu_get_aio_context()) { ++ return bql_locked(); ++ } else { ++ return false; ++ } ++} ++ ++#endif /* QEMU_AIO_WAIT_H */ +diff --git a/include/block/aio.h b/include/block/aio.h +new file mode 100644 +index 00000000..4ee81936 +--- /dev/null ++++ b/include/block/aio.h +@@ -0,0 +1,723 @@ ++/* ++ * QEMU aio implementation ++ * ++ * Copyright IBM, Corp. 2008 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_AIO_H ++#define QEMU_AIO_H ++ ++#ifdef CONFIG_LINUX_IO_URING ++#include ++#endif ++#include "qemu/coroutine-core.h" ++#include "qemu/queue.h" ++#include "qemu/event_notifier.h" ++#include "qemu/thread.h" ++#include "qemu/timer.h" ++#include "block/graph-lock.h" ++#include "hw/qdev-core.h" ++ ++ ++typedef struct BlockAIOCB BlockAIOCB; ++typedef void BlockCompletionFunc(void *opaque, int ret); ++ ++typedef struct AIOCBInfo { ++ void (*cancel_async)(BlockAIOCB *acb); ++ size_t aiocb_size; ++} AIOCBInfo; ++ ++struct BlockAIOCB { ++ const AIOCBInfo *aiocb_info; ++ BlockDriverState *bs; ++ BlockCompletionFunc *cb; ++ void *opaque; ++ int refcnt; ++}; ++ ++void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, ++ BlockCompletionFunc *cb, void *opaque); ++void qemu_aio_unref(void *p); ++void qemu_aio_ref(void *p); ++ ++typedef struct AioHandler AioHandler; ++typedef QLIST_HEAD(, AioHandler) AioHandlerList; ++typedef void QEMUBHFunc(void *opaque); ++typedef bool AioPollFn(void *opaque); ++typedef void IOHandler(void *opaque); ++ ++struct ThreadPool; ++struct LinuxAioState; ++typedef struct LuringState LuringState; ++ ++/* Is polling disabled? */ ++bool aio_poll_disabled(AioContext *ctx); ++ ++/* Callbacks for file descriptor monitoring implementations */ ++typedef struct { ++ /* ++ * update: ++ * @ctx: the AioContext ++ * @old_node: the existing handler or NULL if this file descriptor is being ++ * monitored for the first time ++ * @new_node: the new handler or NULL if this file descriptor is being ++ * removed ++ * ++ * Add/remove/modify a monitored file descriptor. ++ * ++ * Called with ctx->list_lock acquired. ++ */ ++ void (*update)(AioContext *ctx, AioHandler *old_node, AioHandler *new_node); ++ ++ /* ++ * wait: ++ * @ctx: the AioContext ++ * @ready_list: list for handlers that become ready ++ * @timeout: maximum duration to wait, in nanoseconds ++ * ++ * Wait for file descriptors to become ready and place them on ready_list. ++ * ++ * Called with ctx->list_lock incremented but not locked. ++ * ++ * Returns: number of ready file descriptors. ++ */ ++ int (*wait)(AioContext *ctx, AioHandlerList *ready_list, int64_t timeout); ++ ++ /* ++ * need_wait: ++ * @ctx: the AioContext ++ * ++ * Tell aio_poll() when to stop userspace polling early because ->wait() ++ * has fds ready. ++ * ++ * File descriptor monitoring implementations that cannot poll fd readiness ++ * from userspace should use aio_poll_disabled() here. This ensures that ++ * file descriptors are not starved by handlers that frequently make ++ * progress via userspace polling. ++ * ++ * Returns: true if ->wait() should be called, false otherwise. ++ */ ++ bool (*need_wait)(AioContext *ctx); ++} FDMonOps; ++ ++/* ++ * Each aio_bh_poll() call carves off a slice of the BH list, so that newly ++ * scheduled BHs are not processed until the next aio_bh_poll() call. All ++ * active aio_bh_poll() calls chain their slices together in a list, so that ++ * nested aio_bh_poll() calls process all scheduled bottom halves. ++ */ ++typedef QSLIST_HEAD(, QEMUBH) BHList; ++typedef struct BHListSlice BHListSlice; ++struct BHListSlice { ++ BHList bh_list; ++ QSIMPLEQ_ENTRY(BHListSlice) next; ++}; ++ ++typedef QSLIST_HEAD(, AioHandler) AioHandlerSList; ++ ++struct AioContext { ++ GSource source; ++ ++ /* Used by AioContext users to protect from multi-threaded access. */ ++ QemuRecMutex lock; ++ ++ /* ++ * Keep track of readers and writers of the block layer graph. ++ * This is essential to avoid performing additions and removal ++ * of nodes and edges from block graph while some ++ * other thread is traversing it. ++ */ ++ BdrvGraphRWlock *bdrv_graph; ++ ++ /* The list of registered AIO handlers. Protected by ctx->list_lock. */ ++ AioHandlerList aio_handlers; ++ ++ /* The list of AIO handlers to be deleted. Protected by ctx->list_lock. */ ++ AioHandlerList deleted_aio_handlers; ++ ++ /* Used to avoid unnecessary event_notifier_set calls in aio_notify; ++ * only written from the AioContext home thread, or under the BQL in ++ * the case of the main AioContext. However, it is read from any ++ * thread so it is still accessed with atomic primitives. ++ * ++ * If this field is 0, everything (file descriptors, bottom halves, ++ * timers) will be re-evaluated before the next blocking poll() or ++ * io_uring wait; therefore, the event_notifier_set call can be ++ * skipped. If it is non-zero, you may need to wake up a concurrent ++ * aio_poll or the glib main event loop, making event_notifier_set ++ * necessary. ++ * ++ * Bit 0 is reserved for GSource usage of the AioContext, and is 1 ++ * between a call to aio_ctx_prepare and the next call to aio_ctx_check. ++ * Bits 1-31 simply count the number of active calls to aio_poll ++ * that are in the prepare or poll phase. ++ * ++ * The GSource and aio_poll must use a different mechanism because ++ * there is no certainty that a call to GSource's prepare callback ++ * (via g_main_context_prepare) is indeed followed by check and ++ * dispatch. It's not clear whether this would be a bug, but let's ++ * play safe and allow it---it will just cause extra calls to ++ * event_notifier_set until the next call to dispatch. ++ * ++ * Instead, the aio_poll calls include both the prepare and the ++ * dispatch phase, hence a simple counter is enough for them. ++ */ ++ uint32_t notify_me; ++ ++ /* A lock to protect between QEMUBH and AioHandler adders and deleter, ++ * and to ensure that no callbacks are removed while we're walking and ++ * dispatching them. ++ */ ++ QemuLockCnt list_lock; ++ ++ /* Bottom Halves pending aio_bh_poll() processing */ ++ BHList bh_list; ++ ++ /* Chained BH list slices for each nested aio_bh_poll() call */ ++ QSIMPLEQ_HEAD(, BHListSlice) bh_slice_list; ++ ++ /* Used by aio_notify. ++ * ++ * "notified" is used to avoid expensive event_notifier_test_and_clear ++ * calls. When it is clear, the EventNotifier is clear, or one thread ++ * is going to clear "notified" before processing more events. False ++ * positives are possible, i.e. "notified" could be set even though the ++ * EventNotifier is clear. ++ * ++ * Note that event_notifier_set *cannot* be optimized the same way. For ++ * more information on the problem that would result, see "#ifdef BUG2" ++ * in the docs/aio_notify_accept.promela formal model. ++ */ ++ bool notified; ++ EventNotifier notifier; ++ ++ QSLIST_HEAD(, Coroutine) scheduled_coroutines; ++ QEMUBH *co_schedule_bh; ++ ++ int thread_pool_min; ++ int thread_pool_max; ++ /* Thread pool for performing work and receiving completion callbacks. ++ * Has its own locking. ++ */ ++ struct ThreadPool *thread_pool; ++ ++#ifdef CONFIG_LINUX_AIO ++ struct LinuxAioState *linux_aio; ++#endif ++#ifdef CONFIG_LINUX_IO_URING ++ LuringState *linux_io_uring; ++ ++ /* State for file descriptor monitoring using Linux io_uring */ ++ struct io_uring fdmon_io_uring; ++ AioHandlerSList submit_list; ++#endif ++ ++ /* TimerLists for calling timers - one per clock type. Has its own ++ * locking. ++ */ ++ QEMUTimerListGroup tlg; ++ ++ /* Number of AioHandlers without .io_poll() */ ++ int poll_disable_cnt; ++ ++ /* Polling mode parameters */ ++ int64_t poll_ns; /* current polling time in nanoseconds */ ++ int64_t poll_max_ns; /* maximum polling time in nanoseconds */ ++ int64_t poll_grow; /* polling time growth factor */ ++ int64_t poll_shrink; /* polling time shrink factor */ ++ ++ /* AIO engine parameters */ ++ int64_t aio_max_batch; /* maximum number of requests in a batch */ ++ ++ /* ++ * List of handlers participating in userspace polling. Protected by ++ * ctx->list_lock. Iterated and modified mostly by the event loop thread ++ * from aio_poll() with ctx->list_lock incremented. aio_set_fd_handler() ++ * only touches the list to delete nodes if ctx->list_lock's count is zero. ++ */ ++ AioHandlerList poll_aio_handlers; ++ ++ /* Are we in polling mode or monitoring file descriptors? */ ++ bool poll_started; ++ ++ /* epoll(7) state used when built with CONFIG_EPOLL */ ++ int epollfd; ++ ++ const FDMonOps *fdmon_ops; ++}; ++ ++/** ++ * aio_context_new: Allocate a new AioContext. ++ * ++ * AioContext provide a mini event-loop that can be waited on synchronously. ++ * They also provide bottom halves, a service to execute a piece of code ++ * as soon as possible. ++ */ ++AioContext *aio_context_new(Error **errp); ++ ++/** ++ * aio_context_ref: ++ * @ctx: The AioContext to operate on. ++ * ++ * Add a reference to an AioContext. ++ */ ++void aio_context_ref(AioContext *ctx); ++ ++/** ++ * aio_context_unref: ++ * @ctx: The AioContext to operate on. ++ * ++ * Drop a reference to an AioContext. ++ */ ++void aio_context_unref(AioContext *ctx); ++ ++/** ++ * aio_bh_schedule_oneshot_full: Allocate a new bottom half structure that will ++ * run only once and as soon as possible. ++ * ++ * @name: A human-readable identifier for debugging purposes. ++ */ ++void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, ++ const char *name); ++ ++/** ++ * aio_bh_schedule_oneshot: Allocate a new bottom half structure that will run ++ * only once and as soon as possible. ++ * ++ * A convenience wrapper for aio_bh_schedule_oneshot_full() that uses cb as the ++ * name string. ++ */ ++#define aio_bh_schedule_oneshot(ctx, cb, opaque) \ ++ aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb))) ++ ++/** ++ * aio_bh_new_full: Allocate a new bottom half structure. ++ * ++ * Bottom halves are lightweight callbacks whose invocation is guaranteed ++ * to be wait-free, thread-safe and signal-safe. The #QEMUBH structure ++ * is opaque and must be allocated prior to its use. ++ * ++ * @name: A human-readable identifier for debugging purposes. ++ * @reentrancy_guard: A guard set when entering a cb to prevent ++ * device-reentrancy issues ++ */ ++QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, ++ const char *name, MemReentrancyGuard *reentrancy_guard); ++ ++/** ++ * aio_bh_new: Allocate a new bottom half structure ++ * ++ * A convenience wrapper for aio_bh_new_full() that uses the cb as the name ++ * string. ++ */ ++#define aio_bh_new(ctx, cb, opaque) \ ++ aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb)), NULL) ++ ++/** ++ * aio_bh_new_guarded: Allocate a new bottom half structure with a ++ * reentrancy_guard ++ * ++ * A convenience wrapper for aio_bh_new_full() that uses the cb as the name ++ * string. ++ */ ++#define aio_bh_new_guarded(ctx, cb, opaque, guard) \ ++ aio_bh_new_full((ctx), (cb), (opaque), (stringify(cb)), guard) ++ ++/** ++ * aio_notify: Force processing of pending events. ++ * ++ * Similar to signaling a condition variable, aio_notify forces ++ * aio_poll to exit, so that the next call will re-examine pending events. ++ * The caller of aio_notify will usually call aio_poll again very soon, ++ * or go through another iteration of the GLib main loop. Hence, aio_notify ++ * also has the side effect of recalculating the sets of file descriptors ++ * that the main loop waits for. ++ * ++ * Calling aio_notify is rarely necessary, because for example scheduling ++ * a bottom half calls it already. ++ */ ++void aio_notify(AioContext *ctx); ++ ++/** ++ * aio_notify_accept: Acknowledge receiving an aio_notify. ++ * ++ * aio_notify() uses an EventNotifier in order to wake up a sleeping ++ * aio_poll() or g_main_context_iteration(). Calls to aio_notify() are ++ * usually rare, but the AioContext has to clear the EventNotifier on ++ * every aio_poll() or g_main_context_iteration() in order to avoid ++ * busy waiting. This event_notifier_test_and_clear() cannot be done ++ * using the usual aio_context_set_event_notifier(), because it must ++ * be done before processing all events (file descriptors, bottom halves, ++ * timers). ++ * ++ * aio_notify_accept() is an optimized event_notifier_test_and_clear() ++ * that is specific to an AioContext's notifier; it is used internally ++ * to clear the EventNotifier only if aio_notify() had been called. ++ */ ++void aio_notify_accept(AioContext *ctx); ++ ++/** ++ * aio_bh_call: Executes callback function of the specified BH. ++ */ ++void aio_bh_call(QEMUBH *bh); ++ ++/** ++ * aio_bh_poll: Poll bottom halves for an AioContext. ++ * ++ * These are internal functions used by the QEMU main loop. ++ * And notice that multiple occurrences of aio_bh_poll cannot ++ * be called concurrently ++ */ ++int aio_bh_poll(AioContext *ctx); ++ ++/** ++ * qemu_bh_schedule: Schedule a bottom half. ++ * ++ * Scheduling a bottom half interrupts the main loop and causes the ++ * execution of the callback that was passed to qemu_bh_new. ++ * ++ * Bottom halves that are scheduled from a bottom half handler are instantly ++ * invoked. This can create an infinite loop if a bottom half handler ++ * schedules itself. ++ * ++ * @bh: The bottom half to be scheduled. ++ */ ++void qemu_bh_schedule(QEMUBH *bh); ++ ++/** ++ * qemu_bh_cancel: Cancel execution of a bottom half. ++ * ++ * Canceling execution of a bottom half undoes the effect of calls to ++ * qemu_bh_schedule without freeing its resources yet. While cancellation ++ * itself is also wait-free and thread-safe, it can of course race with the ++ * loop that executes bottom halves unless you are holding the iothread ++ * mutex. This makes it mostly useless if you are not holding the mutex. ++ * ++ * @bh: The bottom half to be canceled. ++ */ ++void qemu_bh_cancel(QEMUBH *bh); ++ ++/** ++ *qemu_bh_delete: Cancel execution of a bottom half and free its resources. ++ * ++ * Deleting a bottom half frees the memory that was allocated for it by ++ * qemu_bh_new. It also implies canceling the bottom half if it was ++ * scheduled. ++ * This func is async. The bottom half will do the delete action at the finial ++ * end. ++ * ++ * @bh: The bottom half to be deleted. ++ */ ++void qemu_bh_delete(QEMUBH *bh); ++ ++/* Return whether there are any pending callbacks from the GSource ++ * attached to the AioContext, before g_poll is invoked. ++ * ++ * This is used internally in the implementation of the GSource. ++ */ ++bool aio_prepare(AioContext *ctx); ++ ++/* Return whether there are any pending callbacks from the GSource ++ * attached to the AioContext, after g_poll is invoked. ++ * ++ * This is used internally in the implementation of the GSource. ++ */ ++bool aio_pending(AioContext *ctx); ++ ++/* Dispatch any pending callbacks from the GSource attached to the AioContext. ++ * ++ * This is used internally in the implementation of the GSource. ++ */ ++void aio_dispatch(AioContext *ctx); ++ ++/* Progress in completing AIO work to occur. This can issue new pending ++ * aio as a result of executing I/O completion or bh callbacks. ++ * ++ * Return whether any progress was made by executing AIO or bottom half ++ * handlers. If @blocking == true, this should always be true except ++ * if someone called aio_notify. ++ * ++ * If there are no pending bottom halves, but there are pending AIO ++ * operations, it may not be possible to make any progress without ++ * blocking. If @blocking is true, this function will wait until one ++ * or more AIO events have completed, to ensure something has moved ++ * before returning. ++ */ ++bool no_coroutine_fn aio_poll(AioContext *ctx, bool blocking); ++ ++/* Register a file descriptor and associated callbacks. Behaves very similarly ++ * to qemu_set_fd_handler. Unlike qemu_set_fd_handler, these callbacks will ++ * be invoked when using aio_poll(). ++ * ++ * Code that invokes AIO completion functions should rely on this function ++ * instead of qemu_set_fd_handler[2]. ++ */ ++void aio_set_fd_handler(AioContext *ctx, ++ int fd, ++ IOHandler *io_read, ++ IOHandler *io_write, ++ AioPollFn *io_poll, ++ IOHandler *io_poll_ready, ++ void *opaque); ++ ++/* Register an event notifier and associated callbacks. Behaves very similarly ++ * to event_notifier_set_handler. Unlike event_notifier_set_handler, these callbacks ++ * will be invoked when using aio_poll(). ++ * ++ * Code that invokes AIO completion functions should rely on this function ++ * instead of event_notifier_set_handler. ++ */ ++void aio_set_event_notifier(AioContext *ctx, ++ EventNotifier *notifier, ++ EventNotifierHandler *io_read, ++ AioPollFn *io_poll, ++ EventNotifierHandler *io_poll_ready); ++ ++/* ++ * Set polling begin/end callbacks for an event notifier that has already been ++ * registered with aio_set_event_notifier. Do nothing if the event notifier is ++ * not registered. ++ * ++ * Note that if the io_poll_end() callback (or the entire notifier) is removed ++ * during polling, it will not be called, so an io_poll_begin() is not ++ * necessarily always followed by an io_poll_end(). ++ */ ++void aio_set_event_notifier_poll(AioContext *ctx, ++ EventNotifier *notifier, ++ EventNotifierHandler *io_poll_begin, ++ EventNotifierHandler *io_poll_end); ++ ++/* Return a GSource that lets the main loop poll the file descriptors attached ++ * to this AioContext. ++ */ ++GSource *aio_get_g_source(AioContext *ctx); ++ ++/* Return the ThreadPool bound to this AioContext */ ++struct ThreadPool *aio_get_thread_pool(AioContext *ctx); ++ ++/* Setup the LinuxAioState bound to this AioContext */ ++struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); ++ ++/* Return the LinuxAioState bound to this AioContext */ ++struct LinuxAioState *aio_get_linux_aio(AioContext *ctx); ++ ++/* Setup the LuringState bound to this AioContext */ ++LuringState *aio_setup_linux_io_uring(AioContext *ctx, Error **errp); ++ ++/* Return the LuringState bound to this AioContext */ ++LuringState *aio_get_linux_io_uring(AioContext *ctx); ++/** ++ * aio_timer_new_with_attrs: ++ * @ctx: the aio context ++ * @type: the clock type ++ * @scale: the scale ++ * @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_ values ++ * to assign ++ * @cb: the callback to call on timer expiry ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Allocate a new timer (with attributes) attached to the context @ctx. ++ * The function is responsible for memory allocation. ++ * ++ * The preferred interface is aio_timer_init or aio_timer_init_with_attrs. ++ * Use that unless you really need dynamic memory allocation. ++ * ++ * Returns: a pointer to the new timer ++ */ ++static inline QEMUTimer *aio_timer_new_with_attrs(AioContext *ctx, ++ QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ return timer_new_full(&ctx->tlg, type, scale, attributes, cb, opaque); ++} ++ ++/** ++ * aio_timer_new: ++ * @ctx: the aio context ++ * @type: the clock type ++ * @scale: the scale ++ * @cb: the callback to call on timer expiry ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Allocate a new timer attached to the context @ctx. ++ * See aio_timer_new_with_attrs for details. ++ * ++ * Returns: a pointer to the new timer ++ */ ++static inline QEMUTimer *aio_timer_new(AioContext *ctx, QEMUClockType type, ++ int scale, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ return timer_new_full(&ctx->tlg, type, scale, 0, cb, opaque); ++} ++ ++/** ++ * aio_timer_init_with_attrs: ++ * @ctx: the aio context ++ * @ts: the timer ++ * @type: the clock type ++ * @scale: the scale ++ * @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_ values ++ * to assign ++ * @cb: the callback to call on timer expiry ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialise a new timer (with attributes) attached to the context @ctx. ++ * The caller is responsible for memory allocation. ++ */ ++static inline void aio_timer_init_with_attrs(AioContext *ctx, ++ QEMUTimer *ts, QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init_full(ts, &ctx->tlg, type, scale, attributes, cb, opaque); ++} ++ ++/** ++ * aio_timer_init: ++ * @ctx: the aio context ++ * @ts: the timer ++ * @type: the clock type ++ * @scale: the scale ++ * @cb: the callback to call on timer expiry ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialise a new timer attached to the context @ctx. ++ * See aio_timer_init_with_attrs for details. ++ */ ++static inline void aio_timer_init(AioContext *ctx, ++ QEMUTimer *ts, QEMUClockType type, ++ int scale, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init_full(ts, &ctx->tlg, type, scale, 0, cb, opaque); ++} ++ ++/** ++ * aio_compute_timeout: ++ * @ctx: the aio context ++ * ++ * Compute the timeout that a blocking aio_poll should use. ++ */ ++int64_t aio_compute_timeout(AioContext *ctx); ++ ++/** ++ * aio_co_schedule: ++ * @ctx: the aio context ++ * @co: the coroutine ++ * ++ * Start a coroutine on a remote AioContext. ++ * ++ * The coroutine must not be entered by anyone else while aio_co_schedule() ++ * is active. In addition the coroutine must have yielded unless ctx ++ * is the context in which the coroutine is running (i.e. the value of ++ * qemu_get_current_aio_context() from the coroutine itself). ++ */ ++void aio_co_schedule(AioContext *ctx, Coroutine *co); ++ ++/** ++ * aio_co_reschedule_self: ++ * @new_ctx: the new context ++ * ++ * Move the currently running coroutine to new_ctx. If the coroutine is already ++ * running in new_ctx, do nothing. ++ * ++ * Note that this function cannot reschedule from iohandler_ctx to ++ * qemu_aio_context. ++ */ ++void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx); ++ ++/** ++ * aio_co_wake: ++ * @co: the coroutine ++ * ++ * Restart a coroutine on the AioContext where it was running last, thus ++ * preventing coroutines from jumping from one context to another when they ++ * go to sleep. ++ * ++ * aio_co_wake may be executed either in coroutine or non-coroutine ++ * context. The coroutine must not be entered by anyone else while ++ * aio_co_wake() is active. ++ */ ++void aio_co_wake(Coroutine *co); ++ ++/** ++ * aio_co_enter: ++ * @ctx: the context to run the coroutine ++ * @co: the coroutine to run ++ * ++ * Enter a coroutine in the specified AioContext. ++ */ ++void aio_co_enter(AioContext *ctx, Coroutine *co); ++ ++/** ++ * Return the AioContext whose event loop runs in the current thread. ++ * ++ * If called from an IOThread this will be the IOThread's AioContext. If ++ * called from the main thread or with the "big QEMU lock" taken it ++ * will be the main loop AioContext. ++ * ++ * Note that the return value is never the main loop's iohandler_ctx and the ++ * return value is the main loop AioContext instead. ++ */ ++AioContext *qemu_get_current_aio_context(void); ++ ++void qemu_set_current_aio_context(AioContext *ctx); ++ ++/** ++ * aio_context_setup: ++ * @ctx: the aio context ++ * ++ * Initialize the aio context. ++ */ ++void aio_context_setup(AioContext *ctx); ++ ++/** ++ * aio_context_destroy: ++ * @ctx: the aio context ++ * ++ * Destroy the aio context. ++ */ ++void aio_context_destroy(AioContext *ctx); ++ ++/* Used internally, do not call outside AioContext code */ ++void aio_context_use_g_source(AioContext *ctx); ++ ++/** ++ * aio_context_set_poll_params: ++ * @ctx: the aio context ++ * @max_ns: how long to busy poll for, in nanoseconds ++ * @grow: polling time growth factor ++ * @shrink: polling time shrink factor ++ * ++ * Poll mode can be disabled by setting poll_max_ns to 0. ++ */ ++void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, ++ int64_t grow, int64_t shrink, ++ Error **errp); ++ ++/** ++ * aio_context_set_aio_params: ++ * @ctx: the aio context ++ * @max_batch: maximum number of requests in a batch, 0 means that the ++ * engine will use its default ++ */ ++void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch); ++ ++/** ++ * aio_context_set_thread_pool_params: ++ * @ctx: the aio context ++ * @min: min number of threads to have readily available in the thread pool ++ * @min: max number of threads the thread pool can contain ++ */ ++void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, ++ int64_t max, Error **errp); ++#endif +diff --git a/include/block/aio_task.h b/include/block/aio_task.h +new file mode 100644 +index 00000000..18a9c41f +--- /dev/null ++++ b/include/block/aio_task.h +@@ -0,0 +1,52 @@ ++/* ++ * Aio tasks loops ++ * ++ * Copyright (c) 2019 Virtuozzo International GmbH. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_AIO_TASK_H ++#define BLOCK_AIO_TASK_H ++ ++typedef struct AioTaskPool AioTaskPool; ++typedef struct AioTask AioTask; ++typedef int coroutine_fn (*AioTaskFunc)(AioTask *task); ++struct AioTask { ++ AioTaskPool *pool; ++ AioTaskFunc func; ++ int ret; ++}; ++ ++AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks); ++void aio_task_pool_free(AioTaskPool *); ++ ++/* error code of failed task or 0 if all is OK */ ++int aio_task_pool_status(AioTaskPool *pool); ++ ++bool aio_task_pool_empty(AioTaskPool *pool); ++ ++/* User provides filled @task, however task->pool will be set automatically */ ++void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task); ++ ++void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool); ++void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool); ++void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool); ++ ++#endif /* BLOCK_AIO_TASK_H */ +diff --git a/include/block/block-common.h b/include/block/block-common.h +new file mode 100644 +index 00000000..338fe5ff +--- /dev/null ++++ b/include/block/block-common.h +@@ -0,0 +1,567 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_COMMON_H ++#define BLOCK_COMMON_H ++ ++#include "qapi/qapi-types-block-core.h" ++#include "qemu/queue.h" ++ ++/* ++ * co_wrapper{*}: Function specifiers used by block-coroutine-wrapper.py ++ * ++ * Function specifiers, which do nothing but mark functions to be ++ * generated by scripts/block-coroutine-wrapper.py ++ * ++ * Usage: read docs/devel/block-coroutine-wrapper.rst ++ * ++ * There are 4 kind of specifiers: ++ * - co_wrapper functions can be called by only non-coroutine context, because ++ * they always generate a new coroutine. ++ * - co_wrapper_mixed functions can be called by both coroutine and ++ * non-coroutine context. ++ * - co_wrapper_bdrv_rdlock are co_wrapper functions but automatically take and ++ * release the graph rdlock when creating a new coroutine ++ * - co_wrapper_mixed_bdrv_rdlock are co_wrapper_mixed functions but ++ * automatically take and release the graph rdlock when creating a new ++ * coroutine. ++ * ++ * These functions should not be called from a coroutine_fn; instead, ++ * call the wrapped function directly. ++ */ ++#define co_wrapper no_coroutine_fn ++#define co_wrapper_mixed no_coroutine_fn coroutine_mixed_fn ++#define co_wrapper_bdrv_rdlock no_coroutine_fn ++#define co_wrapper_mixed_bdrv_rdlock no_coroutine_fn coroutine_mixed_fn ++ ++/* ++ * no_co_wrapper: Function specifier used by block-coroutine-wrapper.py ++ * ++ * Function specifier which does nothing but mark functions to be generated by ++ * scripts/block-coroutine-wrapper.py. ++ * ++ * A no_co_wrapper function declaration creates a coroutine_fn wrapper around ++ * functions that must not be called in coroutine context. It achieves this by ++ * scheduling a BH in the bottom half that runs the respective non-coroutine ++ * function. The coroutine yields after scheduling the BH and is reentered when ++ * the wrapped function returns. ++ * ++ * A no_co_wrapper_bdrv_rdlock function is a no_co_wrapper function that ++ * automatically takes the graph rdlock when calling the wrapped function. In ++ * the same way, no_co_wrapper_bdrv_wrlock functions automatically take the ++ * graph wrlock. ++ */ ++#define no_co_wrapper ++#define no_co_wrapper_bdrv_rdlock ++#define no_co_wrapper_bdrv_wrlock ++ ++#include "block/blockjob.h" ++ ++/* block.c */ ++typedef struct BlockDriver BlockDriver; ++typedef struct BdrvChild BdrvChild; ++typedef struct BdrvChildClass BdrvChildClass; ++ ++typedef enum BlockZoneOp { ++ BLK_ZO_OPEN, ++ BLK_ZO_CLOSE, ++ BLK_ZO_FINISH, ++ BLK_ZO_RESET, ++} BlockZoneOp; ++ ++typedef enum BlockZoneModel { ++ BLK_Z_NONE = 0x0, /* Regular block device */ ++ BLK_Z_HM = 0x1, /* Host-managed zoned block device */ ++ BLK_Z_HA = 0x2, /* Host-aware zoned block device */ ++} BlockZoneModel; ++ ++typedef enum BlockZoneState { ++ BLK_ZS_NOT_WP = 0x0, ++ BLK_ZS_EMPTY = 0x1, ++ BLK_ZS_IOPEN = 0x2, ++ BLK_ZS_EOPEN = 0x3, ++ BLK_ZS_CLOSED = 0x4, ++ BLK_ZS_RDONLY = 0xD, ++ BLK_ZS_FULL = 0xE, ++ BLK_ZS_OFFLINE = 0xF, ++} BlockZoneState; ++ ++typedef enum BlockZoneType { ++ BLK_ZT_CONV = 0x1, /* Conventional random writes supported */ ++ BLK_ZT_SWR = 0x2, /* Sequential writes required */ ++ BLK_ZT_SWP = 0x3, /* Sequential writes preferred */ ++} BlockZoneType; ++ ++/* ++ * Zone descriptor data structure. ++ * Provides information on a zone with all position and size values in bytes. ++ */ ++typedef struct BlockZoneDescriptor { ++ uint64_t start; ++ uint64_t length; ++ uint64_t cap; ++ uint64_t wp; ++ BlockZoneType type; ++ BlockZoneState state; ++} BlockZoneDescriptor; ++ ++/* ++ * Track write pointers of a zone in bytes. ++ */ ++typedef struct BlockZoneWps { ++ CoMutex colock; ++ uint64_t wp[]; ++} BlockZoneWps; ++ ++typedef struct BlockDriverInfo { ++ /* in bytes, 0 if irrelevant */ ++ int cluster_size; ++ /* ++ * A fraction of cluster_size, if supported (currently QCOW2 only); if ++ * disabled or unsupported, set equal to cluster_size. ++ */ ++ int subcluster_size; ++ /* offset at which the VM state can be saved (0 if not possible) */ ++ int64_t vm_state_offset; ++ bool is_dirty; ++ /* ++ * True if this block driver only supports compressed writes ++ */ ++ bool needs_compressed_writes; ++} BlockDriverInfo; ++ ++typedef struct BlockFragInfo { ++ uint64_t allocated_clusters; ++ uint64_t total_clusters; ++ uint64_t fragmented_clusters; ++ uint64_t compressed_clusters; ++} BlockFragInfo; ++ ++typedef enum { ++ BDRV_REQ_COPY_ON_READ = 0x1, ++ BDRV_REQ_ZERO_WRITE = 0x2, ++ ++ /* ++ * The BDRV_REQ_MAY_UNMAP flag is used in write_zeroes requests to indicate ++ * that the block driver should unmap (discard) blocks if it is guaranteed ++ * that the result will read back as zeroes. The flag is only passed to the ++ * driver if the block device is opened with BDRV_O_UNMAP. ++ */ ++ BDRV_REQ_MAY_UNMAP = 0x4, ++ ++ /* ++ * An optimization hint when all QEMUIOVector elements are within ++ * previously registered bdrv_register_buf() memory ranges. ++ * ++ * Code that replaces the user's QEMUIOVector elements with bounce buffers ++ * must take care to clear this flag. ++ */ ++ BDRV_REQ_REGISTERED_BUF = 0x8, ++ ++ BDRV_REQ_FUA = 0x10, ++ BDRV_REQ_WRITE_COMPRESSED = 0x20, ++ ++ /* ++ * Signifies that this write request will not change the visible disk ++ * content. ++ */ ++ BDRV_REQ_WRITE_UNCHANGED = 0x40, ++ ++ /* ++ * Forces request serialisation. Use only with write requests. ++ */ ++ BDRV_REQ_SERIALISING = 0x80, ++ ++ /* ++ * Execute the request only if the operation can be offloaded or otherwise ++ * be executed efficiently, but return an error instead of using a slow ++ * fallback. ++ */ ++ BDRV_REQ_NO_FALLBACK = 0x100, ++ ++ /* ++ * BDRV_REQ_PREFETCH makes sense only in the context of copy-on-read ++ * (i.e., together with the BDRV_REQ_COPY_ON_READ flag or when a COR ++ * filter is involved), in which case it signals that the COR operation ++ * need not read the data into memory (qiov) but only ensure they are ++ * copied to the top layer (i.e., that COR operation is done). ++ */ ++ BDRV_REQ_PREFETCH = 0x200, ++ ++ /* ++ * If we need to wait for other requests, just fail immediately. Used ++ * only together with BDRV_REQ_SERIALISING. Used only with requests aligned ++ * to request_alignment (corresponding assertions are in block/io.c). ++ */ ++ BDRV_REQ_NO_WAIT = 0x400, ++ ++ /* Mask of valid flags */ ++ BDRV_REQ_MASK = 0x7ff, ++} BdrvRequestFlags; ++ ++#define BDRV_O_NO_SHARE 0x0001 /* don't share permissions */ ++#define BDRV_O_RDWR 0x0002 ++#define BDRV_O_RESIZE 0x0004 /* request permission for resizing the node */ ++#define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save ++ writes in a snapshot */ ++#define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */ ++#define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */ ++#define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the ++ thread pool */ ++#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */ ++#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */ ++#define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */ ++#define BDRV_O_INACTIVE 0x0800 /* consistency hint for migration handoff */ ++#define BDRV_O_CHECK 0x1000 /* open solely for consistency check */ ++#define BDRV_O_ALLOW_RDWR 0x2000 /* allow reopen to change from r/o to r/w */ ++#define BDRV_O_UNMAP 0x4000 /* execute guest UNMAP/TRIM operations */ ++#define BDRV_O_PROTOCOL 0x8000 /* if no block driver is explicitly given: ++ select an appropriate protocol driver, ++ ignoring the format layer */ ++#define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */ ++#define BDRV_O_AUTO_RDONLY 0x20000 /* degrade to read-only if opening ++ read-write fails */ ++#define BDRV_O_IO_URING 0x40000 /* use io_uring instead of the thread pool */ ++ ++#define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */ ++ ++#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) ++ ++ ++/* Option names of options parsed by the block layer */ ++ ++#define BDRV_OPT_CACHE_WB "cache.writeback" ++#define BDRV_OPT_CACHE_DIRECT "cache.direct" ++#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush" ++#define BDRV_OPT_READ_ONLY "read-only" ++#define BDRV_OPT_AUTO_READ_ONLY "auto-read-only" ++#define BDRV_OPT_DISCARD "discard" ++#define BDRV_OPT_FORCE_SHARE "force-share" ++ ++ ++#define BDRV_SECTOR_BITS 9 ++#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) ++ ++/* ++ * Get the first most significant bit of wp. If it is zero, then ++ * the zone type is SWR. ++ */ ++#define BDRV_ZT_IS_CONV(wp) (wp & (1ULL << 63)) ++ ++#define BDRV_REQUEST_MAX_SECTORS MIN_CONST(SIZE_MAX >> BDRV_SECTOR_BITS, \ ++ INT_MAX >> BDRV_SECTOR_BITS) ++#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) ++ ++/* ++ * We want allow aligning requests and disk length up to any 32bit alignment ++ * and don't afraid of overflow. ++ * To achieve it, and in the same time use some pretty number as maximum disk ++ * size, let's define maximum "length" (a limit for any offset/bytes request and ++ * for disk size) to be the greatest power of 2 less than INT64_MAX. ++ */ ++#define BDRV_MAX_ALIGNMENT (1L << 30) ++#define BDRV_MAX_LENGTH (QEMU_ALIGN_DOWN(INT64_MAX, BDRV_MAX_ALIGNMENT)) ++ ++/* ++ * Allocation status flags for bdrv_block_status() and friends. ++ * ++ * Public flags: ++ * BDRV_BLOCK_DATA: allocation for data at offset is tied to this layer ++ * BDRV_BLOCK_ZERO: offset reads as zero ++ * BDRV_BLOCK_OFFSET_VALID: an associated offset exists for accessing raw data ++ * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this ++ * layer rather than any backing, set by block layer ++ * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this ++ * layer, set by block layer ++ * BDRV_BLOCK_COMPRESSED: the underlying data is compressed; only valid for ++ * the formats supporting compression: qcow, qcow2 ++ * ++ * Internal flags: ++ * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request ++ * that the block layer recompute the answer from the returned ++ * BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID. ++ * BDRV_BLOCK_RECURSE: request that the block layer will recursively search for ++ * zeroes in file child of current block node inside ++ * returned region. Only valid together with both ++ * BDRV_BLOCK_DATA and BDRV_BLOCK_OFFSET_VALID. Should not ++ * appear with BDRV_BLOCK_ZERO. ++ * ++ * If BDRV_BLOCK_OFFSET_VALID is set, the map parameter represents the ++ * host offset within the returned BDS that is allocated for the ++ * corresponding raw guest data. However, whether that offset ++ * actually contains data also depends on BDRV_BLOCK_DATA, as follows: ++ * ++ * DATA ZERO OFFSET_VALID ++ * t t t sectors read as zero, returned file is zero at offset ++ * t f t sectors read as valid from file at offset ++ * f t t sectors preallocated, read as zero, returned file not ++ * necessarily zero at offset ++ * f f t sectors preallocated but read from backing_hd, ++ * returned file contains garbage at offset ++ * t t f sectors preallocated, read as zero, unknown offset ++ * t f f sectors read from unknown file or offset ++ * f t f not allocated or unknown offset, read as zero ++ * f f f not allocated or unknown offset, read from backing_hd ++ */ ++#define BDRV_BLOCK_DATA 0x01 ++#define BDRV_BLOCK_ZERO 0x02 ++#define BDRV_BLOCK_OFFSET_VALID 0x04 ++#define BDRV_BLOCK_RAW 0x08 ++#define BDRV_BLOCK_ALLOCATED 0x10 ++#define BDRV_BLOCK_EOF 0x20 ++#define BDRV_BLOCK_RECURSE 0x40 ++#define BDRV_BLOCK_COMPRESSED 0x80 ++ ++typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; ++ ++typedef struct BDRVReopenState { ++ BlockDriverState *bs; ++ int flags; ++ BlockdevDetectZeroesOptions detect_zeroes; ++ bool backing_missing; ++ BlockDriverState *old_backing_bs; /* keep pointer for permissions update */ ++ BlockDriverState *old_file_bs; /* keep pointer for permissions update */ ++ QDict *options; ++ QDict *explicit_options; ++ void *opaque; ++} BDRVReopenState; ++ ++/* ++ * Block operation types ++ */ ++typedef enum BlockOpType { ++ BLOCK_OP_TYPE_BACKUP_SOURCE, ++ BLOCK_OP_TYPE_BACKUP_TARGET, ++ BLOCK_OP_TYPE_CHANGE, ++ BLOCK_OP_TYPE_COMMIT_SOURCE, ++ BLOCK_OP_TYPE_COMMIT_TARGET, ++ BLOCK_OP_TYPE_DATAPLANE, ++ BLOCK_OP_TYPE_DRIVE_DEL, ++ BLOCK_OP_TYPE_EJECT, ++ BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, ++ BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, ++ BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, ++ BLOCK_OP_TYPE_MIRROR_SOURCE, ++ BLOCK_OP_TYPE_MIRROR_TARGET, ++ BLOCK_OP_TYPE_RESIZE, ++ BLOCK_OP_TYPE_STREAM, ++ BLOCK_OP_TYPE_REPLACE, ++ BLOCK_OP_TYPE_MAX, ++} BlockOpType; ++ ++/* Block node permission constants */ ++enum { ++ /** ++ * A user that has the "permission" of consistent reads is guaranteed that ++ * their view of the contents of the block device is complete and ++ * self-consistent, representing the contents of a disk at a specific ++ * point. ++ * ++ * For most block devices (including their backing files) this is true, but ++ * the property cannot be maintained in a few situations like for ++ * intermediate nodes of a commit block job. ++ */ ++ BLK_PERM_CONSISTENT_READ = 0x01, ++ ++ /** This permission is required to change the visible disk contents. */ ++ BLK_PERM_WRITE = 0x02, ++ ++ /** ++ * This permission (which is weaker than BLK_PERM_WRITE) is both enough and ++ * required for writes to the block node when the caller promises that ++ * the visible disk content doesn't change. ++ * ++ * As the BLK_PERM_WRITE permission is strictly stronger, either is ++ * sufficient to perform an unchanging write. ++ */ ++ BLK_PERM_WRITE_UNCHANGED = 0x04, ++ ++ /** This permission is required to change the size of a block node. */ ++ BLK_PERM_RESIZE = 0x08, ++ ++ /** ++ * There was a now-removed bit BLK_PERM_GRAPH_MOD, with value of 0x10. QEMU ++ * 6.1 and earlier may still lock the corresponding byte in block/file-posix ++ * locking. So, implementing some new permission should be very careful to ++ * not interfere with this old unused thing. ++ */ ++ ++ BLK_PERM_ALL = 0x0f, ++ ++ DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ ++ | BLK_PERM_WRITE ++ | BLK_PERM_WRITE_UNCHANGED ++ | BLK_PERM_RESIZE, ++ ++ DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH, ++}; ++ ++/* ++ * Flags that parent nodes assign to child nodes to specify what kind of ++ * role(s) they take. ++ * ++ * 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 { ++ /* ++ * This child stores data. ++ * Any node may have an arbitrary number of such children. ++ */ ++ BDRV_CHILD_DATA = (1 << 0), ++ ++ /* ++ * This child stores metadata. ++ * Any node may have an arbitrary number of metadata-storing ++ * children. ++ */ ++ BDRV_CHILD_METADATA = (1 << 1), ++ ++ /* ++ * A child that always presents exactly the same visible data as ++ * the parent, e.g. by virtue of the parent forwarding all reads ++ * and writes. ++ * This flag is mutually exclusive with DATA, METADATA, and COW. ++ * Any node may have at most one filtered child at a time. ++ */ ++ BDRV_CHILD_FILTERED = (1 << 2), ++ ++ /* ++ * Child from which to read all data that isn't allocated in the ++ * parent (i.e., the backing child); such data is copied to the ++ * parent through COW (and optionally COR). ++ * This field is mutually exclusive with DATA, METADATA, and ++ * FILTERED. ++ * Any node may have at most one such backing child at a time. ++ */ ++ BDRV_CHILD_COW = (1 << 3), ++ ++ /* ++ * The primary child. For most drivers, this is the child whose ++ * filename applies best to the parent node. ++ * Any node may have at most one primary child at a time. ++ */ ++ BDRV_CHILD_PRIMARY = (1 << 4), ++ ++ /* Useful combination of flags */ ++ BDRV_CHILD_IMAGE = BDRV_CHILD_DATA ++ | BDRV_CHILD_METADATA ++ | BDRV_CHILD_PRIMARY, ++}; ++ ++/* Mask of BdrvChildRoleBits values */ ++typedef unsigned int BdrvChildRole; ++ ++typedef struct BdrvCheckResult { ++ int corruptions; ++ int leaks; ++ int check_errors; ++ int corruptions_fixed; ++ int leaks_fixed; ++ int64_t image_end_offset; ++ BlockFragInfo bfi; ++} BdrvCheckResult; ++ ++typedef enum { ++ BDRV_FIX_LEAKS = 1, ++ BDRV_FIX_ERRORS = 2, ++} BdrvCheckMode; ++ ++typedef struct BlockSizes { ++ uint32_t phys; ++ uint32_t log; ++} BlockSizes; ++ ++typedef struct HDGeometry { ++ uint32_t heads; ++ uint32_t sectors; ++ uint32_t cylinders; ++} HDGeometry; ++ ++/* ++ * Common functions that are neither I/O nor Global State. ++ * ++ * These functions must never call any function from other categories ++ * (I/O, "I/O or GS", Global State) except this one, but can be invoked by ++ * all of them. ++ */ ++ ++char *bdrv_perm_names(uint64_t perm); ++uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); ++ ++void bdrv_init_with_whitelist(void); ++bool bdrv_uses_whitelist(void); ++int bdrv_is_whitelisted(BlockDriver *drv, bool read_only); ++ ++int bdrv_parse_aio(const char *mode, int *flags); ++int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough); ++int bdrv_parse_discard_flags(const char *mode, int *flags); ++ ++int path_has_protocol(const char *path); ++int path_is_absolute(const char *path); ++char *path_combine(const char *base_path, const char *filename); ++ ++char *bdrv_get_full_backing_filename_from_filename(const char *backed, ++ const char *backing, ++ Error **errp); ++ ++#endif /* BLOCK_COMMON_H */ +diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h +new file mode 100644 +index 00000000..bd7cecd1 +--- /dev/null ++++ b/include/block/block-global-state.h +@@ -0,0 +1,305 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_GLOBAL_STATE_H ++#define BLOCK_GLOBAL_STATE_H ++ ++#include "block/block-common.h" ++#include "qemu/coroutine.h" ++#include "qemu/transactions.h" ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * If a function modifies the graph, it also uses the graph lock to be sure it ++ * has unique access. The graph lock is needed together with BQL because of the ++ * thread-safe I/O API that concurrently runs and accesses the graph without ++ * the BQL. ++ * ++ * It is important to note that not all of these functions are ++ * necessarily limited to running under the BQL, but they would ++ * require additional auditing and many small thread-safety changes ++ * to move them into the I/O API. Often it's not worth doing that ++ * work since the APIs are only used with the BQL held at the ++ * moment, so they have been placed in the GS API (for now). ++ * ++ * These functions can call any function from this and other categories ++ * (I/O, "I/O or GS", Common), but must be invoked only by other GS APIs. ++ * ++ * All functions in this header must use the macro ++ * GLOBAL_STATE_CODE(); ++ * to catch when they are accidentally called without the BQL. ++ */ ++ ++void bdrv_init(void); ++BlockDriver *bdrv_find_protocol(const char *filename, ++ bool allow_protocol_prefix, ++ Error **errp); ++BlockDriver *bdrv_find_format(const char *format_name); ++ ++int coroutine_fn GRAPH_UNLOCKED ++bdrv_co_create(BlockDriver *drv, const char *filename, QemuOpts *opts, ++ Error **errp); ++ ++int co_wrapper bdrv_create(BlockDriver *drv, const char *filename, ++ QemuOpts *opts, Error **errp); ++ ++int coroutine_fn GRAPH_UNLOCKED ++bdrv_co_create_file(const char *filename, QemuOpts *opts, Error **errp); ++ ++BlockDriverState *bdrv_new(void); ++int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, ++ Error **errp); ++ ++int GRAPH_WRLOCK ++bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp); ++ ++int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, ++ Error **errp); ++BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, ++ int flags, Error **errp); ++int bdrv_drop_filter(BlockDriverState *bs, Error **errp); ++ ++BdrvChild * no_coroutine_fn ++bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, ++ BlockDriverState *parent, const BdrvChildClass *child_class, ++ BdrvChildRole child_role, bool allow_none, Error **errp); ++ ++BdrvChild * coroutine_fn no_co_wrapper ++bdrv_co_open_child(const char *filename, QDict *options, const char *bdref_key, ++ BlockDriverState *parent, 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 * no_coroutine_fn ++bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); ++ ++BlockDriverState * coroutine_fn no_co_wrapper ++bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp); ++ ++int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, ++ Error **errp); ++int GRAPH_WRLOCK ++bdrv_set_backing_hd_drained(BlockDriverState *bs, BlockDriverState *backing_hd, ++ Error **errp); ++ ++int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, ++ const char *bdref_key, Error **errp); ++ ++BlockDriverState * no_coroutine_fn ++bdrv_open(const char *filename, const char *reference, QDict *options, ++ int flags, Error **errp); ++ ++BlockDriverState * coroutine_fn no_co_wrapper ++bdrv_co_open(const char *filename, const char *reference, ++ QDict *options, int flags, Error **errp); ++ ++BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, ++ const char *node_name, ++ QDict *options, int flags, ++ Error **errp); ++BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, ++ int flags, Error **errp); ++BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, ++ BlockDriverState *bs, QDict *options, ++ bool keep_old_opts); ++void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue); ++int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); ++int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, ++ Error **errp); ++int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, ++ Error **errp); ++BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, ++ const char *backing_file); ++void GRAPH_RDLOCK bdrv_refresh_filename(BlockDriverState *bs); ++ ++void GRAPH_RDLOCK ++bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp); ++ ++int bdrv_commit(BlockDriverState *bs); ++int GRAPH_RDLOCK bdrv_make_empty(BdrvChild *c, Error **errp); ++ ++void bdrv_register(BlockDriver *bdrv); ++int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, ++ const char *backing_file_str, ++ bool backing_mask_protocol); ++ ++BlockDriverState * GRAPH_RDLOCK ++bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs); ++ ++BlockDriverState * GRAPH_RDLOCK bdrv_find_base(BlockDriverState *bs); ++ ++int GRAPH_RDLOCK ++bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, ++ Error **errp); ++void GRAPH_RDLOCK ++bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); ++ ++/* ++ * The units of offset and total_work_size may be chosen arbitrarily by the ++ * block driver; total_work_size may change during the course of the amendment ++ * operation ++ */ ++typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, ++ int64_t total_work_size, void *opaque); ++int GRAPH_RDLOCK ++bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ bool force, Error **errp); ++ ++/* check if a named node can be replaced when doing drive-mirror */ ++BlockDriverState * GRAPH_RDLOCK ++check_to_replace_node(BlockDriverState *parent_bs, const char *node_name, ++ Error **errp); ++ ++int no_coroutine_fn GRAPH_RDLOCK ++bdrv_activate(BlockDriverState *bs, Error **errp); ++ ++int coroutine_fn no_co_wrapper_bdrv_rdlock ++bdrv_co_activate(BlockDriverState *bs, Error **errp); ++ ++void bdrv_activate_all(Error **errp); ++int bdrv_inactivate_all(void); ++ ++int bdrv_flush_all(void); ++void bdrv_close_all(void); ++void bdrv_drain_all_begin(void); ++void bdrv_drain_all_begin_nopoll(void); ++void bdrv_drain_all_end(void); ++void bdrv_drain_all(void); ++ ++void bdrv_aio_cancel(BlockAIOCB *acb); ++ ++int bdrv_has_zero_init_1(BlockDriverState *bs); ++int coroutine_mixed_fn GRAPH_RDLOCK bdrv_has_zero_init(BlockDriverState *bs); ++BlockDriverState *bdrv_find_node(const char *node_name); ++BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); ++XDbgBlockGraph * GRAPH_RDLOCK bdrv_get_xdbg_block_graph(Error **errp); ++BlockDriverState *bdrv_lookup_bs(const char *device, ++ const char *node_name, ++ Error **errp); ++bool GRAPH_RDLOCK ++bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); ++ ++BlockDriverState *bdrv_next_node(BlockDriverState *bs); ++BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); ++ ++typedef struct BdrvNextIterator { ++ enum { ++ BDRV_NEXT_BACKEND_ROOTS, ++ BDRV_NEXT_MONITOR_OWNED, ++ } phase; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++} BdrvNextIterator; ++ ++BlockDriverState * GRAPH_RDLOCK bdrv_first(BdrvNextIterator *it); ++BlockDriverState * GRAPH_RDLOCK bdrv_next(BdrvNextIterator *it); ++void bdrv_next_cleanup(BdrvNextIterator *it); ++ ++BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); ++void bdrv_iterate_format(void (*it)(void *opaque, const char *name), ++ void *opaque, bool read_only); ++ ++char * GRAPH_RDLOCK ++bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp); ++ ++char * GRAPH_RDLOCK bdrv_dirname(BlockDriverState *bs, Error **errp); ++ ++void bdrv_img_create(const char *filename, const char *fmt, ++ const char *base_filename, const char *base_fmt, ++ char *options, uint64_t img_size, int flags, ++ bool quiet, Error **errp); ++ ++void bdrv_ref(BlockDriverState *bs); ++void no_coroutine_fn bdrv_unref(BlockDriverState *bs); ++void coroutine_fn no_co_wrapper bdrv_co_unref(BlockDriverState *bs); ++void GRAPH_WRLOCK bdrv_schedule_unref(BlockDriverState *bs); ++ ++void GRAPH_WRLOCK ++bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); ++ ++void coroutine_fn no_co_wrapper_bdrv_wrlock ++bdrv_co_unref_child(BlockDriverState *parent, BdrvChild *child); ++ ++BdrvChild * GRAPH_WRLOCK ++bdrv_attach_child(BlockDriverState *parent_bs, ++ BlockDriverState *child_bs, ++ const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ Error **errp); ++ ++bool GRAPH_RDLOCK ++bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); ++ ++void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason); ++void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason); ++void bdrv_op_block_all(BlockDriverState *bs, Error *reason); ++void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason); ++bool bdrv_op_blocker_is_empty(BlockDriverState *bs); ++ ++int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, ++ const char *tag); ++int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag); ++int bdrv_debug_resume(BlockDriverState *bs, const char *tag); ++bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag); ++ ++bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp); ++int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, ++ BdrvChild *ignore_child, Error **errp); ++ ++int GRAPH_RDLOCK bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); ++int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); ++ ++void GRAPH_WRLOCK ++bdrv_add_child(BlockDriverState *parent, BlockDriverState *child, Error **errp); ++ ++void GRAPH_WRLOCK ++bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp); ++ ++/** ++ * ++ * bdrv_register_buf/bdrv_unregister_buf: ++ * ++ * Register/unregister a buffer for I/O. For example, VFIO drivers are ++ * interested to know the memory areas that would later be used for I/O, so ++ * that they can prepare IOMMU mapping etc., to get better performance. ++ * ++ * Buffers must not overlap and they must be unregistered with the same values that they were registered with. ++ * ++ * Returns: true on success, false on failure ++ */ ++bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size, ++ Error **errp); ++void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size); ++ ++void bdrv_cancel_in_flight(BlockDriverState *bs); ++ ++#endif /* BLOCK_GLOBAL_STATE_H */ +diff --git a/include/block/block-io.h b/include/block/block-io.h +new file mode 100644 +index 00000000..b49e0537 +--- /dev/null ++++ b/include/block/block-io.h +@@ -0,0 +1,455 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_IO_H ++#define BLOCK_IO_H ++ ++#include "block/aio-wait.h" ++#include "block/block-common.h" ++#include "qemu/coroutine.h" ++#include "qemu/iov.h" ++ ++/* ++ * I/O API functions. These functions are thread-safe, and therefore ++ * can run in any thread. ++ * ++ * These functions can only call functions from I/O and Common categories, ++ * but can be invoked by GS, "I/O or GS" and I/O APIs. ++ * ++ * All functions in this category must use the macro ++ * IO_CODE(); ++ * to catch when they are accidentally called by the wrong API. ++ */ ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags); ++ ++int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pread(BdrvChild *child, int64_t offset, int64_t bytes, void *buf, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pwrite(BdrvChild *child, int64_t offset,int64_t bytes, ++ const void *buf, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes, ++ const void *buf, BdrvRequestFlags flags); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes, ++ const void *buf, BdrvRequestFlags flags); ++ ++/* ++ * Efficiently zero a region of the disk image. Note that this is a regular ++ * I/O request like read or write and should have a reasonable size. This ++ * function is not suitable for zeroing the entire image in a single request ++ * because it may allocate memory for the entire region. ++ */ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); ++ ++int64_t coroutine_fn GRAPH_RDLOCK bdrv_co_nb_sectors(BlockDriverState *bs); ++int64_t coroutine_mixed_fn bdrv_nb_sectors(BlockDriverState *bs); ++ ++int64_t coroutine_fn GRAPH_RDLOCK bdrv_co_getlength(BlockDriverState *bs); ++int64_t co_wrapper_mixed_bdrv_rdlock bdrv_getlength(BlockDriverState *bs); ++ ++int64_t coroutine_fn GRAPH_RDLOCK ++bdrv_co_get_allocated_file_size(BlockDriverState *bs); ++ ++int64_t co_wrapper_bdrv_rdlock ++bdrv_get_allocated_file_size(BlockDriverState *bs); ++ ++BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, ++ BlockDriverState *in_bs, Error **errp); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_delete_file(BlockDriverState *bs, Error **errp); ++ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_co_delete_file_noerr(BlockDriverState *bs); ++ ++ ++/* async block I/O */ ++void bdrv_aio_cancel_async(BlockAIOCB *acb); ++ ++/* sg packet commands */ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf); ++ ++/* Ensure contents are flushed to disk. */ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_flush(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard(BdrvChild *child, int64_t offset, ++ int64_t bytes); ++ ++/* Report zone information of zone block device. */ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_report(BlockDriverState *bs, ++ int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_mgmt(BlockDriverState *bs, ++ BlockZoneOp op, ++ int64_t offset, int64_t len); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_zone_append(BlockDriverState *bs, ++ int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file); ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_block_status_above(BlockDriverState *bs, BlockDriverState *base, ++ int64_t offset, int64_t bytes, int64_t *pnum, ++ int64_t *map, BlockDriverState **file); ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, ++ int64_t offset, int64_t bytes, int64_t *pnum, ++ int64_t *map, BlockDriverState **file); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ int64_t *pnum); ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_is_allocated(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, int64_t *pnum); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_is_allocated_above(BlockDriverState *top, BlockDriverState *base, ++ bool include_base, int64_t offset, int64_t bytes, ++ int64_t *pnum); ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_is_allocated_above(BlockDriverState *bs, BlockDriverState *base, ++ bool include_base, int64_t offset, ++ int64_t bytes, int64_t *pnum); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++int GRAPH_RDLOCK ++bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, ++ Error **errp); ++ ++bool bdrv_is_read_only(BlockDriverState *bs); ++bool bdrv_is_writable(BlockDriverState *bs); ++bool bdrv_is_sg(BlockDriverState *bs); ++int bdrv_get_flags(BlockDriverState *bs); ++ ++bool coroutine_fn GRAPH_RDLOCK bdrv_co_is_inserted(BlockDriverState *bs); ++bool co_wrapper_bdrv_rdlock bdrv_is_inserted(BlockDriverState *bs); ++ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_co_lock_medium(BlockDriverState *bs, bool locked); ++ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_co_eject(BlockDriverState *bs, bool eject_flag); ++ ++const char *bdrv_get_format_name(BlockDriverState *bs); ++ ++bool GRAPH_RDLOCK bdrv_supports_compressed_writes(BlockDriverState *bs); ++const char *bdrv_get_node_name(const BlockDriverState *bs); ++ ++const char * GRAPH_RDLOCK ++bdrv_get_device_name(const BlockDriverState *bs); ++ ++const char * GRAPH_RDLOCK ++bdrv_get_device_or_node_name(const BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); ++ ++ImageInfoSpecific * GRAPH_RDLOCK ++bdrv_get_specific_info(BlockDriverState *bs, Error **errp); ++ ++BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs); ++void bdrv_round_to_subclusters(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, ++ int64_t *cluster_offset, ++ int64_t *cluster_bytes); ++ ++void bdrv_get_backing_filename(BlockDriverState *bs, ++ char *filename, int filename_size); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt, bool warn); ++ ++int co_wrapper_bdrv_rdlock ++bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt, bool warn); ++ ++int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, ++ int64_t pos, int size); ++ ++int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, ++ int64_t pos, int size); ++ ++/* ++ * Returns the alignment in bytes that is required so that no bounce buffer ++ * is required throughout the stack ++ */ ++size_t bdrv_min_mem_align(BlockDriverState *bs); ++/* Returns optimal alignment in bytes for bounce buffer */ ++size_t bdrv_opt_mem_align(BlockDriverState *bs); ++void *qemu_blockalign(BlockDriverState *bs, size_t size); ++void *qemu_blockalign0(BlockDriverState *bs, size_t size); ++void *qemu_try_blockalign(BlockDriverState *bs, size_t size); ++void *qemu_try_blockalign0(BlockDriverState *bs, size_t size); ++ ++void bdrv_enable_copy_on_read(BlockDriverState *bs); ++void bdrv_disable_copy_on_read(BlockDriverState *bs); ++ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent event); ++ ++void co_wrapper_mixed_bdrv_rdlock ++bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event); ++ ++#define BLKDBG_CO_EVENT(child, evt) \ ++ do { \ ++ if (child) { \ ++ bdrv_co_debug_event(child->bs, evt); \ ++ } \ ++ } while (0) ++ ++#define BLKDBG_EVENT(child, evt) \ ++ do { \ ++ if (child) { \ ++ bdrv_debug_event(child->bs, evt); \ ++ } \ ++ } while (0) ++ ++/** ++ * bdrv_get_aio_context: ++ * ++ * Returns: the currently bound #AioContext ++ */ ++AioContext *bdrv_get_aio_context(BlockDriverState *bs); ++ ++AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c); ++ ++/** ++ * Move the current coroutine to the AioContext of @bs and return the old ++ * AioContext of the coroutine. Increase bs->in_flight so that draining @bs ++ * will wait for the operation to proceed until the corresponding ++ * bdrv_co_leave(). ++ * ++ * Consequently, you can't call drain inside a bdrv_co_enter/leave() section as ++ * this will deadlock. ++ */ ++AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs); ++ ++/** ++ * Ends a section started by bdrv_co_enter(). Move the current coroutine back ++ * to old_ctx and decrease bs->in_flight again. ++ */ ++void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx); ++ ++AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c); ++ ++bool coroutine_fn GRAPH_RDLOCK ++bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp); ++bool co_wrapper_bdrv_rdlock ++bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp); ++ ++/** ++ * ++ * bdrv_co_copy_range: ++ * ++ * Do offloaded copy between two children. If the operation is not implemented ++ * by the driver, or if the backend storage doesn't support it, a negative ++ * error code will be returned. ++ * ++ * Note: block layer doesn't emulate or fallback to a bounce buffer approach ++ * because usually the caller shouldn't attempt offloaded copy any more (e.g. ++ * calling copy_file_range(2)) after the first error, thus it should fall back ++ * to a read+write path in the caller level. ++ * ++ * @src: Source child to copy data from ++ * @src_offset: offset in @src image to read data ++ * @dst: Destination child to copy data to ++ * @dst_offset: offset in @dst image to write data ++ * @bytes: number of bytes to copy ++ * @flags: request flags. Supported flags: ++ * BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero ++ * write on @dst as if bdrv_co_pwrite_zeroes is ++ * called. Used to simplify caller code, or ++ * during BlockDriver.bdrv_co_copy_range_from() ++ * recursion. ++ * BDRV_REQ_NO_SERIALISING - do not serialize with other overlapping ++ * requests currently in flight. ++ * ++ * Returns: 0 if succeeded; negative error code if failed. ++ **/ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); ++ ++/* ++ * "I/O or GS" API functions. These functions can run without ++ * the BQL, but only in one specific iothread/main loop. ++ * ++ * More specifically, these functions use BDRV_POLL_WHILE(bs), which requires ++ * the caller to be either in the main thread or directly in the home thread ++ * that runs the bs AioContext. Calling them from another thread in another ++ * AioContext would cause deadlocks. ++ * ++ * Therefore, these functions are not proper I/O, because they ++ * can't run in *any* iothreads, but only in a specific one. ++ * ++ * These functions can call any function from I/O, Common and this ++ * categories, but must be invoked only by other "I/O or GS" and GS APIs. ++ * ++ * All functions in this category must use the macro ++ * IO_OR_GS_CODE(); ++ * to catch when they are accidentally called by the wrong API. ++ */ ++ ++#define BDRV_POLL_WHILE(bs, cond) ({ \ ++ BlockDriverState *bs_ = (bs); \ ++ IO_OR_GS_CODE(); \ ++ AIO_WAIT_WHILE(bdrv_get_aio_context(bs_), \ ++ cond); }) ++ ++void bdrv_drain(BlockDriverState *bs); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); ++ ++/* Invalidate any cached metadata used by image formats */ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); ++ ++int co_wrapper_mixed_bdrv_rdlock bdrv_flush(BlockDriverState *bs); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++/** ++ * bdrv_parent_drained_begin_single: ++ * ++ * Begin a quiesced section for the parent of @c. ++ */ ++void GRAPH_RDLOCK bdrv_parent_drained_begin_single(BdrvChild *c); ++ ++/** ++ * bdrv_parent_drained_poll_single: ++ * ++ * Returns true if there is any pending activity to cease before @c can be ++ * called quiesced, false otherwise. ++ */ ++bool GRAPH_RDLOCK bdrv_parent_drained_poll_single(BdrvChild *c); ++ ++/** ++ * bdrv_parent_drained_end_single: ++ * ++ * End a quiesced section for the parent of @c. ++ */ ++void GRAPH_RDLOCK bdrv_parent_drained_end_single(BdrvChild *c); ++ ++/** ++ * bdrv_drain_poll: ++ * ++ * Poll for pending requests in @bs and its parents (except for @ignore_parent). ++ * ++ * If @ignore_bds_parents is true, parents that are BlockDriverStates must ++ * ignore the drain request because they will be drained separately (used for ++ * drain_all). ++ * ++ * This is part of bdrv_drained_begin. ++ */ ++bool GRAPH_RDLOCK ++bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, ++ bool ignore_bds_parents); ++ ++/** ++ * bdrv_drained_begin: ++ * ++ * Begin a quiesced section for exclusive access to the BDS, by disabling ++ * external request sources including NBD server, block jobs, and device model. ++ * ++ * This function can only be invoked by the main loop or a coroutine ++ * (regardless of the AioContext where it is running). ++ * If the coroutine is running in an Iothread AioContext, this function will ++ * just schedule a BH to run in the main loop. ++ * However, it cannot be directly called by an Iothread. ++ * ++ * This function can be recursive. ++ */ ++void bdrv_drained_begin(BlockDriverState *bs); ++ ++/** ++ * bdrv_do_drained_begin_quiesce: ++ * ++ * Quiesces a BDS like bdrv_drained_begin(), but does not wait for already ++ * running requests to complete. ++ */ ++void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent); ++ ++/** ++ * bdrv_drained_end: ++ * ++ * End a quiescent section started by bdrv_drained_begin(). ++ * ++ * This function can only be invoked by the main loop or a coroutine ++ * (regardless of the AioContext where it is running). ++ * If the coroutine is running in an Iothread AioContext, this function will ++ * just schedule a BH to run in the main loop. ++ * However, it cannot be directly called by an Iothread. ++ */ ++void bdrv_drained_end(BlockDriverState *bs); ++ ++#endif /* BLOCK_IO_H */ +diff --git a/include/block/block.h b/include/block/block.h +new file mode 100644 +index 00000000..e2c647de +--- /dev/null ++++ b/include/block/block.h +@@ -0,0 +1,32 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_H ++#define BLOCK_H ++ ++#include "block/block-global-state.h" ++#include "block/block-io.h" ++ ++/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ ++ ++#endif /* BLOCK_H */ +diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h +new file mode 100644 +index 00000000..ebb4e56a +--- /dev/null ++++ b/include/block/block_int-common.h +@@ -0,0 +1,1318 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_INT_COMMON_H ++#define BLOCK_INT_COMMON_H ++ ++#include "block/aio.h" ++#include "block/block-common.h" ++#include "block/block-global-state.h" ++#include "block/snapshot.h" ++#include "qemu/iov.h" ++#include "qemu/rcu.h" ++#include "qemu/stats64.h" ++ ++#define BLOCK_FLAG_LAZY_REFCOUNTS 8 ++ ++#define BLOCK_OPT_SIZE "size" ++#define BLOCK_OPT_ENCRYPT "encryption" ++#define BLOCK_OPT_ENCRYPT_FORMAT "encrypt.format" ++#define BLOCK_OPT_COMPAT6 "compat6" ++#define BLOCK_OPT_HWVERSION "hwversion" ++#define BLOCK_OPT_BACKING_FILE "backing_file" ++#define BLOCK_OPT_BACKING_FMT "backing_fmt" ++#define BLOCK_OPT_CLUSTER_SIZE "cluster_size" ++#define BLOCK_OPT_TABLE_SIZE "table_size" ++#define BLOCK_OPT_PREALLOC "preallocation" ++#define BLOCK_OPT_SUBFMT "subformat" ++#define BLOCK_OPT_COMPAT_LEVEL "compat" ++#define BLOCK_OPT_LAZY_REFCOUNTS "lazy_refcounts" ++#define BLOCK_OPT_ADAPTER_TYPE "adapter_type" ++#define BLOCK_OPT_REDUNDANCY "redundancy" ++#define BLOCK_OPT_NOCOW "nocow" ++#define BLOCK_OPT_EXTENT_SIZE_HINT "extent_size_hint" ++#define BLOCK_OPT_OBJECT_SIZE "object_size" ++#define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" ++#define BLOCK_OPT_DATA_FILE "data_file" ++#define BLOCK_OPT_DATA_FILE_RAW "data_file_raw" ++#define BLOCK_OPT_COMPRESSION_TYPE "compression_type" ++#define BLOCK_OPT_EXTL2 "extended_l2" ++ ++#define BLOCK_PROBE_BUF_SIZE 512 ++ ++enum BdrvTrackedRequestType { ++ BDRV_TRACKED_READ, ++ BDRV_TRACKED_WRITE, ++ BDRV_TRACKED_DISCARD, ++ BDRV_TRACKED_TRUNCATE, ++}; ++ ++/* ++ * That is not quite good that BdrvTrackedRequest structure is public, ++ * as block/io.c is very careful about incoming offset/bytes being ++ * correct. Be sure to assert bdrv_check_request() succeeded after any ++ * modification of BdrvTrackedRequest object out of block/io.c ++ */ ++typedef struct BdrvTrackedRequest { ++ BlockDriverState *bs; ++ int64_t offset; ++ int64_t bytes; ++ enum BdrvTrackedRequestType type; ++ ++ bool serialising; ++ int64_t overlap_offset; ++ int64_t overlap_bytes; ++ ++ QLIST_ENTRY(BdrvTrackedRequest) list; ++ Coroutine *co; /* owner, used for deadlock detection */ ++ CoQueue wait_queue; /* coroutines blocked on this request */ ++ ++ struct BdrvTrackedRequest *waiting_for; ++} BdrvTrackedRequest; ++ ++ ++struct BlockDriver { ++ /* ++ * These fields are initialized when this object is created, ++ * and are never changed afterwards. ++ */ ++ ++ const char *format_name; ++ int instance_size; ++ ++ /* ++ * Set to true if the BlockDriver is a block filter. Block filters pass ++ * certain callbacks that refer to data (see block.c) to their bs->file ++ * or bs->backing (whichever one exists) if the driver doesn't implement ++ * them. Drivers that do not wish to forward must implement them and return ++ * -ENOTSUP. ++ * Note that filters are not allowed to modify data. ++ * ++ * Filters generally cannot have more than a single filtered child, ++ * because the data they present must at all times be the same as ++ * that on their filtered child. That would be impossible to ++ * achieve for multiple filtered children. ++ * (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. ++ * Two internal filters use bs->backing as filtered child and has this ++ * field set to true: mirror_top and commit_top. There also two such test ++ * filters in tests/unit/test-bdrv-graph-mod.c. ++ * ++ * Never create any more such filters! ++ * ++ * TODO: imagine how to deprecate this behavior and make all filters work ++ * similarly using bs->file as filtered child. ++ */ ++ bool filtered_child_is_backing; ++ ++ /* ++ * Set to true if the BlockDriver is a format driver. Format nodes ++ * generally do not expect their children to be other format nodes ++ * (except for backing files), and so format probing is disabled ++ * on those children. ++ */ ++ bool is_format; ++ ++ /* ++ * Set to true if the BlockDriver supports zoned children. ++ */ ++ bool supports_zoned_children; ++ ++ /* ++ * Drivers not implementing bdrv_parse_filename nor bdrv_open should have ++ * this field set to true, except ones that are defined only by their ++ * child's bs. ++ * An example of the last type will be the quorum block driver. ++ */ ++ bool bdrv_needs_filename; ++ ++ /* ++ * Set if a driver can support backing files. This also implies the ++ * following semantics: ++ * ++ * - Return status 0 of .bdrv_co_block_status means that corresponding ++ * blocks are not allocated in this layer of backing-chain ++ * - For such (unallocated) blocks, read will: ++ * - fill buffer with zeros if there is no backing file ++ * - read from the backing file otherwise, where the block layer ++ * takes care of reading zeros beyond EOF if backing file is short ++ */ ++ bool supports_backing; ++ ++ /* ++ * Drivers setting this field must be able to work with just a plain ++ * filename with ':' as a prefix, and no other options. ++ * Options may be extracted from the filename by implementing ++ * bdrv_parse_filename. ++ */ ++ const char *protocol_name; ++ ++ /* List of options for creating images, terminated by name == NULL */ ++ QemuOptsList *create_opts; ++ ++ /* List of options for image amend */ ++ QemuOptsList *amend_opts; ++ ++ /* ++ * If this driver supports reopening images this contains a ++ * NULL-terminated list of the runtime options that can be ++ * modified. If an option in this list is unspecified during ++ * reopen then it _must_ be reset to its default value or return ++ * an error. ++ */ ++ const char *const *mutable_opts; ++ ++ /* ++ * Pointer to a NULL-terminated array of names of strong options ++ * that can be specified for bdrv_open(). A strong option is one ++ * that changes the data of a BDS. ++ * If this pointer is NULL, the array is considered empty. ++ * "filename" and "driver" are always considered strong. ++ */ ++ const char *const *strong_runtime_opts; ++ ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++ /* ++ * This function is invoked under BQL before .bdrv_co_amend() ++ * (which in contrast does not necessarily run under the BQL) ++ * to allow driver-specific initialization code that requires ++ * the BQL, like setting up specific permission flags. ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_amend_pre_run)( ++ BlockDriverState *bs, Error **errp); ++ /* ++ * This function is invoked under BQL after .bdrv_co_amend() ++ * to allow cleaning up what was done in .bdrv_amend_pre_run(). ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_amend_clean)(BlockDriverState *bs); ++ ++ /* ++ * Return true if @to_replace can be replaced by a BDS with the ++ * same data as @bs without it affecting @bs's behavior (that is, ++ * without it being visible to @bs's parents). ++ */ ++ bool GRAPH_RDLOCK_PTR (*bdrv_recurse_can_replace)( ++ BlockDriverState *bs, BlockDriverState *to_replace); ++ ++ int (*bdrv_probe_device)(const char *filename); ++ ++ /* ++ * Any driver implementing this callback is expected to be able to handle ++ * NULL file names in its .bdrv_open() implementation. ++ */ ++ void (*bdrv_parse_filename)(const char *filename, QDict *options, ++ Error **errp); ++ ++ /* For handling image reopen for split or non-split files. */ ++ int GRAPH_UNLOCKED_PTR (*bdrv_reopen_prepare)( ++ BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); ++ void GRAPH_UNLOCKED_PTR (*bdrv_reopen_commit)( ++ BDRVReopenState *reopen_state); ++ void GRAPH_UNLOCKED_PTR (*bdrv_reopen_commit_post)( ++ BDRVReopenState *reopen_state); ++ void GRAPH_UNLOCKED_PTR (*bdrv_reopen_abort)( ++ BDRVReopenState *reopen_state); ++ void (*bdrv_join_options)(QDict *options, QDict *old_options); ++ ++ int GRAPH_UNLOCKED_PTR (*bdrv_open)( ++ BlockDriverState *bs, QDict *options, int flags, Error **errp); ++ ++ void (*bdrv_close)(BlockDriverState *bs); ++ ++ int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create)( ++ BlockdevCreateOptions *opts, Error **errp); ++ ++ int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create_opts)( ++ BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_amend_options)( ++ BlockDriverState *bs, QemuOpts *opts, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ bool force, Error **errp); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_make_empty)(BlockDriverState *bs); ++ ++ /* ++ * Refreshes the bs->exact_filename field. If that is impossible, ++ * bs->exact_filename has to be left empty. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_refresh_filename)(BlockDriverState *bs); ++ ++ /* ++ * Gathers the open options for all children into @target. ++ * A simple format driver (without backing file support) might ++ * implement this function like this: ++ * ++ * QINCREF(bs->file->bs->full_open_options); ++ * qdict_put(target, "file", bs->file->bs->full_open_options); ++ * ++ * If not specified, the generic implementation will simply put ++ * all children's options under their respective name. ++ * ++ * @backing_overridden is true when bs->backing seems not to be ++ * the child that would result from opening bs->backing_file. ++ * Therefore, if it is true, the backing child's options should be ++ * gathered; otherwise, there is no need since the backing child ++ * is the one implied by the image header. ++ * ++ * Note that ideally this function would not be needed. Every ++ * block driver which implements it is probably doing something ++ * shady regarding its runtime option structure. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_gather_child_options)( ++ BlockDriverState *bs, QDict *target, bool backing_overridden); ++ ++ /* ++ * Returns an allocated string which is the directory name of this BDS: It ++ * will be used to make relative filenames absolute by prepending this ++ * function's return value to them. ++ */ ++ char * GRAPH_RDLOCK_PTR (*bdrv_dirname)(BlockDriverState *bs, Error **errp); ++ ++ /* ++ * This informs the driver that we are no longer interested in the result ++ * of in-flight requests, so don't waste the time if possible. ++ * ++ * One example usage is to avoid waiting for an nbd target node reconnect ++ * timeout during job-cancel with force=true. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_cancel_in_flight)(BlockDriverState *bs); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_inactivate)(BlockDriverState *bs); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_snapshot_create)( ++ BlockDriverState *bs, QEMUSnapshotInfo *sn_info); ++ ++ int GRAPH_UNLOCKED_PTR (*bdrv_snapshot_goto)( ++ BlockDriverState *bs, const char *snapshot_id); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_snapshot_delete)( ++ BlockDriverState *bs, const char *snapshot_id, const char *name, ++ Error **errp); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_snapshot_list)( ++ BlockDriverState *bs, QEMUSnapshotInfo **psn_info); ++ ++ int GRAPH_RDLOCK_PTR (*bdrv_snapshot_load_tmp)( ++ BlockDriverState *bs, const char *snapshot_id, const char *name, ++ Error **errp); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_change_backing_file)( ++ BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt); ++ ++ /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ ++ int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event, ++ const char *tag); ++ int (*bdrv_debug_remove_breakpoint)(BlockDriverState *bs, ++ const char *tag); ++ int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag); ++ bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag); ++ ++ void GRAPH_RDLOCK_PTR (*bdrv_refresh_limits)( ++ BlockDriverState *bs, Error **errp); ++ ++ /* ++ * Returns 1 if newly created images are guaranteed to contain only ++ * zeros, 0 otherwise. ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_has_zero_init)(BlockDriverState *bs); ++ ++ /* ++ * Remove fd handlers, timers, and other event loop callbacks so the event ++ * loop is no longer in use. Called with no in-flight requests and in ++ * depth-first traversal order with parents before child nodes. ++ */ ++ void (*bdrv_detach_aio_context)(BlockDriverState *bs); ++ ++ /* ++ * Add fd handlers, timers, and other event loop callbacks so I/O requests ++ * can be processed again. Called with no in-flight requests and in ++ * depth-first traversal order with child nodes before parent nodes. ++ */ ++ void (*bdrv_attach_aio_context)(BlockDriverState *bs, ++ AioContext *new_context); ++ ++ /** ++ * bdrv_drain_begin is called if implemented in the beginning of a ++ * drain operation to drain and stop any internal sources of requests in ++ * the driver. ++ * bdrv_drain_end is called if implemented at the end of the drain. ++ * ++ * They should be used by the driver to e.g. manage scheduled I/O ++ * requests, or toggle an internal state. After the end of the drain new ++ * requests will continue normally. ++ * ++ * Implementations of both functions must not call aio_poll(). ++ */ ++ void (*bdrv_drain_begin)(BlockDriverState *bs); ++ void (*bdrv_drain_end)(BlockDriverState *bs); ++ ++ /** ++ * Try to get @bs's logical and physical block size. ++ * On success, store them in @bsz and return zero. ++ * On failure, return negative errno. ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_probe_blocksizes)( ++ BlockDriverState *bs, BlockSizes *bsz); ++ /** ++ * Try to get @bs's geometry (cyls, heads, sectors) ++ * On success, store them in @geo and return 0. ++ * On failure return -errno. ++ * Only drivers that want to override guest geometry implement this ++ * callback; see hd_geometry_guess(). ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_probe_geometry)( ++ BlockDriverState *bs, HDGeometry *geo); ++ ++ void GRAPH_WRLOCK_PTR (*bdrv_add_child)( ++ BlockDriverState *parent, BlockDriverState *child, Error **errp); ++ ++ void GRAPH_WRLOCK_PTR (*bdrv_del_child)( ++ BlockDriverState *parent, BdrvChild *child, Error **errp); ++ ++ /** ++ * Informs the block driver that a permission change is intended. The ++ * driver checks whether the change is permissible and may take other ++ * preparations for the change (e.g. get file system locks). This operation ++ * is always followed either by a call to either .bdrv_set_perm or ++ * .bdrv_abort_perm_update. ++ * ++ * Checks whether the requested set of cumulative permissions in @perm ++ * can be granted for accessing @bs and whether no other users are using ++ * permissions other than those given in @shared (both arguments take ++ * BLK_PERM_* bitmasks). ++ * ++ * If both conditions are met, 0 is returned. Otherwise, -errno is returned ++ * and errp is set to an error describing the conflict. ++ */ ++ int GRAPH_RDLOCK_PTR (*bdrv_check_perm)(BlockDriverState *bs, uint64_t perm, ++ uint64_t shared, Error **errp); ++ ++ /** ++ * Called to inform the driver that the set of cumulative set of used ++ * permissions for @bs has changed to @perm, and the set of shareable ++ * permission to @shared. The driver can use this to propagate changes to ++ * its children (i.e. request permissions only if a parent actually needs ++ * them). ++ * ++ * This function is only invoked after bdrv_check_perm(), so block drivers ++ * may rely on preparations made in their .bdrv_check_perm implementation. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_set_perm)( ++ BlockDriverState *bs, uint64_t perm, uint64_t shared); ++ ++ /* ++ * Called to inform the driver that after a previous bdrv_check_perm() ++ * call, the permission update is not performed and any preparations made ++ * for it (e.g. taken file locks) need to be undone. ++ * ++ * This function can be called even for nodes that never saw a ++ * bdrv_check_perm() call. It is a no-op then. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_abort_perm_update)(BlockDriverState *bs); ++ ++ /** ++ * Returns in @nperm and @nshared the permissions that the driver for @bs ++ * needs on its child @c, based on the cumulative permissions requested by ++ * the parents in @parent_perm and @parent_shared. ++ * ++ * If @c is NULL, return the permissions for attaching a new child for the ++ * given @child_class and @role. ++ * ++ * If @reopen_queue is non-NULL, don't return the currently needed ++ * permissions, but those that will be needed after applying the ++ * @reopen_queue. ++ */ ++ void GRAPH_RDLOCK_PTR (*bdrv_child_perm)( ++ BlockDriverState *bs, BdrvChild *c, BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t parent_perm, uint64_t parent_shared, ++ uint64_t *nperm, uint64_t *nshared); ++ ++ /** ++ * Register/unregister a buffer for I/O. For example, when the driver is ++ * interested to know the memory areas that will later be used in iovs, so ++ * that it can do IOMMU mapping with VFIO etc., in order to get better ++ * performance. In the case of VFIO drivers, this callback is used to do ++ * DMA mapping for hot buffers. ++ * ++ * Returns: true on success, false on failure ++ */ ++ bool GRAPH_RDLOCK_PTR (*bdrv_register_buf)( ++ BlockDriverState *bs, void *host, size_t size, Error **errp); ++ void GRAPH_RDLOCK_PTR (*bdrv_unregister_buf)( ++ BlockDriverState *bs, void *host, size_t size); ++ ++ /* ++ * This field is modified only under the BQL, and is part of ++ * the global state. ++ */ ++ QLIST_ENTRY(BlockDriver) list; ++ ++ /* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++ int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_amend)( ++ BlockDriverState *bs, BlockdevAmendOptions *opts, bool force, ++ Error **errp); ++ ++ /* aio */ ++ BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_preadv)(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); ++ ++ BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_pwritev)(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags, BlockCompletionFunc *cb, void *opaque); ++ ++ BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_flush)( ++ BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); ++ ++ BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_pdiscard)( ++ BlockDriverState *bs, int64_t offset, int bytes, ++ BlockCompletionFunc *cb, void *opaque); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_readv)(BlockDriverState *bs, ++ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); ++ ++ /** ++ * @offset: position in bytes to read at ++ * @bytes: number of bytes to read ++ * @qiov: the buffers to fill with read data ++ * @flags: currently unused, always 0 ++ * ++ * @offset and @bytes will be a multiple of 'request_alignment', ++ * but the length of individual @qiov elements does not have to ++ * be a multiple. ++ * ++ * @bytes will always equal the total size of @qiov, and will be ++ * no larger than 'max_transfer'. ++ * ++ * The buffer in @qiov may point directly to guest memory. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv)(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv_part)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_writev)(BlockDriverState *bs, ++ int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, ++ int flags); ++ /** ++ * @offset: position in bytes to write at ++ * @bytes: number of bytes to write ++ * @qiov: the buffers containing data to write ++ * @flags: zero or more bits allowed by 'supported_write_flags' ++ * ++ * @offset and @bytes will be a multiple of 'request_alignment', ++ * but the length of individual @qiov elements does not have to ++ * be a multiple. ++ * ++ * @bytes will always equal the total size of @qiov, and will be ++ * no larger than 'max_transfer'. ++ * ++ * The buffer in @qiov may point directly to guest memory. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_part)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, BdrvRequestFlags flags); ++ ++ /* ++ * Efficiently zero a region of the disk image. Typically an image format ++ * would use a compact metadata representation to implement this. This ++ * function pointer may be NULL or return -ENOSUP and .bdrv_co_writev() ++ * will be called instead. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwrite_zeroes)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pdiscard)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++ /* ++ * Map [offset, offset + nbytes) range onto a child of @bs to copy from, ++ * and invoke bdrv_co_copy_range_from(child, ...), or invoke ++ * bdrv_co_copy_range_to() if @bs is the leaf child to copy data from. ++ * ++ * See the comment of bdrv_co_copy_range for the parameter and return value ++ * semantics. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_copy_range_from)( ++ BlockDriverState *bs, BdrvChild *src, int64_t offset, ++ BdrvChild *dst, int64_t dst_offset, int64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); ++ ++ /* ++ * Map [offset, offset + nbytes) range onto a child of bs to copy data to, ++ * and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy ++ * operation if @bs is the leaf and @src has the same BlockDriver. Return ++ * -ENOTSUP if @bs is the leaf but @src has a different BlockDriver. ++ * ++ * See the comment of bdrv_co_copy_range for the parameter and return value ++ * semantics. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_copy_range_to)( ++ BlockDriverState *bs, BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, int64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); ++ ++ /* ++ * Building block for bdrv_block_status[_above] and ++ * bdrv_is_allocated[_above]. The driver should answer only ++ * according to the current layer, and should only need to set ++ * BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID, ++ * and/or BDRV_BLOCK_RAW; if the current layer defers to a backing ++ * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). See ++ * block.h for the overall meaning of the bits. As a hint, the ++ * flag want_zero is true if the caller cares more about precise ++ * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for ++ * overall allocation (favor larger *pnum, perhaps by reporting ++ * _DATA instead of _ZERO). The block layer guarantees input ++ * clamped to bdrv_getlength() and aligned to request_alignment, ++ * as well as non-NULL pnum, map, and file; in turn, the driver ++ * must return an error or set pnum to an aligned non-zero value. ++ * ++ * Note that @bytes is just a hint on how big of a region the ++ * caller wants to inspect. It is not a limit on *pnum. ++ * Implementations are free to return larger values of *pnum if ++ * doing so does not incur a performance penalty. ++ * ++ * block/io.c's bdrv_co_block_status() will utilize an unclamped ++ * *pnum value for the block-status cache on protocol nodes, prior ++ * to clamping *pnum for return to its caller. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_block_status)( ++ BlockDriverState *bs, ++ bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, ++ int64_t *map, BlockDriverState **file); ++ ++ /* ++ * Snapshot-access API. ++ * ++ * Block-driver may provide snapshot-access API: special functions to access ++ * some internal "snapshot". The functions are similar with normal ++ * read/block_status/discard handler, but don't have any specific handling ++ * in generic block-layer: no serializing, no alignment, no tracked ++ * requests. So, block-driver that realizes these APIs is fully responsible ++ * for synchronization between snapshot-access API and normal IO requests. ++ * ++ * TODO: To be able to support qcow2's internal snapshots, this API will ++ * need to be extended to: ++ * - be able to select a specific snapshot ++ * - receive the snapshot's actual length (which may differ from bs's ++ * length) ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_preadv_snapshot)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_snapshot_block_status)( ++ BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pdiscard_snapshot)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++ /* ++ * Invalidate any cached meta-data. ++ */ ++ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_invalidate_cache)( ++ BlockDriverState *bs, Error **errp); ++ ++ /* ++ * Flushes all data for all layers by calling bdrv_co_flush for underlying ++ * layers, if needed. This function is needed for deterministic ++ * synchronization of the flush finishing callback. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush)(BlockDriverState *bs); ++ ++ /* Delete a created file. */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_delete_file)( ++ BlockDriverState *bs, Error **errp); ++ ++ /* ++ * Flushes all data that was already written to the OS all the way down to ++ * the disk (for example file-posix.c calls fsync()). ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush_to_disk)( ++ BlockDriverState *bs); ++ ++ /* ++ * Flushes all internal caches to the OS. The data may still sit in a ++ * writeback cache of the host OS, but it will survive a crash of the qemu ++ * process. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_flush_to_os)( ++ BlockDriverState *bs); ++ ++ /* ++ * Truncate @bs to @offset bytes using the given @prealloc mode ++ * when growing. Modes other than PREALLOC_MODE_OFF should be ++ * rejected when shrinking @bs. ++ * ++ * If @exact is true, @bs must be resized to exactly @offset. ++ * Otherwise, it is sufficient for @bs (if it is a host block ++ * device and thus there is no way to resize it) to be at least ++ * @offset bytes in length. ++ * ++ * If @exact is true and this function fails but would succeed ++ * with @exact = false, it should return -ENOTSUP. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_truncate)( ++ BlockDriverState *bs, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp); ++ ++ int64_t coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_getlength)( ++ BlockDriverState *bs); ++ ++ int64_t coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_get_allocated_file_size)( ++ BlockDriverState *bs); ++ ++ BlockMeasureInfo *(*bdrv_measure)(QemuOpts *opts, BlockDriverState *in_bs, ++ Error **errp); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_compressed)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pwritev_compressed_part)( ++ BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_get_info)( ++ BlockDriverState *bs, BlockDriverInfo *bdi); ++ ++ ImageInfoSpecific * GRAPH_RDLOCK_PTR (*bdrv_get_specific_info)( ++ BlockDriverState *bs, Error **errp); ++ BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_save_vmstate)( ++ BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_load_vmstate)( ++ BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++ int coroutine_fn (*bdrv_co_zone_report)(BlockDriverState *bs, ++ int64_t offset, unsigned int *nr_zones, ++ BlockZoneDescriptor *zones); ++ int coroutine_fn (*bdrv_co_zone_mgmt)(BlockDriverState *bs, BlockZoneOp op, ++ int64_t offset, int64_t len); ++ int coroutine_fn (*bdrv_co_zone_append)(BlockDriverState *bs, ++ int64_t *offset, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++ /* removable device specific */ ++ bool coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_is_inserted)( ++ BlockDriverState *bs); ++ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_eject)( ++ BlockDriverState *bs, bool eject_flag); ++ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_lock_medium)( ++ BlockDriverState *bs, bool locked); ++ ++ /* to control generic scsi devices */ ++ BlockAIOCB *coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_aio_ioctl)( ++ BlockDriverState *bs, unsigned long int req, void *buf, ++ BlockCompletionFunc *cb, void *opaque); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_ioctl)( ++ BlockDriverState *bs, unsigned long int req, void *buf); ++ ++ /* ++ * Returns 0 for completed check, -errno for internal errors. ++ * The check results are stored in result. ++ */ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_check)( ++ BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix); ++ ++ void coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_debug_event)( ++ BlockDriverState *bs, BlkdebugEvent event); ++ ++ bool (*bdrv_supports_persistent_dirty_bitmap)(BlockDriverState *bs); ++ ++ bool coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_can_store_new_dirty_bitmap)( ++ BlockDriverState *bs, const char *name, uint32_t granularity, ++ Error **errp); ++ ++ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_remove_persistent_dirty_bitmap)( ++ BlockDriverState *bs, const char *name, Error **errp); ++}; ++ ++static inline bool TSA_NO_TSA block_driver_can_compress(BlockDriver *drv) ++{ ++ return drv->bdrv_co_pwritev_compressed || ++ drv->bdrv_co_pwritev_compressed_part; ++} ++ ++typedef struct BlockLimits { ++ /* ++ * Alignment requirement, in bytes, for offset/length of I/O ++ * requests. Must be a power of 2 less than INT_MAX; defaults to ++ * 1 for drivers with modern byte interfaces, and to 512 ++ * otherwise. ++ */ ++ uint32_t request_alignment; ++ ++ /* ++ * Maximum number of bytes that can be discarded at once. Must be multiple ++ * of pdiscard_alignment, but need not be power of 2. May be 0 if no ++ * inherent 64-bit limit. ++ */ ++ int64_t max_pdiscard; ++ ++ /* ++ * Optimal alignment for discard requests in bytes. A power of 2 ++ * is best but not mandatory. Must be a multiple of ++ * bl.request_alignment, and must be less than max_pdiscard if ++ * that is set. May be 0 if bl.request_alignment is good enough ++ */ ++ uint32_t pdiscard_alignment; ++ ++ /* ++ * Maximum number of bytes that can zeroized at once. Must be multiple of ++ * pwrite_zeroes_alignment. 0 means no limit. ++ */ ++ int64_t max_pwrite_zeroes; ++ ++ /* ++ * Optimal alignment for write zeroes requests in bytes. A power ++ * of 2 is best but not mandatory. Must be a multiple of ++ * bl.request_alignment, and must be less than max_pwrite_zeroes ++ * if that is set. May be 0 if bl.request_alignment is good ++ * enough ++ */ ++ uint32_t pwrite_zeroes_alignment; ++ ++ /* ++ * Optimal transfer length in bytes. A power of 2 is best but not ++ * mandatory. Must be a multiple of bl.request_alignment, or 0 if ++ * no preferred size ++ */ ++ uint32_t opt_transfer; ++ ++ /* ++ * Maximal transfer length in bytes. Need not be power of 2, but ++ * must be multiple of opt_transfer and bl.request_alignment, or 0 ++ * for no 32-bit limit. For now, anything larger than INT_MAX is ++ * clamped down. ++ */ ++ uint32_t max_transfer; ++ ++ /* ++ * Maximal hardware transfer length in bytes. Applies whenever ++ * transfers to the device bypass the kernel I/O scheduler, for ++ * example with SG_IO. If larger than max_transfer or if zero, ++ * blk_get_max_hw_transfer will fall back to max_transfer. ++ */ ++ uint64_t max_hw_transfer; ++ ++ /* ++ * Maximal number of scatter/gather elements allowed by the hardware. ++ * Applies whenever transfers to the device bypass the kernel I/O ++ * scheduler, for example with SG_IO. If larger than max_iov ++ * or if zero, blk_get_max_hw_iov will fall back to max_iov. ++ */ ++ int max_hw_iov; ++ ++ ++ /* memory alignment, in bytes so that no bounce buffer is needed */ ++ size_t min_mem_alignment; ++ ++ /* memory alignment, in bytes, for bounce buffer */ ++ size_t opt_mem_alignment; ++ ++ /* maximum number of iovec elements */ ++ int max_iov; ++ ++ /* ++ * true if the length of the underlying file can change, and QEMU ++ * is expected to adjust automatically. Mostly for CD-ROM drives, ++ * whose length is zero when the tray is empty (they don't need ++ * an explicit monitor command to load the disk inside the guest). ++ */ ++ bool has_variable_length; ++ ++ /* device zone model */ ++ BlockZoneModel zoned; ++ ++ /* zone size expressed in bytes */ ++ uint32_t zone_size; ++ ++ /* total number of zones */ ++ uint32_t nr_zones; ++ ++ /* maximum sectors of a zone append write operation */ ++ uint32_t max_append_sectors; ++ ++ /* maximum number of open zones */ ++ uint32_t max_open_zones; ++ ++ /* maximum number of active zones */ ++ uint32_t max_active_zones; ++ ++ uint32_t write_granularity; ++} BlockLimits; ++ ++typedef struct BdrvOpBlocker BdrvOpBlocker; ++ ++typedef struct BdrvAioNotifier { ++ void (*attached_aio_context)(AioContext *new_context, void *opaque); ++ void (*detach_aio_context)(void *opaque); ++ ++ void *opaque; ++ bool deleted; ++ ++ QLIST_ENTRY(BdrvAioNotifier) list; ++} BdrvAioNotifier; ++ ++struct BdrvChildClass { ++ /* ++ * If true, bdrv_replace_node() doesn't change the node this BdrvChild ++ * points to. ++ */ ++ bool stay_at_node; ++ ++ /* ++ * If true, the parent is a BlockDriverState and bdrv_next_all_states() ++ * will return it. This information is used for drain_all, where every node ++ * will be drained separately, so the drain only needs to be propagated to ++ * non-BDS parents. ++ */ ++ bool parent_is_bds; ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ void (*inherit_options)(BdrvChildRole role, bool parent_is_format, ++ int *child_flags, QDict *child_options, ++ int parent_flags, QDict *parent_options); ++ void (*change_media)(BdrvChild *child, bool load); ++ ++ /* ++ * Returns a malloced string that describes the parent of the child for a ++ * human reader. This could be a node-name, BlockBackend name, qdev ID or ++ * QOM path of the device owning the BlockBackend, job type and ID etc. The ++ * caller is responsible for freeing the memory. ++ */ ++ char *(*get_parent_desc)(BdrvChild *child); ++ ++ /* ++ * Notifies the parent that the child has been activated/inactivated (e.g. ++ * when migration is completing) and it can start/stop requesting ++ * permissions and doing I/O on it. ++ */ ++ void GRAPH_RDLOCK_PTR (*activate)(BdrvChild *child, Error **errp); ++ int GRAPH_RDLOCK_PTR (*inactivate)(BdrvChild *child); ++ ++ void GRAPH_WRLOCK_PTR (*attach)(BdrvChild *child); ++ void GRAPH_WRLOCK_PTR (*detach)(BdrvChild *child); ++ ++ /* ++ * If this pair of functions is implemented, the parent doesn't issue new ++ * requests after returning from .drained_begin() until .drained_end() is ++ * called. ++ * ++ * These functions must not change the graph (and therefore also must not ++ * call aio_poll(), which could change the graph indirectly). ++ * ++ * Note that this can be nested. If drained_begin() was called twice, new ++ * I/O is allowed only after drained_end() was called twice, too. ++ */ ++ void GRAPH_RDLOCK_PTR (*drained_begin)(BdrvChild *child); ++ void GRAPH_RDLOCK_PTR (*drained_end)(BdrvChild *child); ++ ++ /* ++ * Returns whether the parent has pending requests for the child. This ++ * callback is polled after .drained_begin() has been called until all ++ * activity on the child has stopped. ++ */ ++ bool GRAPH_RDLOCK_PTR (*drained_poll)(BdrvChild *child); ++ ++ /* ++ * Notifies the parent that the filename of its child has changed (e.g. ++ * because the direct child was removed from the backing chain), so that it ++ * can update its reference. ++ */ ++ int (*update_filename)(BdrvChild *child, BlockDriverState *new_base, ++ const char *filename, ++ bool backing_mask_protocol, ++ Error **errp); ++ ++ bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp); ++ ++ /* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++ void (*resize)(BdrvChild *child); ++ ++ /* ++ * Returns a name that is supposedly more useful for human users than the ++ * node name for identifying the node in question (in particular, a BB ++ * name), or NULL if the parent can't provide a better name. ++ */ ++ const char *(*get_name)(BdrvChild *child); ++ ++ AioContext *(*get_parent_aio_context)(BdrvChild *child); ++}; ++ ++extern const BdrvChildClass child_of_bds; ++ ++struct BdrvChild { ++ BlockDriverState *bs; ++ char *name; ++ const BdrvChildClass *klass; ++ BdrvChildRole role; ++ void *opaque; ++ ++ /** ++ * Granted permissions for operating on this BdrvChild (BLK_PERM_* bitmask) ++ */ ++ uint64_t perm; ++ ++ /** ++ * Permissions that can still be granted to other users of @bs while this ++ * BdrvChild is still attached to it. (BLK_PERM_* bitmask) ++ */ ++ uint64_t shared_perm; ++ ++ /* ++ * This link is frozen: the child can neither be replaced nor ++ * detached from the parent. ++ */ ++ bool frozen; ++ ++ /* ++ * True if the parent of this child has been drained by this BdrvChild ++ * (through klass->drained_*). ++ * ++ * It is generally true if bs->quiesce_counter > 0. It may differ while the ++ * child is entering or leaving a drained section. ++ */ ++ bool quiesced_parent; ++ ++ QLIST_ENTRY(BdrvChild GRAPH_RDLOCK_PTR) next; ++ QLIST_ENTRY(BdrvChild GRAPH_RDLOCK_PTR) next_parent; ++}; ++ ++/* ++ * Allows bdrv_co_block_status() to cache one data region for a ++ * protocol node. ++ * ++ * @valid: Whether the cache is valid (should be accessed with atomic ++ * functions so this can be reset by RCU readers) ++ * @data_start: Offset where we know (or strongly assume) is data ++ * @data_end: Offset where the data region ends (which is not necessarily ++ * the start of a zeroed region) ++ */ ++typedef struct BdrvBlockStatusCache { ++ struct rcu_head rcu; ++ ++ bool valid; ++ int64_t data_start; ++ int64_t data_end; ++} BdrvBlockStatusCache; ++ ++struct BlockDriverState { ++ /* ++ * Protected by big QEMU lock or read-only after opening. No special ++ * locking needed during I/O... ++ */ ++ int open_flags; /* flags used to open the file, re-used for re-open */ ++ bool encrypted; /* if true, the media is encrypted */ ++ bool sg; /* if true, the device is a /dev/sg* */ ++ bool probed; /* if true, format was probed rather than specified */ ++ bool force_share; /* if true, always allow all shared permissions */ ++ bool implicit; /* if true, this filter node was automatically inserted */ ++ ++ BlockDriver *drv; /* NULL means no media */ ++ void *opaque; ++ ++ AioContext *aio_context; /* event loop used for fd handlers, timers, etc */ ++ /* ++ * long-running tasks intended to always use the same AioContext as this ++ * BDS may register themselves in this list to be notified of changes ++ * regarding this BDS's context ++ */ ++ QLIST_HEAD(, BdrvAioNotifier) aio_notifiers; ++ bool walking_aio_notifiers; /* to make removal during iteration safe */ ++ ++ char filename[PATH_MAX]; ++ /* ++ * If not empty, this image is a diff in relation to backing_file. ++ * Note that this is the name given in the image header and ++ * therefore may or may not be equal to .backing->bs->filename. ++ * If this field contains a relative path, it is to be resolved ++ * relatively to the overlay's location. ++ */ ++ char backing_file[PATH_MAX]; ++ /* ++ * The backing filename indicated by the image header. Contrary ++ * to backing_file, if we ever open this file, auto_backing_file ++ * is replaced by the resulting BDS's filename (i.e. after a ++ * bdrv_refresh_filename() run). ++ */ ++ char auto_backing_file[PATH_MAX]; ++ char backing_format[16]; /* if non-zero and backing_file exists */ ++ ++ QDict *full_open_options; ++ char exact_filename[PATH_MAX]; ++ ++ /* I/O Limits */ ++ BlockLimits bl; ++ ++ /* ++ * Flags honored during pread ++ */ ++ BdrvRequestFlags supported_read_flags; ++ /* ++ * Flags honored during pwrite (so far: BDRV_REQ_FUA, ++ * BDRV_REQ_WRITE_UNCHANGED). ++ * If a driver does not support BDRV_REQ_WRITE_UNCHANGED, those ++ * writes will be issued as normal writes without the flag set. ++ * This is important to note for drivers that do not explicitly ++ * request a WRITE permission for their children and instead take ++ * the same permissions as their parent did (this is commonly what ++ * block filters do). Such drivers have to be aware that the ++ * parent may have taken a WRITE_UNCHANGED permission only and is ++ * issuing such requests. Drivers either must make sure that ++ * these requests do not result in plain WRITE accesses (usually ++ * by supporting BDRV_REQ_WRITE_UNCHANGED, and then forwarding ++ * every incoming write request as-is, including potentially that ++ * flag), or they have to explicitly take the WRITE permission for ++ * their children. ++ */ ++ BdrvRequestFlags supported_write_flags; ++ /* ++ * Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA, ++ * BDRV_REQ_MAY_UNMAP, BDRV_REQ_WRITE_UNCHANGED) ++ */ ++ BdrvRequestFlags supported_zero_flags; ++ /* ++ * Flags honoured during truncate (so far: BDRV_REQ_ZERO_WRITE). ++ * ++ * If BDRV_REQ_ZERO_WRITE is given, the truncate operation must make sure ++ * that any added space reads as all zeros. If this can't be guaranteed, ++ * the operation must fail. ++ */ ++ BdrvRequestFlags supported_truncate_flags; ++ ++ /* the following member gives a name to every node on the bs graph. */ ++ char node_name[32]; ++ /* element of the list of named nodes building the graph */ ++ QTAILQ_ENTRY(BlockDriverState) node_list; ++ /* element of the list of all BlockDriverStates (all_bdrv_states) */ ++ QTAILQ_ENTRY(BlockDriverState) bs_list; ++ /* element of the list of monitor-owned BDS */ ++ QTAILQ_ENTRY(BlockDriverState) monitor_list; ++ int refcnt; ++ ++ /* operation blockers. Protected by BQL. */ ++ QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MAX]; ++ ++ /* ++ * The node that this node inherited default options from (and a reopen on ++ * which can affect this node by changing these defaults). This is always a ++ * 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 GRAPH_RDLOCK_PTR) children; ++ BdrvChild * GRAPH_RDLOCK_PTR backing; ++ BdrvChild * GRAPH_RDLOCK_PTR file; ++ ++ QLIST_HEAD(, BdrvChild GRAPH_RDLOCK_PTR) parents; ++ ++ QDict *options; ++ QDict *explicit_options; ++ BlockdevDetectZeroesOptions detect_zeroes; ++ ++ /* The error object in use for blocking operations on backing_hd */ ++ Error *backing_blocker; ++ ++ /* ++ * If we are reading a disk image, give its size in sectors. ++ * Generally read-only; it is written to by load_snapshot and ++ * save_snaphost, but the block layer is quiescent during those. ++ */ ++ int64_t total_sectors; ++ ++ /* threshold limit for writes, in bytes. "High water mark". */ ++ uint64_t write_threshold_offset; ++ ++ /* ++ * Writing to the list requires the BQL _and_ the dirty_bitmap_mutex. ++ * Reading from the list can be done with either the BQL or the ++ * dirty_bitmap_mutex. Modifying a bitmap only requires ++ * dirty_bitmap_mutex. ++ */ ++ QemuMutex dirty_bitmap_mutex; ++ QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps; ++ ++ /* Offset after the highest byte written to */ ++ Stat64 wr_highest_offset; ++ ++ /* ++ * If true, copy read backing sectors into image. Can be >1 if more ++ * than one client has requested copy-on-read. Accessed with atomic ++ * ops. ++ */ ++ int copy_on_read; ++ ++ /* ++ * number of in-flight requests; overall and serialising. ++ * Accessed with atomic ops. ++ */ ++ unsigned int in_flight; ++ unsigned int serialising_in_flight; ++ ++ /* do we need to tell the quest if we have a volatile write cache? */ ++ int enable_write_cache; ++ ++ /* Accessed with atomic ops. */ ++ int quiesce_counter; ++ ++ unsigned int write_gen; /* Current data generation */ ++ ++ /* Protected by reqs_lock. */ ++ QemuMutex reqs_lock; ++ QLIST_HEAD(, BdrvTrackedRequest) tracked_requests; ++ CoQueue flush_queue; /* Serializing flush queue */ ++ bool active_flush_req; /* Flush request in flight? */ ++ ++ /* Only read/written by whoever has set active_flush_req to true. */ ++ unsigned int flushed_gen; /* Flushed write generation */ ++ ++ /* BdrvChild links to this node may never be frozen */ ++ bool never_freeze; ++ ++ /* Lock for block-status cache RCU writers */ ++ CoMutex bsc_modify_lock; ++ /* Always non-NULL, but must only be dereferenced under an RCU read guard */ ++ BdrvBlockStatusCache *block_status_cache; ++ ++ /* array of write pointers' location of each zone in the zoned device. */ ++ BlockZoneWps *wps; ++}; ++ ++struct BlockBackendRootState { ++ int open_flags; ++ BlockdevDetectZeroesOptions detect_zeroes; ++}; ++ ++typedef enum BlockMirrorBackingMode { ++ /* ++ * Reuse the existing backing chain from the source for the target. ++ * - sync=full: Set backing BDS to NULL. ++ * - sync=top: Use source's backing BDS. ++ * - sync=none: Use source as the backing BDS. ++ */ ++ MIRROR_SOURCE_BACKING_CHAIN, ++ ++ /* Open the target's backing chain completely anew */ ++ MIRROR_OPEN_BACKING_CHAIN, ++ ++ /* Do not change the target's backing BDS after job completion */ ++ MIRROR_LEAVE_BACKING_CHAIN, ++} BlockMirrorBackingMode; ++ ++ ++/* ++ * Essential block drivers which must always be statically linked into qemu, and ++ * which therefore can be accessed without using bdrv_find_format() ++ */ ++extern BlockDriver bdrv_file; ++extern BlockDriver bdrv_raw; ++extern BlockDriver bdrv_qcow2; ++ ++extern unsigned int bdrv_drain_all_count; ++extern QemuOptsList bdrv_create_opts_simple; ++ ++/* ++ * Common functions that are neither I/O nor Global State. ++ * ++ * See include/block/block-common.h for more information about ++ * the Common API. ++ */ ++ ++static inline BlockDriverState *child_bs(BdrvChild *child) ++{ ++ return child ? child->bs : NULL; ++} ++ ++int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp); ++char *create_tmp_file(Error **errp); ++void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, ++ QDict *options); ++ ++ ++int bdrv_check_qiov_request(int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ Error **errp); ++ ++#ifdef _WIN32 ++int is_windows_drive(const char *filename); ++#endif ++ ++#endif /* BLOCK_INT_COMMON_H */ +diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h +new file mode 100644 +index 00000000..eb2d92a2 +--- /dev/null ++++ b/include/block/block_int-global-state.h +@@ -0,0 +1,326 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_INT_GLOBAL_STATE_H ++#define BLOCK_INT_GLOBAL_STATE_H ++ ++#include "block/blockjob.h" ++#include "block/block_int-common.h" ++#include "qemu/hbitmap.h" ++#include "qemu/main-loop.h" ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++/** ++ * stream_start: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Block device to operate on. ++ * @base: Block device that will become the new base, or %NULL to ++ * flatten the whole backing file chain onto @bs. ++ * @backing_file_str: The file name that will be written to @bs as the ++ * the new backing file if the job completes. Ignored if @base is %NULL. ++ * @backing_mask_protocol: Replace potential protocol name with 'raw' in ++ * 'backing file format' header ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @on_error: The action to take upon error. ++ * @filter_node_name: The node name that should be assigned to the filter ++ * driver that the stream job inserts into the graph above ++ * @bs. NULL means that a node name should be autogenerated. ++ * @errp: Error object. ++ * ++ * Start a streaming operation on @bs. Clusters that are unallocated ++ * in @bs, but allocated in any image between @base and @bs (both ++ * exclusive) will be written to @bs. At the end of a successful ++ * streaming job, the backing file of @bs will be changed to ++ * @backing_file_str in the written image and to @base in the live ++ * BlockDriverState. ++ */ ++void stream_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, const char *backing_file_str, ++ bool backing_mask_protocol, ++ BlockDriverState *bottom, ++ int creation_flags, int64_t speed, ++ BlockdevOnError on_error, ++ const char *filter_node_name, ++ Error **errp); ++ ++/** ++ * commit_start: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Active block device. ++ * @top: Top block device to be committed. ++ * @base: Block device that will be written into, and become the new top. ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @on_error: The action to take upon error. ++ * @backing_file_str: String to use as the backing file in @top's overlay ++ * @backing_mask_protocol: Replace potential protocol name with 'raw' in ++ * 'backing file format' header ++ * @filter_node_name: The node name that should be assigned to the filter ++ * driver that the commit job inserts into the graph above @top. NULL means ++ * that a node name should be autogenerated. ++ * @errp: Error object. ++ * ++ */ ++void commit_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, BlockDriverState *top, ++ int creation_flags, int64_t speed, ++ BlockdevOnError on_error, const char *backing_file_str, ++ bool backing_mask_protocol, ++ const char *filter_node_name, Error **errp); ++/** ++ * commit_active_start: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Active block device to be committed. ++ * @base: Block device that will be written into, and become the new top. ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @on_error: The action to take upon error. ++ * @filter_node_name: The node name that should be assigned to the filter ++ * driver that the commit job inserts into the graph above @bs. NULL means that ++ * a node name should be autogenerated. ++ * @cb: Completion function for the job. ++ * @opaque: Opaque pointer value passed to @cb. ++ * @auto_complete: Auto complete the job. ++ * @errp: Error object. ++ * ++ */ ++BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, int creation_flags, ++ int64_t speed, BlockdevOnError on_error, ++ const char *filter_node_name, ++ BlockCompletionFunc *cb, void *opaque, ++ bool auto_complete, Error **errp); ++/* ++ * mirror_start: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Block device to operate on. ++ * @target: Block device to write to. ++ * @replaces: Block graph node name to replace once the mirror is done. Can ++ * only be used when full mirroring is selected. ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @granularity: The chosen granularity for the dirty bitmap. ++ * @buf_size: The amount of data that can be in flight at one time. ++ * @mode: Whether to collapse all images in the chain to the target. ++ * @backing_mode: How to establish the target's backing chain after completion. ++ * @zero_target: Whether the target should be explicitly zero-initialized ++ * @on_source_error: The action to take upon error reading from the source. ++ * @on_target_error: The action to take upon error writing to the target. ++ * @unmap: Whether to unmap target where source sectors only contain zeroes. ++ * @filter_node_name: The node name that should be assigned to the filter ++ * driver that the mirror job inserts into the graph above @bs. NULL means that ++ * a node name should be autogenerated. ++ * @copy_mode: When to trigger writes to the target. ++ * @errp: Error object. ++ * ++ * Start a mirroring operation on @bs. Clusters that are allocated ++ * in @bs will be written to @target until the job is cancelled or ++ * manually completed. At the end of a successful mirroring job, ++ * @bs will be switched to read from @target. ++ */ ++void mirror_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *target, const char *replaces, ++ int creation_flags, int64_t speed, ++ uint32_t granularity, int64_t buf_size, ++ MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, ++ bool zero_target, ++ BlockdevOnError on_source_error, ++ BlockdevOnError on_target_error, ++ bool unmap, const char *filter_node_name, ++ MirrorCopyMode copy_mode, Error **errp); ++ ++/* ++ * backup_job_create: ++ * @job_id: The id of the newly-created job, or %NULL to use the ++ * device name of @bs. ++ * @bs: Block device to operate on. ++ * @target: Block device to write to. ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @sync_mode: What parts of the disk image should be copied to the destination. ++ * @sync_bitmap: The dirty bitmap if sync_mode is 'bitmap' or 'incremental' ++ * @bitmap_mode: The bitmap synchronization policy to use. ++ * @perf: Performance options. All actual fields assumed to be present, ++ * all ".has_*" fields are ignored. ++ * @on_source_error: The action to take upon error reading from the source. ++ * @on_target_error: The action to take upon error writing to the target. ++ * @creation_flags: Flags that control the behavior of the Job lifetime. ++ * See @BlockJobCreateFlags ++ * @cb: Completion function for the job. ++ * @opaque: Opaque pointer value passed to @cb. ++ * @txn: Transaction that this job is part of (may be NULL). ++ * ++ * Create a backup operation on @bs. Clusters in @bs are written to @target ++ * until the job is cancelled or manually completed. ++ */ ++BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *target, int64_t speed, ++ MirrorSyncMode sync_mode, ++ BdrvDirtyBitmap *sync_bitmap, ++ BitmapSyncMode bitmap_mode, ++ bool compress, bool discard_source, ++ const char *filter_node_name, ++ BackupPerf *perf, ++ BlockdevOnError on_source_error, ++ BlockdevOnError on_target_error, ++ int creation_flags, ++ BlockCompletionFunc *cb, void *opaque, ++ JobTxn *txn, Error **errp); ++ ++BdrvChild * GRAPH_WRLOCK ++bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ uint64_t perm, uint64_t shared_perm, ++ void *opaque, Error **errp); ++ ++void GRAPH_WRLOCK bdrv_root_unref_child(BdrvChild *child); ++ ++void GRAPH_RDLOCK bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, ++ uint64_t *shared_perm); ++ ++/** ++ * Sets a BdrvChild's permissions. Avoid if the parent is a BDS; use ++ * bdrv_child_refresh_perms() instead and make the parent's ++ * .bdrv_child_perm() implementation return the correct values. ++ */ ++int GRAPH_RDLOCK ++bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, ++ Error **errp); ++ ++/** ++ * Calls bs->drv->bdrv_child_perm() and updates the child's permission ++ * masks with the result. ++ * Drivers should invoke this function whenever an event occurs that ++ * makes their .bdrv_child_perm() implementation return different ++ * values than before, but which will not result in the block layer ++ * automatically refreshing the permissions. ++ */ ++int GRAPH_RDLOCK ++bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); ++ ++bool GRAPH_RDLOCK bdrv_recurse_can_replace(BlockDriverState *bs, ++ BlockDriverState *to_replace); ++ ++/* ++ * Default implementation for BlockDriver.bdrv_child_perm() that can ++ * be used by block filters and image formats, as long as they use the ++ * child_of_bds child class and set an appropriate BdrvChildRole. ++ */ ++void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared); ++ ++void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); ++bool blk_dev_has_removable_media(BlockBackend *blk); ++void blk_dev_eject_request(BlockBackend *blk, bool force); ++bool blk_dev_is_medium_locked(BlockBackend *blk); ++ ++void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup); ++ ++void bdrv_set_monitor_owned(BlockDriverState *bs); ++ ++void blockdev_close_all_bdrv_states(void); ++ ++BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp); ++ ++/** ++ * Simple implementation of bdrv_co_create_opts for protocol drivers ++ * which only support creation via opening a file ++ * (usually existing raw storage device) ++ */ ++int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, ++ const char *filename, ++ QemuOpts *opts, ++ Error **errp); ++ ++BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, ++ const char *name, ++ BlockDriverState **pbs, ++ Error **errp); ++BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target, ++ BlockDirtyBitmapOrStrList *bms, ++ HBitmap **backup, Error **errp); ++BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, ++ bool release, ++ BlockDriverState **bitmap_bs, ++ Error **errp); ++ ++ ++BlockDriverState * GRAPH_RDLOCK ++bdrv_skip_implicit_filters(BlockDriverState *bs); ++ ++/** ++ * bdrv_add_aio_context_notifier: ++ * ++ * If a long-running job intends to be always run in the same AioContext as a ++ * certain BDS, it may use this function to be notified of changes regarding the ++ * association of the BDS to an AioContext. ++ * ++ * attached_aio_context() is called after the target BDS has been attached to a ++ * new AioContext; detach_aio_context() is called before the target BDS is being ++ * detached from its old AioContext. ++ */ ++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); ++ ++/** ++ * bdrv_remove_aio_context_notifier: ++ * ++ * Unsubscribe of change notifications regarding the BDS's AioContext. The ++ * parameters given here have to be the same as those given to ++ * bdrv_add_aio_context_notifier(). ++ */ ++void bdrv_remove_aio_context_notifier(BlockDriverState *bs, ++ void (*aio_context_attached)(AioContext *, ++ void *), ++ void (*aio_context_detached)(void *), ++ void *opaque); ++ ++/** ++ * End all quiescent sections started by bdrv_drain_all_begin(). This is ++ * needed when deleting a BDS before bdrv_drain_all_end() is called. ++ * ++ * NOTE: this is an internal helper for bdrv_close() *only*. No one else ++ * should call it. ++ */ ++void bdrv_drain_all_end_quiesce(BlockDriverState *bs); ++ ++#endif /* BLOCK_INT_GLOBAL_STATE_H */ +diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h +new file mode 100644 +index 00000000..4a7cf2b4 +--- /dev/null ++++ b/include/block/block_int-io.h +@@ -0,0 +1,194 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_INT_IO_H ++#define BLOCK_INT_IO_H ++ ++#include "block/block_int-common.h" ++#include "qemu/hbitmap.h" ++#include "qemu/main-loop.h" ++ ++/* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv_snapshot(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_snapshot_block_status( ++ BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard_snapshot(BlockDriverState *bs, ++ int64_t offset, int64_t bytes); ++ ++ ++int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv_part(BdrvChild *child, ++ int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_pwritev(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int coroutine_fn GRAPH_RDLOCK bdrv_co_pwritev_part(BdrvChild *child, ++ int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags); ++ ++static inline int coroutine_fn GRAPH_RDLOCK bdrv_co_pread(BdrvChild *child, ++ int64_t offset, int64_t bytes, void *buf, BdrvRequestFlags flags) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ return bdrv_co_preadv(child, offset, bytes, &qiov, flags); ++} ++ ++static inline int coroutine_fn GRAPH_RDLOCK bdrv_co_pwrite(BdrvChild *child, ++ int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ return bdrv_co_pwritev(child, offset, bytes, &qiov, flags); ++} ++ ++void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, ++ uint64_t align); ++BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs); ++ ++BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, ++ const char *filename); ++ ++/** ++ * bdrv_wakeup: ++ * @bs: The BlockDriverState for which an I/O operation has been completed. ++ * ++ * Wake up the main thread if it is waiting on BDRV_POLL_WHILE. During ++ * synchronous I/O on a BlockDriverState that is attached to another ++ * I/O thread, the main thread lets the I/O thread's event loop run, ++ * waiting for the I/O operation to complete. A bdrv_wakeup will wake ++ * up the main thread if necessary. ++ * ++ * Manual calls to bdrv_wakeup are rarely necessary, because ++ * bdrv_dec_in_flight already calls it. ++ */ ++void bdrv_wakeup(BlockDriverState *bs); ++ ++const char * GRAPH_RDLOCK bdrv_get_parent_name(const BlockDriverState *bs); ++bool blk_dev_has_tray(BlockBackend *blk); ++bool blk_dev_is_tray_open(BlockBackend *blk); ++ ++void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); ++void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, ++ const BdrvDirtyBitmap *src, ++ HBitmap **backup, bool lock); ++ ++void bdrv_inc_in_flight(BlockDriverState *bs); ++void bdrv_dec_in_flight(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_refresh_total_sectors(BlockDriverState *bs, int64_t hint); ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint); ++ ++BdrvChild * GRAPH_RDLOCK bdrv_cow_child(BlockDriverState *bs); ++BdrvChild * GRAPH_RDLOCK bdrv_filter_child(BlockDriverState *bs); ++BdrvChild * GRAPH_RDLOCK bdrv_filter_or_cow_child(BlockDriverState *bs); ++BdrvChild * GRAPH_RDLOCK bdrv_primary_child(BlockDriverState *bs); ++BlockDriverState * GRAPH_RDLOCK bdrv_skip_filters(BlockDriverState *bs); ++BlockDriverState * GRAPH_RDLOCK bdrv_backing_chain_next(BlockDriverState *bs); ++ ++static inline BlockDriverState * GRAPH_RDLOCK ++bdrv_cow_bs(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return child_bs(bdrv_cow_child(bs)); ++} ++ ++static inline BlockDriverState * GRAPH_RDLOCK ++bdrv_filter_bs(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return child_bs(bdrv_filter_child(bs)); ++} ++ ++static inline BlockDriverState * GRAPH_RDLOCK ++bdrv_filter_or_cow_bs(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return child_bs(bdrv_filter_or_cow_child(bs)); ++} ++ ++static inline BlockDriverState * GRAPH_RDLOCK ++bdrv_primary_bs(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return child_bs(bdrv_primary_child(bs)); ++} ++ ++/** ++ * Check whether the given offset is in the cached block-status data ++ * region. ++ * ++ * If it is, and @pnum is not NULL, *pnum is set to ++ * `bsc.data_end - offset`, i.e. how many bytes, starting from ++ * @offset, are data (according to the cache). ++ * Otherwise, *pnum is not touched. ++ */ ++bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum); ++ ++/** ++ * If [offset, offset + bytes) overlaps with the currently cached ++ * block-status region, invalidate the cache. ++ * ++ * (To be used by I/O paths that cause data regions to be zero or ++ * holes.) ++ */ ++void bdrv_bsc_invalidate_range(BlockDriverState *bs, ++ int64_t offset, int64_t bytes); ++ ++/** ++ * Mark the range [offset, offset + bytes) as a data region. ++ */ ++void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes); ++ ++#endif /* BLOCK_INT_IO_H */ +diff --git a/include/block/block_int.h b/include/block/block_int.h +new file mode 100644 +index 00000000..567a178e +--- /dev/null ++++ b/include/block/block_int.h +@@ -0,0 +1,33 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#ifndef BLOCK_INT_H ++#define BLOCK_INT_H ++ ++#include "block/block_int-global-state.h" ++#include "block/block_int-io.h" ++#include "block/graph-lock.h" ++ ++/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ ++ ++#endif /* BLOCK_INT_H */ +diff --git a/include/block/blockjob.h b/include/block/blockjob.h +new file mode 100644 +index 00000000..7061ab72 +--- /dev/null ++++ b/include/block/blockjob.h +@@ -0,0 +1,238 @@ ++/* ++ * Declarations for long-running block device operations ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCKJOB_H ++#define BLOCKJOB_H ++ ++#include "qapi/qapi-types-block-core.h" ++#include "qemu/job.h" ++#include "qemu/ratelimit.h" ++ ++#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ ++ ++typedef struct BlockJobDriver BlockJobDriver; ++ ++/** ++ * BlockJob: ++ * ++ * Long-running operation on a BlockDriverState. ++ */ ++typedef struct BlockJob { ++ /** ++ * Data belonging to the generic Job infrastructure. ++ * Protected by job mutex. ++ */ ++ Job job; ++ ++ /** ++ * Status that is published by the query-block-jobs QMP API. ++ * Protected by job mutex. ++ */ ++ BlockDeviceIoStatus iostatus; ++ ++ /** ++ * Speed that was set with @block_job_set_speed. ++ * Always modified and read under the BQL (GLOBAL_STATE_CODE). ++ */ ++ int64_t speed; ++ ++ /** ++ * Rate limiting data structure for implementing @speed. ++ * RateLimit API is thread-safe. ++ */ ++ RateLimit limit; ++ ++ /** ++ * Block other operations when block job is running. ++ * Always modified and read under the BQL (GLOBAL_STATE_CODE). ++ */ ++ Error *blocker; ++ ++ /** All notifiers are set once in block_job_create() and never modified. */ ++ ++ /** Called when a cancelled job is finalised. */ ++ Notifier finalize_cancelled_notifier; ++ ++ /** Called when a successfully completed job is finalised. */ ++ Notifier finalize_completed_notifier; ++ ++ /** Called when the job transitions to PENDING */ ++ Notifier pending_notifier; ++ ++ /** Called when the job transitions to READY */ ++ Notifier ready_notifier; ++ ++ /** Called when the job coroutine yields or terminates */ ++ Notifier idle_notifier; ++ ++ /** ++ * BlockDriverStates that are involved in this block job. ++ * Always modified and read under the BQL (GLOBAL_STATE_CODE). ++ */ ++ GSList *nodes; ++} BlockJob; ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++/** ++ * block_job_next_locked: ++ * @job: A block job, or %NULL. ++ * ++ * Get the next element from the list of block jobs after @job, or the ++ * first one if @job is %NULL. ++ * ++ * Returns the requested job, or %NULL if there are no more jobs left. ++ * Called with job lock held. ++ */ ++BlockJob *block_job_next_locked(BlockJob *job); ++ ++/** ++ * block_job_get: ++ * @id: The id of the block job. ++ * ++ * Get the block job identified by @id (which must not be %NULL). ++ * ++ * Returns the requested job, or %NULL if it doesn't exist. ++ * Called with job lock *not* held. ++ */ ++BlockJob *block_job_get(const char *id); ++ ++/* Same as block_job_get(), but called with job lock held. */ ++BlockJob *block_job_get_locked(const char *id); ++ ++/** ++ * block_job_add_bdrv: ++ * @job: A block job ++ * @name: The name to assign to the new BdrvChild ++ * @bs: A BlockDriverState that is involved in @job ++ * @perm, @shared_perm: Permissions to request on the node ++ * ++ * Add @bs to the list of BlockDriverState that are involved in ++ * @job. This means that all operations will be blocked on @bs while ++ * @job exists. ++ */ ++int GRAPH_WRLOCK ++block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, ++ uint64_t perm, uint64_t shared_perm, Error **errp); ++ ++/** ++ * block_job_remove_all_bdrv: ++ * @job: The block job ++ * ++ * Remove all BlockDriverStates from the list of nodes that are involved in the ++ * job. This removes the blockers added with block_job_add_bdrv(). ++ */ ++void block_job_remove_all_bdrv(BlockJob *job); ++ ++/** ++ * block_job_has_bdrv: ++ * @job: The block job ++ * ++ * Searches for @bs in the list of nodes that are involved in the ++ * job. ++ */ ++bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs); ++ ++/** ++ * block_job_set_speed_locked: ++ * @job: The job to set the speed for. ++ * @speed: The new value ++ * @errp: Error object. ++ * ++ * Set a rate-limiting parameter for the job; the actual meaning may ++ * vary depending on the job type. ++ * ++ * Called with job lock held, but might release it temporarily. ++ */ ++bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp); ++ ++/** ++ * block_job_change_locked: ++ * @job: The job to change. ++ * @opts: The new options. ++ * @errp: Error object. ++ * ++ * Change the job according to opts. ++ */ ++void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, ++ Error **errp); ++ ++/** ++ * block_job_query_locked: ++ * @job: The job to get information about. ++ * ++ * Return information about a job. ++ * ++ * Called with job lock held. ++ */ ++BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp); ++ ++/** ++ * block_job_iostatus_reset_locked: ++ * @job: The job whose I/O status should be reset. ++ * ++ * Reset I/O status on @job and on BlockDriverState objects it uses, ++ * other than job->blk. ++ * ++ * Called with job lock held. ++ */ ++void block_job_iostatus_reset_locked(BlockJob *job); ++ ++/* ++ * block_job_get_aio_context: ++ * ++ * Returns aio context associated with a block job. ++ */ ++AioContext *block_job_get_aio_context(BlockJob *job); ++ ++ ++/* ++ * Common functions that are neither I/O nor Global State. ++ * ++ * See include/block/block-common.h for more information about ++ * the Common API. ++ */ ++ ++/** ++ * block_job_is_internal: ++ * @job: The job to determine if it is user-visible or not. ++ * ++ * Returns true if the job should not be visible to the management layer. ++ */ ++bool block_job_is_internal(BlockJob *job); ++ ++/** ++ * block_job_driver: ++ * ++ * Returns the driver associated with a block job. ++ */ ++const BlockJobDriver *block_job_driver(BlockJob *job); ++ ++#endif +diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h +new file mode 100644 +index 00000000..4c3d2e25 +--- /dev/null ++++ b/include/block/blockjob_int.h +@@ -0,0 +1,168 @@ ++/* ++ * Declarations for long-running block device operations ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCKJOB_INT_H ++#define BLOCKJOB_INT_H ++ ++#include "block/blockjob.h" ++ ++/** ++ * BlockJobDriver: ++ * ++ * A class type for block job driver. ++ */ ++struct BlockJobDriver { ++ /** Generic JobDriver callbacks and settings */ ++ JobDriver job_driver; ++ ++ /* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++ /* ++ * Returns whether the job has pending requests for the child or will ++ * submit new requests before the next pause point. This callback is polled ++ * in the context of draining a job node after requesting that the job be ++ * paused, until all activity on the child has stopped. ++ */ ++ bool (*drained_poll)(BlockJob *job); ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++ /* ++ * If the callback is not NULL, it will be invoked before the job is ++ * resumed in a new AioContext. This is the place to move any resources ++ * besides job->blk to the new AioContext. ++ */ ++ void (*attached_aio_context)(BlockJob *job, AioContext *new_context); ++ ++ void (*set_speed)(BlockJob *job, int64_t speed); ++ ++ /* ++ * Change the @job's options according to @opts. ++ * ++ * Note that this can already be called before the job coroutine is running. ++ */ ++ void (*change)(BlockJob *job, BlockJobChangeOptions *opts, Error **errp); ++ ++ /* ++ * Query information specific to this kind of block job. ++ */ ++ void (*query)(BlockJob *job, BlockJobInfo *info); ++}; ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++/** ++ * block_job_create: ++ * @job_id: The id of the newly-created job, or %NULL to have one ++ * generated automatically. ++ * @driver: The class object for the newly-created job. ++ * @txn: The transaction this job belongs to, if any. %NULL otherwise. ++ * @bs: The block ++ * @perm, @shared_perm: Permissions to request for @bs ++ * @speed: The maximum speed, in bytes per second, or 0 for unlimited. ++ * @flags: Creation flags for the Block Job. See @JobCreateFlags. ++ * @cb: Completion function for the job. ++ * @opaque: Opaque pointer value passed to @cb. ++ * @errp: Error object. ++ * ++ * Create a new long-running block device job and return it. The job ++ * will call @cb asynchronously when the job completes. Note that ++ * @bs may have been closed at the time the @cb it is called. If ++ * this is the case, the job may be reported as either cancelled or ++ * completed. ++ * ++ * This function is not part of the public job interface; it should be ++ * called from a wrapper that is specific to the job type. ++ */ ++void * GRAPH_UNLOCKED ++block_job_create(const char *job_id, const BlockJobDriver *driver, ++ JobTxn *txn, BlockDriverState *bs, uint64_t perm, ++ uint64_t shared_perm, int64_t speed, int flags, ++ BlockCompletionFunc *cb, void *opaque, Error **errp); ++ ++/** ++ * block_job_free: ++ * Callback to be used for JobDriver.free in all block jobs. Frees block job ++ * specific resources in @job. ++ */ ++void block_job_free(Job *job); ++ ++/** ++ * block_job_user_resume: ++ * Callback to be used for JobDriver.user_resume in all block jobs. Resets the ++ * iostatus when the user resumes @job. ++ */ ++void block_job_user_resume(Job *job); ++ ++/* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++/** ++ * block_job_ratelimit_processed_bytes: ++ * ++ * To be called after some work has been done. Adjusts the delay for the next ++ * request. See the documentation of ratelimit_calculate_delay() for details. ++ */ ++void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n); ++ ++/** ++ * Put the job to sleep (assuming that it wasn't canceled) to throttle it to the ++ * right speed according to its rate limiting. ++ */ ++void block_job_ratelimit_sleep(BlockJob *job); ++ ++/** ++ * block_job_error_action: ++ * @job: The job to signal an error for. ++ * @on_err: The error action setting. ++ * @is_read: Whether the operation was a read. ++ * @error: The error that was reported. ++ * ++ * Report an I/O error for a block job and possibly stop the VM. Return the ++ * action that was selected based on @on_err and @error. ++ */ ++BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, ++ int is_read, int error); ++ ++#endif +diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h +new file mode 100644 +index 00000000..fa956deb +--- /dev/null ++++ b/include/block/dirty-bitmap.h +@@ -0,0 +1,130 @@ ++#ifndef BLOCK_DIRTY_BITMAP_H ++#define BLOCK_DIRTY_BITMAP_H ++ ++#include "block/block-common.h" ++#include "qapi/qapi-types-block-core.h" ++#include "qemu/hbitmap.h" ++ ++typedef enum BitmapCheckFlags { ++ BDRV_BITMAP_BUSY = 1, ++ BDRV_BITMAP_RO = 2, ++ BDRV_BITMAP_INCONSISTENT = 4, ++} BitmapCheckFlags; ++ ++#define BDRV_BITMAP_DEFAULT (BDRV_BITMAP_BUSY | BDRV_BITMAP_RO | \ ++ BDRV_BITMAP_INCONSISTENT) ++#define BDRV_BITMAP_ALLOW_RO (BDRV_BITMAP_BUSY | BDRV_BITMAP_INCONSISTENT) ++ ++#define BDRV_BITMAP_MAX_NAME_SIZE 1023 ++ ++bool bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs); ++BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, ++ uint32_t granularity, ++ const char *name, ++ Error **errp); ++int bdrv_dirty_bitmap_create_successor(BdrvDirtyBitmap *bitmap, ++ Error **errp); ++BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BdrvDirtyBitmap *bitmap, ++ Error **errp); ++BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ Error **errp); ++void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap); ++BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, ++ const char *name); ++int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, ++ Error **errp); ++void bdrv_release_dirty_bitmap(BdrvDirtyBitmap *bitmap); ++void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, ++ Error **errp); ++int co_wrapper_bdrv_rdlock ++bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, ++ Error **errp); ++ ++void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap); ++void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); ++void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap); ++BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); ++uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); ++uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap); ++const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap); ++int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap); ++void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes); ++void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes); ++BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter); ++ ++uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes); ++uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap); ++uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size, ++ const BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, ++ uint8_t *buf, uint64_t offset, ++ uint64_t bytes); ++void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, ++ uint8_t *buf, uint64_t offset, ++ uint64_t bytes, bool finish); ++void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes, ++ bool finish); ++void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes, ++ bool finish); ++void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); ++ ++void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); ++void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, ++ bool persistent); ++void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy); ++bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, ++ HBitmap **backup, Error **errp); ++void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip); ++bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset); ++ ++/* Functions that require manual locking. */ ++void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_get_locked(BdrvDirtyBitmap *bitmap, int64_t offset); ++void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes); ++void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes); ++int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); ++void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); ++int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); ++void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes); ++bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap); ++bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); ++bool bdrv_has_named_bitmaps(BlockDriverState *bs); ++bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap); ++bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap); ++ ++BdrvDirtyBitmap *bdrv_dirty_bitmap_first(BlockDriverState *bs); ++BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap); ++#define FOR_EACH_DIRTY_BITMAP(bs, bitmap) \ ++for (bitmap = bdrv_dirty_bitmap_first(bs); bitmap; \ ++ bitmap = bdrv_dirty_bitmap_next(bitmap)) ++ ++char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp); ++int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes); ++int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes); ++bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, ++ int64_t start, int64_t end, int64_t max_dirty_count, ++ int64_t *dirty_start, int64_t *dirty_count); ++bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes, int64_t *count); ++BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ Error **errp); ++ ++#endif +diff --git a/include/block/export.h b/include/block/export.h +new file mode 100644 +index 00000000..f2fe0f80 +--- /dev/null ++++ b/include/block/export.h +@@ -0,0 +1,91 @@ ++/* ++ * Declarations for block exports ++ * ++ * Copyright (c) 2012, 2020 Red Hat, Inc. ++ * ++ * Authors: ++ * Paolo Bonzini ++ * Kevin Wolf ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_EXPORT_H ++#define BLOCK_EXPORT_H ++ ++#include "qapi/qapi-types-block-export.h" ++#include "qemu/queue.h" ++ ++typedef struct BlockExport BlockExport; ++ ++typedef struct BlockExportDriver { ++ /* The export type that this driver services */ ++ BlockExportType type; ++ ++ /* ++ * The size of the driver-specific state that contains BlockExport as its ++ * first field. ++ */ ++ size_t instance_size; ++ ++ /* Creates and starts a new block export */ ++ int (*create)(BlockExport *, BlockExportOptions *, Error **); ++ ++ /* ++ * Frees a removed block export. This function is only called after all ++ * references have been dropped. ++ */ ++ void (*delete)(BlockExport *); ++ ++ /* ++ * Start to disconnect all clients and drop other references held ++ * internally by the export driver. When the function returns, there may ++ * still be active references while the export is in the process of ++ * shutting down. ++ */ ++ void (*request_shutdown)(BlockExport *); ++} BlockExportDriver; ++ ++struct BlockExport { ++ const BlockExportDriver *drv; ++ ++ /* Unique identifier for the export */ ++ char *id; ++ ++ /* ++ * Reference count for this block export. This includes strong references ++ * both from the owner (qemu-nbd or the monitor) and clients connected to ++ * the export. ++ * ++ * Use atomics to access this field. ++ */ ++ int refcount; ++ ++ /* ++ * True if one of the references in refcount belongs to the user. After the ++ * user has dropped their reference, they may not e.g. remove the same ++ * export a second time (which would decrease the refcount without having ++ * it incremented first). ++ */ ++ bool user_owned; ++ ++ /* The AioContext whose lock protects this BlockExport object. */ ++ AioContext *ctx; ++ ++ /* The block device to export */ ++ BlockBackend *blk; ++ ++ /* List entry for block_exports */ ++ QLIST_ENTRY(BlockExport) next; ++}; ++ ++BlockExport *blk_exp_add(BlockExportOptions *export, Error **errp); ++BlockExport *blk_exp_find(const char *id); ++void blk_exp_ref(BlockExport *exp); ++void blk_exp_unref(BlockExport *exp); ++void blk_exp_request_shutdown(BlockExport *exp); ++void blk_exp_close_all(void); ++void blk_exp_close_all_type(BlockExportType type); ++ ++#endif +diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h +new file mode 100644 +index 00000000..dc8d9491 +--- /dev/null ++++ b/include/block/graph-lock.h +@@ -0,0 +1,285 @@ ++/* ++ * Graph lock: rwlock to protect block layer graph manipulations (add/remove ++ * edges and nodes) ++ * ++ * Copyright (c) 2022 Red Hat ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++#ifndef GRAPH_LOCK_H ++#define GRAPH_LOCK_H ++ ++#include "qemu/clang-tsa.h" ++ ++/** ++ * Graph Lock API ++ * This API provides a rwlock used to protect block layer ++ * graph modifications like edge (BdrvChild) and node (BlockDriverState) ++ * addition and removal. ++ * Currently we have 1 writer only, the Main loop, and many ++ * readers, mostly coroutines running in other AioContext thus other threads. ++ * ++ * We distinguish between writer (main loop, under BQL) that modifies the ++ * graph, and readers (all other coroutines running in various AioContext), ++ * that go through the graph edges, reading ++ * BlockDriverState ->parents and->children. ++ * ++ * The writer (main loop) has an "exclusive" access, so it first waits for ++ * current read to finish, and then prevents incoming ones from ++ * entering while it has the exclusive access. ++ * ++ * The readers (coroutines in multiple AioContext) are free to ++ * access the graph as long the writer is not modifying the graph. ++ * In case it is, they go in a CoQueue and sleep until the writer ++ * is done. ++ * ++ * If a coroutine changes AioContext, the counter in the original and new ++ * AioContext are left intact, since the writer does not care where is the ++ * reader, but only if there is one. ++ * As a result, some AioContexts might have a negative reader count, to ++ * balance the positive count of the AioContext that took the lock. ++ * This also means that when an AioContext is deleted it may have a nonzero ++ * reader count. In that case we transfer the count to a global shared counter ++ * so that the writer is always aware of all readers. ++ */ ++typedef struct BdrvGraphRWlock BdrvGraphRWlock; ++ ++/* Dummy lock object to use for Thread Safety Analysis (TSA) */ ++typedef struct TSA_CAPABILITY("mutex") BdrvGraphLock { ++} BdrvGraphLock; ++ ++extern BdrvGraphLock graph_lock; ++ ++/* ++ * clang doesn't check consistency in locking annotations between forward ++ * declarations and the function definition. Having the annotation on the ++ * definition, but not the declaration in a header file, may give the reader ++ * a false sense of security because the condition actually remains unchecked ++ * for callers in other source files. ++ * ++ * Therefore, as a convention, for public functions, GRAPH_RDLOCK and ++ * GRAPH_WRLOCK annotations should be present only in the header file. ++ */ ++#define GRAPH_WRLOCK TSA_REQUIRES(graph_lock) ++#define GRAPH_RDLOCK TSA_REQUIRES_SHARED(graph_lock) ++#define GRAPH_UNLOCKED TSA_EXCLUDES(graph_lock) ++ ++/* ++ * TSA annotations are not part of function types, so checks are defeated when ++ * using a function pointer. As a workaround, annotate function pointers with ++ * this macro that will require that the lock is at least taken while reading ++ * the pointer. In most cases this is equivalent to actually protecting the ++ * function call. ++ */ ++#define GRAPH_RDLOCK_PTR TSA_GUARDED_BY(graph_lock) ++#define GRAPH_WRLOCK_PTR TSA_GUARDED_BY(graph_lock) ++#define GRAPH_UNLOCKED_PTR ++ ++/* ++ * register_aiocontext: ++ * Add AioContext @ctx to the list of AioContext. ++ * This list is used to obtain the total number of readers ++ * currently running the graph. ++ */ ++void register_aiocontext(AioContext *ctx); ++ ++/* ++ * unregister_aiocontext: ++ * Removes AioContext @ctx to the list of AioContext. ++ */ ++void unregister_aiocontext(AioContext *ctx); ++ ++/* ++ * bdrv_graph_wrlock: ++ * Start an exclusive write operation to modify the graph. This means we are ++ * adding or removing an edge or a node in the block layer graph. Nobody else ++ * is allowed to access the graph. ++ * ++ * Must only be called from outside bdrv_graph_co_rdlock. ++ * ++ * The wrlock can only be taken from the main loop, with BQL held, as only the ++ * main loop is allowed to modify the graph. ++ */ ++void no_coroutine_fn TSA_ACQUIRE(graph_lock) TSA_NO_TSA ++bdrv_graph_wrlock(void); ++ ++/* ++ * bdrv_graph_wrunlock: ++ * Write finished, reset global has_writer to 0 and restart ++ * all readers that are waiting. ++ */ ++void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA ++bdrv_graph_wrunlock(void); ++ ++/* ++ * bdrv_graph_co_rdlock: ++ * Read the bs graph. This usually means traversing all nodes in ++ * the graph, therefore it can't happen while another thread is ++ * modifying it. ++ * Increases the reader counter of the current aiocontext, ++ * and if has_writer is set, it means that the writer is modifying ++ * the graph, therefore wait in a coroutine queue. ++ * The writer will then wake this coroutine once it is done. ++ * ++ * This lock should be taken from Iothreads (IO_CODE() class of functions) ++ * because it signals the writer that there are some ++ * readers currently running, or waits until the current ++ * write is finished before continuing. ++ * Calling this function from the Main Loop with BQL held ++ * is not necessary, since the Main Loop itself is the only ++ * writer, thus won't be able to read and write at the same time. ++ * The only exception to that is when we can't take the lock in the ++ * function/coroutine itself, and need to delegate the caller (usually main ++ * loop) to take it and wait that the coroutine ends, so that ++ * we always signal that a reader is running. ++ */ ++void coroutine_fn TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA ++bdrv_graph_co_rdlock(void); ++ ++/* ++ * bdrv_graph_rdunlock: ++ * Read terminated, decrease the count of readers in the current aiocontext. ++ * If the writer is waiting for reads to finish (has_writer == 1), signal ++ * the writer that we are done via aio_wait_kick() to let it continue. ++ */ ++void coroutine_fn TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA ++bdrv_graph_co_rdunlock(void); ++ ++/* ++ * bdrv_graph_rd{un}lock_main_loop: ++ * Just a placeholder to mark where the graph rdlock should be taken ++ * in the main loop. It is just asserting that we are not ++ * in a coroutine and in GLOBAL_STATE_CODE. ++ */ ++void TSA_ACQUIRE_SHARED(graph_lock) TSA_NO_TSA ++bdrv_graph_rdlock_main_loop(void); ++ ++void TSA_RELEASE_SHARED(graph_lock) TSA_NO_TSA ++bdrv_graph_rdunlock_main_loop(void); ++ ++/* ++ * assert_bdrv_graph_readable: ++ * Make sure that the reader is either the main loop, ++ * or there is at least a reader helding the rdlock. ++ * In this way an incoming writer is aware of the read and waits. ++ */ ++void GRAPH_RDLOCK assert_bdrv_graph_readable(void); ++ ++/* ++ * assert_bdrv_graph_writable: ++ * Make sure that the writer is the main loop and has set @has_writer, ++ * so that incoming readers will pause. ++ */ ++void GRAPH_WRLOCK assert_bdrv_graph_writable(void); ++ ++/* ++ * Calling this function tells TSA that we know that the lock is effectively ++ * taken even though we cannot prove it (yet) with GRAPH_RDLOCK. This can be ++ * useful in intermediate stages of a conversion to using the GRAPH_RDLOCK ++ * macro. ++ */ ++static inline void TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA ++assume_graph_lock(void) ++{ ++} ++ ++typedef struct GraphLockable { } GraphLockable; ++ ++/* ++ * In C, compound literals have the lifetime of an automatic variable. ++ * In C++ it would be different, but then C++ wouldn't need QemuLockable ++ * either... ++ */ ++#define GML_OBJ_() (&(GraphLockable) { }) ++ ++/* ++ * This is not marked as TSA_ACQUIRE_SHARED() because TSA doesn't understand the ++ * cleanup attribute and would therefore complain that the graph is never ++ * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that ++ * we hold the lock while unlocking is left unchecked. ++ */ ++static inline GraphLockable * TSA_ACQUIRE_SHARED(graph_lock) coroutine_fn ++graph_lockable_auto_lock(GraphLockable *x) ++{ ++ bdrv_graph_co_rdlock(); ++ return x; ++} ++ ++static inline void TSA_RELEASE_SHARED(graph_lock) coroutine_fn ++graph_lockable_auto_unlock(GraphLockable **x) ++{ ++ bdrv_graph_co_rdunlock(); ++} ++ ++#define GRAPH_AUTO_UNLOCK __attribute__((cleanup(graph_lockable_auto_unlock))) ++ ++/* ++ * @var is only used to break the loop after the first iteration. ++ * @unlock_var can't be unlocked and then set to NULL because TSA wants the lock ++ * to be held at the start of every iteration of the loop. ++ */ ++#define WITH_GRAPH_RDLOCK_GUARD_(var) \ ++ for (GraphLockable *unlock_var GRAPH_AUTO_UNLOCK = \ ++ graph_lockable_auto_lock(GML_OBJ_()), \ ++ *var = unlock_var; \ ++ var; \ ++ var = NULL) ++ ++#define WITH_GRAPH_RDLOCK_GUARD() \ ++ WITH_GRAPH_RDLOCK_GUARD_(glue(graph_lockable_auto, __COUNTER__)) ++ ++#define GRAPH_RDLOCK_GUARD(x) \ ++ GraphLockable * GRAPH_AUTO_UNLOCK \ ++ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ ++ graph_lockable_auto_lock(GML_OBJ_()) ++ ++ ++typedef struct GraphLockableMainloop { } GraphLockableMainloop; ++ ++/* ++ * In C, compound literals have the lifetime of an automatic variable. ++ * In C++ it would be different, but then C++ wouldn't need QemuLockable ++ * either... ++ */ ++#define GMLML_OBJ_() (&(GraphLockableMainloop) { }) ++ ++/* ++ * This is not marked as TSA_ACQUIRE_SHARED() because TSA doesn't understand the ++ * cleanup attribute and would therefore complain that the graph is never ++ * unlocked. TSA_ASSERT_SHARED() makes sure that the following calls know that ++ * we hold the lock while unlocking is left unchecked. ++ */ ++static inline GraphLockableMainloop * TSA_ASSERT_SHARED(graph_lock) TSA_NO_TSA ++graph_lockable_auto_lock_mainloop(GraphLockableMainloop *x) ++{ ++ bdrv_graph_rdlock_main_loop(); ++ return x; ++} ++ ++static inline void TSA_NO_TSA ++graph_lockable_auto_unlock_mainloop(GraphLockableMainloop *x) ++{ ++ bdrv_graph_rdunlock_main_loop(); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GraphLockableMainloop, ++ graph_lockable_auto_unlock_mainloop) ++ ++#define GRAPH_RDLOCK_GUARD_MAINLOOP(x) \ ++ g_autoptr(GraphLockableMainloop) \ ++ glue(graph_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ ++ graph_lockable_auto_lock_mainloop(GMLML_OBJ_()) ++ ++#endif /* GRAPH_LOCK_H */ ++ +diff --git a/include/block/module_block.h b/include/block/module_block.h +new file mode 100644 +index 00000000..6b09256b +--- /dev/null ++++ b/include/block/module_block.h +@@ -0,0 +1,19 @@ ++/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ ++/* ++ * QEMU Block Module Infrastructure ++ * ++ * Authors: ++ * Marc Mari ++ */ ++ ++#ifndef QEMU_MODULE_BLOCK_H ++#define QEMU_MODULE_BLOCK_H ++ ++static const struct { ++ const char *format_name; ++ const char *protocol_name; ++ const char *library_name; ++} block_driver_modules[] = { ++}; ++ ++#endif +diff --git a/include/block/nbd.h b/include/block/nbd.h +new file mode 100644 +index 00000000..d4f8b21a +--- /dev/null ++++ b/include/block/nbd.h +@@ -0,0 +1,509 @@ ++/* ++ * Copyright Red Hat ++ * Copyright (C) 2005 Anthony Liguori ++ * ++ * Network Block Device ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; under version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#ifndef NBD_H ++#define NBD_H ++ ++#include "block/export.h" ++#include "io/channel-socket.h" ++#include "crypto/tlscreds.h" ++#include "qapi/error.h" ++#include "qemu/bswap.h" ++ ++typedef struct NBDExport NBDExport; ++typedef struct NBDClient NBDClient; ++typedef struct NBDClientConnection NBDClientConnection; ++typedef struct NBDMetaContexts NBDMetaContexts; ++ ++extern const BlockExportDriver blk_exp_nbd; ++ ++/* ++ * NBD_DEFAULT_HANDSHAKE_MAX_SECS: Number of seconds in which client must ++ * succeed at NBD_OPT_GO before being forcefully dropped as too slow. ++ */ ++#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10 ++ ++/* ++ * NBD_DEFAULT_MAX_CONNECTIONS: Number of client sockets to allow at ++ * once; must be large enough to allow a MULTI_CONN-aware client like ++ * nbdcopy to create its typical number of 8-16 sockets. ++ */ ++#define NBD_DEFAULT_MAX_CONNECTIONS 100 ++ ++/* Handshake phase structs - this struct is passed on the wire */ ++ ++typedef struct NBDOption { ++ uint64_t magic; /* NBD_OPTS_MAGIC */ ++ uint32_t option; /* NBD_OPT_* */ ++ uint32_t length; ++} QEMU_PACKED NBDOption; ++ ++typedef struct NBDOptionReply { ++ uint64_t magic; /* NBD_REP_MAGIC */ ++ uint32_t option; /* NBD_OPT_* */ ++ uint32_t type; /* NBD_REP_* */ ++ uint32_t length; ++} QEMU_PACKED NBDOptionReply; ++ ++typedef struct NBDOptionReplyMetaContext { ++ NBDOptionReply h; /* h.type = NBD_REP_META_CONTEXT, h.length > 4 */ ++ uint32_t context_id; ++ /* metadata context name follows */ ++} QEMU_PACKED NBDOptionReplyMetaContext; ++ ++/* Track results of negotiation */ ++typedef enum NBDMode { ++ /* Keep this list in a continuum of increasing features. */ ++ NBD_MODE_OLDSTYLE, /* server lacks newstyle negotiation */ ++ NBD_MODE_EXPORT_NAME, /* newstyle but only OPT_EXPORT_NAME safe */ ++ NBD_MODE_SIMPLE, /* newstyle but only simple replies */ ++ NBD_MODE_STRUCTURED, /* newstyle, structured replies enabled */ ++ NBD_MODE_EXTENDED, /* newstyle, extended headers enabled */ ++} NBDMode; ++ ++/* Transmission phase structs */ ++ ++/* ++ * Note: NBDRequest is _NOT_ the same as the network representation of an NBD ++ * request! ++ */ ++typedef struct NBDRequest { ++ uint64_t cookie; ++ uint64_t from; /* Offset touched by the command */ ++ uint64_t len; /* Effect length; 32 bit limit without extended headers */ ++ uint16_t flags; /* NBD_CMD_FLAG_* */ ++ uint16_t type; /* NBD_CMD_* */ ++ NBDMode mode; /* Determines which network representation to use */ ++ NBDMetaContexts *contexts; /* Used by NBD_CMD_BLOCK_STATUS */ ++} NBDRequest; ++ ++typedef struct NBDSimpleReply { ++ uint32_t magic; /* NBD_SIMPLE_REPLY_MAGIC */ ++ uint32_t error; ++ uint64_t cookie; ++} QEMU_PACKED NBDSimpleReply; ++ ++/* Header of all structured replies */ ++typedef struct NBDStructuredReplyChunk { ++ uint32_t magic; /* NBD_STRUCTURED_REPLY_MAGIC */ ++ uint16_t flags; /* combination of NBD_REPLY_FLAG_* */ ++ uint16_t type; /* NBD_REPLY_TYPE_* */ ++ uint64_t cookie; /* request handle */ ++ uint32_t length; /* length of payload */ ++} QEMU_PACKED NBDStructuredReplyChunk; ++ ++typedef struct NBDExtendedReplyChunk { ++ uint32_t magic; /* NBD_EXTENDED_REPLY_MAGIC */ ++ uint16_t flags; /* combination of NBD_REPLY_FLAG_* */ ++ uint16_t type; /* NBD_REPLY_TYPE_* */ ++ uint64_t cookie; /* request handle */ ++ uint64_t offset; /* request offset */ ++ uint64_t length; /* length of payload */ ++} QEMU_PACKED NBDExtendedReplyChunk; ++ ++typedef union NBDReply { ++ NBDSimpleReply simple; ++ NBDStructuredReplyChunk structured; ++ NBDExtendedReplyChunk extended; ++ struct { ++ /* ++ * @magic and @cookie fields have the same offset and size in all ++ * forms of replies, so let them be accessible without ".simple.", ++ * ".structured.", or ".extended." specifications. ++ */ ++ uint32_t magic; ++ uint32_t _skip; ++ uint64_t cookie; ++ }; ++} NBDReply; ++QEMU_BUILD_BUG_ON(offsetof(NBDReply, simple.cookie) != ++ offsetof(NBDReply, cookie)); ++QEMU_BUILD_BUG_ON(offsetof(NBDReply, structured.cookie) != ++ offsetof(NBDReply, cookie)); ++QEMU_BUILD_BUG_ON(offsetof(NBDReply, extended.cookie) != ++ offsetof(NBDReply, cookie)); ++ ++/* Header of chunk for NBD_REPLY_TYPE_OFFSET_DATA */ ++typedef struct NBDStructuredReadData { ++ /* header's .length >= 9 */ ++ uint64_t offset; ++ /* At least one byte of data payload follows, calculated from h.length */ ++} QEMU_PACKED NBDStructuredReadData; ++ ++/* Complete chunk for NBD_REPLY_TYPE_OFFSET_HOLE */ ++typedef struct NBDStructuredReadHole { ++ /* header's length == 12 */ ++ uint64_t offset; ++ uint32_t length; ++} QEMU_PACKED NBDStructuredReadHole; ++ ++/* Header of all NBD_REPLY_TYPE_ERROR* errors */ ++typedef struct NBDStructuredError { ++ /* header's length >= 6 */ ++ uint32_t error; ++ uint16_t message_length; ++} QEMU_PACKED NBDStructuredError; ++ ++/* Header of NBD_REPLY_TYPE_BLOCK_STATUS */ ++typedef struct NBDStructuredMeta { ++ /* header's length >= 12 (at least one extent) */ ++ uint32_t context_id; ++ /* NBDExtent32 extents[] follows, array length implied by header */ ++} QEMU_PACKED NBDStructuredMeta; ++ ++/* Extent array element for NBD_REPLY_TYPE_BLOCK_STATUS */ ++typedef struct NBDExtent32 { ++ uint32_t length; ++ uint32_t flags; /* NBD_STATE_* */ ++} QEMU_PACKED NBDExtent32; ++ ++/* Header of NBD_REPLY_TYPE_BLOCK_STATUS_EXT */ ++typedef struct NBDExtendedMeta { ++ /* header's length >= 24 (at least one extent) */ ++ uint32_t context_id; ++ uint32_t count; /* header length must be count * 16 + 8 */ ++ /* NBDExtent64 extents[count] follows */ ++} QEMU_PACKED NBDExtendedMeta; ++ ++/* Extent array element for NBD_REPLY_TYPE_BLOCK_STATUS_EXT */ ++typedef struct NBDExtent64 { ++ uint64_t length; ++ uint64_t flags; /* NBD_STATE_* */ ++} QEMU_PACKED NBDExtent64; ++ ++/* Client payload for limiting NBD_CMD_BLOCK_STATUS reply */ ++typedef struct NBDBlockStatusPayload { ++ uint64_t effect_length; ++ /* uint32_t ids[] follows, array length implied by header */ ++} QEMU_PACKED NBDBlockStatusPayload; ++ ++/* Transmission (export) flags: sent from server to client during handshake, ++ but describe what will happen during transmission */ ++enum { ++ NBD_FLAG_HAS_FLAGS_BIT = 0, /* Flags are there */ ++ NBD_FLAG_READ_ONLY_BIT = 1, /* Device is read-only */ ++ NBD_FLAG_SEND_FLUSH_BIT = 2, /* Send FLUSH */ ++ NBD_FLAG_SEND_FUA_BIT = 3, /* Send FUA (Force Unit Access) */ ++ NBD_FLAG_ROTATIONAL_BIT = 4, /* Use elevator algorithm - ++ rotational media */ ++ NBD_FLAG_SEND_TRIM_BIT = 5, /* Send TRIM (discard) */ ++ NBD_FLAG_SEND_WRITE_ZEROES_BIT = 6, /* Send WRITE_ZEROES */ ++ NBD_FLAG_SEND_DF_BIT = 7, /* Send DF (Do not Fragment) */ ++ NBD_FLAG_CAN_MULTI_CONN_BIT = 8, /* Multi-client cache consistent */ ++ NBD_FLAG_SEND_RESIZE_BIT = 9, /* Send resize */ ++ NBD_FLAG_SEND_CACHE_BIT = 10, /* Send CACHE (prefetch) */ ++ NBD_FLAG_SEND_FAST_ZERO_BIT = 11, /* FAST_ZERO flag for WRITE_ZEROES */ ++ NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT = 12, /* PAYLOAD flag for BLOCK_STATUS */ ++}; ++ ++#define NBD_FLAG_HAS_FLAGS (1 << NBD_FLAG_HAS_FLAGS_BIT) ++#define NBD_FLAG_READ_ONLY (1 << NBD_FLAG_READ_ONLY_BIT) ++#define NBD_FLAG_SEND_FLUSH (1 << NBD_FLAG_SEND_FLUSH_BIT) ++#define NBD_FLAG_SEND_FUA (1 << NBD_FLAG_SEND_FUA_BIT) ++#define NBD_FLAG_ROTATIONAL (1 << NBD_FLAG_ROTATIONAL_BIT) ++#define NBD_FLAG_SEND_TRIM (1 << NBD_FLAG_SEND_TRIM_BIT) ++#define NBD_FLAG_SEND_WRITE_ZEROES (1 << NBD_FLAG_SEND_WRITE_ZEROES_BIT) ++#define NBD_FLAG_SEND_DF (1 << NBD_FLAG_SEND_DF_BIT) ++#define NBD_FLAG_CAN_MULTI_CONN (1 << NBD_FLAG_CAN_MULTI_CONN_BIT) ++#define NBD_FLAG_SEND_RESIZE (1 << NBD_FLAG_SEND_RESIZE_BIT) ++#define NBD_FLAG_SEND_CACHE (1 << NBD_FLAG_SEND_CACHE_BIT) ++#define NBD_FLAG_SEND_FAST_ZERO (1 << NBD_FLAG_SEND_FAST_ZERO_BIT) ++#define NBD_FLAG_BLOCK_STAT_PAYLOAD (1 << NBD_FLAG_BLOCK_STAT_PAYLOAD_BIT) ++ ++/* New-style handshake (global) flags, sent from server to client, and ++ control what will happen during handshake phase. */ ++#define NBD_FLAG_FIXED_NEWSTYLE (1 << 0) /* Fixed newstyle protocol. */ ++#define NBD_FLAG_NO_ZEROES (1 << 1) /* End handshake without zeroes. */ ++ ++/* New-style client flags, sent from client to server to control what happens ++ during handshake phase. */ ++#define NBD_FLAG_C_FIXED_NEWSTYLE (1 << 0) /* Fixed newstyle protocol. */ ++#define NBD_FLAG_C_NO_ZEROES (1 << 1) /* End handshake without zeroes. */ ++ ++/* Option requests. */ ++#define NBD_OPT_EXPORT_NAME (1) ++#define NBD_OPT_ABORT (2) ++#define NBD_OPT_LIST (3) ++/* #define NBD_OPT_PEEK_EXPORT (4) not in use */ ++#define NBD_OPT_STARTTLS (5) ++#define NBD_OPT_INFO (6) ++#define NBD_OPT_GO (7) ++#define NBD_OPT_STRUCTURED_REPLY (8) ++#define NBD_OPT_LIST_META_CONTEXT (9) ++#define NBD_OPT_SET_META_CONTEXT (10) ++#define NBD_OPT_EXTENDED_HEADERS (11) ++ ++/* Option reply types. */ ++#define NBD_REP_ERR(value) ((UINT32_C(1) << 31) | (value)) ++ ++#define NBD_REP_ACK (1) /* Data sending finished. */ ++#define NBD_REP_SERVER (2) /* Export description. */ ++#define NBD_REP_INFO (3) /* NBD_OPT_INFO/GO. */ ++#define NBD_REP_META_CONTEXT (4) /* NBD_OPT_{LIST,SET}_META_CONTEXT */ ++ ++#define NBD_REP_ERR_UNSUP NBD_REP_ERR(1) /* Unknown option */ ++#define NBD_REP_ERR_POLICY NBD_REP_ERR(2) /* Server denied */ ++#define NBD_REP_ERR_INVALID NBD_REP_ERR(3) /* Invalid length */ ++#define NBD_REP_ERR_PLATFORM NBD_REP_ERR(4) /* Not compiled in */ ++#define NBD_REP_ERR_TLS_REQD NBD_REP_ERR(5) /* TLS required */ ++#define NBD_REP_ERR_UNKNOWN NBD_REP_ERR(6) /* Export unknown */ ++#define NBD_REP_ERR_SHUTDOWN NBD_REP_ERR(7) /* Server shutting down */ ++#define NBD_REP_ERR_BLOCK_SIZE_REQD NBD_REP_ERR(8) /* Need INFO_BLOCK_SIZE */ ++#define NBD_REP_ERR_TOO_BIG NBD_REP_ERR(9) /* Payload size overflow */ ++#define NBD_REP_ERR_EXT_HEADER_REQD NBD_REP_ERR(10) /* Need extended headers */ ++ ++/* Info types, used during NBD_REP_INFO */ ++#define NBD_INFO_EXPORT 0 ++#define NBD_INFO_NAME 1 ++#define NBD_INFO_DESCRIPTION 2 ++#define NBD_INFO_BLOCK_SIZE 3 ++ ++/* Request flags, sent from client to server during transmission phase */ ++#define NBD_CMD_FLAG_FUA (1 << 0) /* 'force unit access' during write */ ++#define NBD_CMD_FLAG_NO_HOLE (1 << 1) /* don't punch hole on zero run */ ++#define NBD_CMD_FLAG_DF (1 << 2) /* don't fragment structured read */ ++#define NBD_CMD_FLAG_REQ_ONE (1 << 3) \ ++ /* only one extent in BLOCK_STATUS reply chunk */ ++#define NBD_CMD_FLAG_FAST_ZERO (1 << 4) /* fail if WRITE_ZEROES is not fast */ ++#define NBD_CMD_FLAG_PAYLOAD_LEN (1 << 5) \ ++ /* length describes payload, not effect; only with ext header */ ++ ++/* Supported request types */ ++enum { ++ NBD_CMD_READ = 0, ++ NBD_CMD_WRITE = 1, ++ NBD_CMD_DISC = 2, ++ NBD_CMD_FLUSH = 3, ++ NBD_CMD_TRIM = 4, ++ NBD_CMD_CACHE = 5, ++ NBD_CMD_WRITE_ZEROES = 6, ++ NBD_CMD_BLOCK_STATUS = 7, ++}; ++ ++#define NBD_DEFAULT_PORT 10809 ++ ++/* Maximum size of a single READ/WRITE data buffer */ ++#define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024) ++ ++/* ++ * Maximum size of a protocol string (export name, metadata context name, ++ * etc.). Use malloc rather than stack allocation for storage of a ++ * string. ++ */ ++#define NBD_MAX_STRING_SIZE 4096 ++ ++/* Two types of request structures, a given client will only use 1 */ ++#define NBD_REQUEST_MAGIC 0x25609513 ++#define NBD_EXTENDED_REQUEST_MAGIC 0x21e41c71 ++ ++/* ++ * Three types of reply structures, but what a client expects depends ++ * on NBD_OPT_STRUCTURED_REPLY and NBD_OPT_EXTENDED_HEADERS. ++ */ ++#define NBD_SIMPLE_REPLY_MAGIC 0x67446698 ++#define NBD_STRUCTURED_REPLY_MAGIC 0x668e33ef ++#define NBD_EXTENDED_REPLY_MAGIC 0x6e8a278c ++ ++/* Chunk reply flags (for structured and extended replies) */ ++#define NBD_REPLY_FLAG_DONE (1 << 0) /* This reply-chunk is last */ ++ ++/* Chunk reply types */ ++#define NBD_REPLY_ERR(value) ((1 << 15) | (value)) ++ ++#define NBD_REPLY_TYPE_NONE 0 ++#define NBD_REPLY_TYPE_OFFSET_DATA 1 ++#define NBD_REPLY_TYPE_OFFSET_HOLE 2 ++#define NBD_REPLY_TYPE_BLOCK_STATUS 5 ++#define NBD_REPLY_TYPE_BLOCK_STATUS_EXT 6 ++#define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) ++#define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) ++ ++/* Extent flags for base:allocation in NBD_REPLY_TYPE_BLOCK_STATUS */ ++#define NBD_STATE_HOLE (1 << 0) ++#define NBD_STATE_ZERO (1 << 1) ++ ++/* Extent flags for qemu:dirty-bitmap in NBD_REPLY_TYPE_BLOCK_STATUS */ ++#define NBD_STATE_DIRTY (1 << 0) ++ ++/* No flags needed for qemu:allocation-depth in NBD_REPLY_TYPE_BLOCK_STATUS */ ++ ++static inline bool nbd_reply_type_is_error(int type) ++{ ++ return type & (1 << 15); ++} ++ ++/* NBD errors are based on errno numbers, so there is a 1:1 mapping, ++ * but only a limited set of errno values is specified in the protocol. ++ * Everything else is squashed to EINVAL. ++ */ ++#define NBD_SUCCESS 0 ++#define NBD_EPERM 1 ++#define NBD_EIO 5 ++#define NBD_ENOMEM 12 ++#define NBD_EINVAL 22 ++#define NBD_ENOSPC 28 ++#define NBD_EOVERFLOW 75 ++#define NBD_ENOTSUP 95 ++#define NBD_ESHUTDOWN 108 ++ ++/* Details collected by NBD_OPT_EXPORT_NAME and NBD_OPT_GO */ ++typedef struct NBDExportInfo { ++ /* Set by client before nbd_receive_negotiate() */ ++ bool request_sizes; ++ char *x_dirty_bitmap; ++ ++ /* Set by client before nbd_receive_negotiate(), or by server results ++ * during nbd_receive_export_list() */ ++ char *name; /* must be non-NULL */ ++ ++ /* In-out fields, set by client before nbd_receive_negotiate() and ++ * updated by server results during nbd_receive_negotiate() */ ++ NBDMode mode; /* input maximum mode tolerated; output actual mode chosen */ ++ bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ ++ ++ /* Set by server results during nbd_receive_negotiate() and ++ * nbd_receive_export_list() */ ++ uint64_t size; ++ uint16_t flags; ++ uint32_t min_block; ++ uint32_t opt_block; ++ uint32_t max_block; ++ ++ uint32_t context_id; ++ ++ /* Set by server results during nbd_receive_export_list() */ ++ char *description; ++ int n_contexts; ++ char **contexts; ++} NBDExportInfo; ++ ++int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, QIOChannel **outioc, ++ NBDExportInfo *info, Error **errp); ++void nbd_free_export_list(NBDExportInfo *info, int count); ++int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, NBDExportInfo **info, ++ Error **errp); ++int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, ++ Error **errp); ++int nbd_send_request(QIOChannel *ioc, NBDRequest *request); ++int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, ++ NBDReply *reply, NBDMode mode, ++ Error **errp); ++int nbd_client(int fd); ++int nbd_disconnect(int fd); ++int nbd_errno_to_system_errno(int err); ++ ++void nbd_export_set_on_eject_blk(BlockExport *exp, BlockBackend *blk); ++ ++AioContext *nbd_export_aio_context(NBDExport *exp); ++NBDExport *nbd_export_find(const char *name); ++ ++void nbd_client_new(QIOChannelSocket *sioc, ++ uint32_t handshake_max_secs, ++ QCryptoTLSCreds *tlscreds, ++ const char *tlsauthz, ++ void (*close_fn)(NBDClient *, bool), ++ void *owner); ++void *nbd_client_owner(NBDClient *client); ++void nbd_client_get(NBDClient *client); ++void nbd_client_put(NBDClient *client); ++ ++void nbd_server_is_qemu_nbd(int max_connections); ++bool nbd_server_is_running(void); ++int nbd_server_max_connections(void); ++void nbd_server_start(SocketAddress *addr, const char *tls_creds, ++ const char *tls_authz, uint32_t max_connections, ++ Error **errp); ++void nbd_server_start_options(NbdServerOptions *arg, Error **errp); ++ ++/* nbd_read ++ * Reads @size bytes from @ioc. Returns 0 on success. ++ */ ++static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size, ++ const char *desc, Error **errp) ++{ ++ ERRP_GUARD(); ++ int ret = qio_channel_read_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; ++ ++ if (ret < 0) { ++ if (desc) { ++ error_prepend(errp, "Failed to read %s: ", desc); ++ } ++ return ret; ++ } ++ ++ return 0; ++} ++ ++#define DEF_NBD_READ_N(bits) \ ++static inline int nbd_read##bits(QIOChannel *ioc, \ ++ uint##bits##_t *val, \ ++ const char *desc, Error **errp) \ ++{ \ ++ int ret = nbd_read(ioc, val, sizeof(*val), desc, errp); \ ++ if (ret < 0) { \ ++ return ret; \ ++ } \ ++ *val = be##bits##_to_cpu(*val); \ ++ return 0; \ ++} ++ ++DEF_NBD_READ_N(16) /* Defines nbd_read16(). */ ++DEF_NBD_READ_N(32) /* Defines nbd_read32(). */ ++DEF_NBD_READ_N(64) /* Defines nbd_read64(). */ ++ ++#undef DEF_NBD_READ_N ++ ++static inline bool nbd_reply_is_simple(NBDReply *reply) ++{ ++ return reply->magic == NBD_SIMPLE_REPLY_MAGIC; ++} ++ ++static inline bool nbd_reply_is_structured(NBDReply *reply) ++{ ++ return reply->magic == NBD_STRUCTURED_REPLY_MAGIC; ++} ++ ++const char *nbd_reply_type_lookup(uint16_t type); ++const char *nbd_opt_lookup(uint32_t opt); ++const char *nbd_rep_lookup(uint32_t rep); ++const char *nbd_info_lookup(uint16_t info); ++const char *nbd_cmd_lookup(uint16_t info); ++const char *nbd_err_lookup(int err); ++const char *nbd_mode_lookup(NBDMode mode); ++ ++/* nbd/client-connection.c */ ++void nbd_client_connection_enable_retry(NBDClientConnection *conn); ++ ++NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, ++ bool do_negotiation, ++ const char *export_name, ++ const char *x_dirty_bitmap, ++ QCryptoTLSCreds *tlscreds, ++ const char *tlshostname); ++void nbd_client_connection_release(NBDClientConnection *conn); ++ ++QIOChannel *coroutine_fn ++nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info, ++ bool blocking, Error **errp); ++ ++void nbd_co_establish_connection_cancel(NBDClientConnection *conn); ++ ++#endif +diff --git a/include/block/qapi.h b/include/block/qapi.h +new file mode 100644 +index 00000000..54c48de2 +--- /dev/null ++++ b/include/block/qapi.h +@@ -0,0 +1,52 @@ ++/* ++ * Block layer qmp and info dump related functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_QAPI_H ++#define BLOCK_QAPI_H ++ ++#include "block/graph-lock.h" ++#include "block/snapshot.h" ++#include "qapi/qapi-types-block-core.h" ++ ++BlockDeviceInfo * GRAPH_RDLOCK ++bdrv_block_device_info(BlockBackend *blk, BlockDriverState *bs, ++ bool flat, Error **errp); ++ ++int GRAPH_RDLOCK ++bdrv_query_snapshot_info_list(BlockDriverState *bs, ++ SnapshotInfoList **p_list, ++ Error **errp); ++void GRAPH_RDLOCK ++bdrv_query_image_info(BlockDriverState *bs, ImageInfo **p_info, bool flat, ++ bool skip_implicit_filters, Error **errp); ++void GRAPH_RDLOCK ++bdrv_query_block_graph_info(BlockDriverState *bs, BlockGraphInfo **p_info, ++ Error **errp); ++ ++void bdrv_snapshot_dump(QEMUSnapshotInfo *sn); ++void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, ++ const char *prefix, ++ int indentation); ++void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol); ++#endif +diff --git a/include/block/qdict.h b/include/block/qdict.h +new file mode 100644 +index 00000000..b4c28d96 +--- /dev/null ++++ b/include/block/qdict.h +@@ -0,0 +1,35 @@ ++/* ++ * Special QDict functions used by the block layer ++ * ++ * Copyright (c) 2013-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_QDICT_H ++#define BLOCK_QDICT_H ++ ++#include "qapi/qmp/qdict.h" ++ ++QObject *qdict_crumple(const QDict *src, Error **errp); ++void qdict_flatten(QDict *qdict); ++ ++void qdict_copy_default(QDict *dst, QDict *src, const char *key); ++void qdict_set_default_str(QDict *dst, const char *key, const char *val); ++ ++void qdict_join(QDict *dest, QDict *src, bool overwrite); ++ ++void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); ++void qdict_array_split(QDict *src, QList **dst); ++int qdict_array_entries(QDict *src, const char *subqdict); ++ ++typedef struct QDictRenames { ++ const char *from; ++ const char *to; ++} QDictRenames; ++bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); ++ ++Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, ++ Error **errp); ++#endif +diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h +new file mode 100644 +index 00000000..62670682 +--- /dev/null ++++ b/include/block/raw-aio.h +@@ -0,0 +1,94 @@ ++/* ++ * Declarations for AIO in the raw protocol ++ * ++ * Copyright IBM, Corp. 2008 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#ifndef QEMU_RAW_AIO_H ++#define QEMU_RAW_AIO_H ++ ++#include "block/aio.h" ++#include "qemu/iov.h" ++ ++/* AIO request types */ ++#define QEMU_AIO_READ 0x0001 ++#define QEMU_AIO_WRITE 0x0002 ++#define QEMU_AIO_IOCTL 0x0004 ++#define QEMU_AIO_FLUSH 0x0008 ++#define QEMU_AIO_DISCARD 0x0010 ++#define QEMU_AIO_WRITE_ZEROES 0x0020 ++#define QEMU_AIO_COPY_RANGE 0x0040 ++#define QEMU_AIO_TRUNCATE 0x0080 ++#define QEMU_AIO_ZONE_REPORT 0x0100 ++#define QEMU_AIO_ZONE_MGMT 0x0200 ++#define QEMU_AIO_ZONE_APPEND 0x0400 ++#define QEMU_AIO_TYPE_MASK \ ++ (QEMU_AIO_READ | \ ++ QEMU_AIO_WRITE | \ ++ QEMU_AIO_IOCTL | \ ++ QEMU_AIO_FLUSH | \ ++ QEMU_AIO_DISCARD | \ ++ QEMU_AIO_WRITE_ZEROES | \ ++ QEMU_AIO_COPY_RANGE | \ ++ QEMU_AIO_TRUNCATE | \ ++ QEMU_AIO_ZONE_REPORT | \ ++ QEMU_AIO_ZONE_MGMT | \ ++ QEMU_AIO_ZONE_APPEND) ++ ++/* AIO flags */ ++#define QEMU_AIO_MISALIGNED 0x1000 ++#define QEMU_AIO_BLKDEV 0x2000 ++#define QEMU_AIO_NO_FALLBACK 0x4000 ++ ++ ++/* linux-aio.c - Linux native implementation */ ++#ifdef CONFIG_LINUX_AIO ++typedef struct LinuxAioState LinuxAioState; ++LinuxAioState *laio_init(Error **errp); ++void laio_cleanup(LinuxAioState *s); ++ ++/* laio_co_submit: submit I/O requests in the thread's current AioContext. */ ++int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, ++ int type, uint64_t dev_max_batch); ++ ++bool laio_has_fdsync(int); ++void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context); ++void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context); ++#endif ++/* io_uring.c - Linux io_uring implementation */ ++#ifdef CONFIG_LINUX_IO_URING ++LuringState *luring_init(Error **errp); ++void luring_cleanup(LuringState *s); ++ ++/* luring_co_submit: submit I/O requests in the thread's current AioContext. */ ++int coroutine_fn luring_co_submit(BlockDriverState *bs, int fd, uint64_t offset, ++ QEMUIOVector *qiov, int type); ++void luring_detach_aio_context(LuringState *s, AioContext *old_context); ++void luring_attach_aio_context(LuringState *s, AioContext *new_context); ++#endif ++ ++#ifdef _WIN32 ++typedef struct QEMUWin32AIOState QEMUWin32AIOState; ++QEMUWin32AIOState *win32_aio_init(void); ++void win32_aio_cleanup(QEMUWin32AIOState *aio); ++int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile); ++BlockAIOCB *win32_aio_submit(BlockDriverState *bs, ++ QEMUWin32AIOState *aio, HANDLE hfile, ++ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, ++ BlockCompletionFunc *cb, void *opaque, int type); ++void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, ++ AioContext *old_context); ++void win32_aio_attach_aio_context(QEMUWin32AIOState *aio, ++ AioContext *new_context); ++#endif ++ ++#endif /* QEMU_RAW_AIO_H */ +diff --git a/include/block/snapshot.h b/include/block/snapshot.h +new file mode 100644 +index 00000000..304cc6ea +--- /dev/null ++++ b/include/block/snapshot.h +@@ -0,0 +1,113 @@ ++/* ++ * Block layer snapshot related functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef SNAPSHOT_H ++#define SNAPSHOT_H ++ ++#include "block/graph-lock.h" ++#include "qapi/qapi-builtin-types.h" ++ ++#define SNAPSHOT_OPT_BASE "snapshot." ++#define SNAPSHOT_OPT_ID "snapshot.id" ++#define SNAPSHOT_OPT_NAME "snapshot.name" ++ ++extern QemuOptsList internal_snapshot_opts; ++ ++typedef struct QEMUSnapshotInfo { ++ char id_str[128]; /* unique snapshot id */ ++ /* the following fields are informative. They are not needed for ++ the consistency of the snapshot */ ++ char name[256]; /* user chosen name */ ++ uint64_t vm_state_size; /* VM state info size */ ++ uint32_t date_sec; /* UTC date of the snapshot */ ++ uint32_t date_nsec; ++ uint64_t vm_clock_nsec; /* VM clock relative to boot */ ++ uint64_t icount; /* record/replay step */ ++} QEMUSnapshotInfo; ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, ++ const char *name); ++bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, ++ const char *id, ++ const char *name, ++ QEMUSnapshotInfo *sn_info, ++ Error **errp); ++ ++int GRAPH_RDLOCK bdrv_can_snapshot(BlockDriverState *bs); ++ ++int GRAPH_RDLOCK ++bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); ++ ++int GRAPH_UNLOCKED ++bdrv_snapshot_goto(BlockDriverState *bs, const char *snapshot_id, Error **errp); ++ ++int GRAPH_RDLOCK ++bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, ++ const char *name, Error **errp); ++ ++int bdrv_snapshot_list(BlockDriverState *bs, ++ QEMUSnapshotInfo **psn_info); ++int bdrv_snapshot_load_tmp(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp); ++int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, ++ const char *id_or_name, ++ Error **errp); ++ ++ ++/* ++ * Group operations. All block drivers are involved. ++ */ ++ ++bool bdrv_all_can_snapshot(bool has_devices, strList *devices, ++ Error **errp); ++int bdrv_all_delete_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp); ++int bdrv_all_goto_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp); ++int bdrv_all_has_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp); ++int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, ++ BlockDriverState *vm_state_bs, ++ uint64_t vm_state_size, ++ bool has_devices, ++ strList *devices, ++ Error **errp); ++ ++BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, ++ bool has_devices, strList *devices, ++ Error **errp); ++ ++#endif +diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h +new file mode 100644 +index 00000000..948ff5f3 +--- /dev/null ++++ b/include/block/thread-pool.h +@@ -0,0 +1,43 @@ ++/* ++ * QEMU block layer thread pool ++ * ++ * Copyright IBM, Corp. 2008 ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Authors: ++ * Anthony Liguori ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#ifndef QEMU_THREAD_POOL_H ++#define QEMU_THREAD_POOL_H ++ ++#include "block/aio.h" ++ ++#define THREAD_POOL_MAX_THREADS_DEFAULT 64 ++ ++typedef int ThreadPoolFunc(void *opaque); ++ ++typedef struct ThreadPool ThreadPool; ++ ++ThreadPool *thread_pool_new(struct AioContext *ctx); ++void thread_pool_free(ThreadPool *pool); ++ ++/* ++ * thread_pool_submit* API: submit I/O requests in the thread's ++ * current AioContext. ++ */ ++BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, ++ BlockCompletionFunc *cb, void *opaque); ++int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg); ++void thread_pool_submit(ThreadPoolFunc *func, void *arg); ++ ++void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx); ++ ++#endif +diff --git a/include/crypto/hash.h b/include/crypto/hash.h +new file mode 100644 +index 00000000..54d87aa2 +--- /dev/null ++++ b/include/crypto/hash.h +@@ -0,0 +1,192 @@ ++/* ++ * QEMU Crypto hash algorithms ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_HASH_H ++#define QCRYPTO_HASH_H ++ ++#include "qapi/qapi-types-crypto.h" ++ ++/* See also "QCryptoHashAlgorithm" defined in qapi/crypto.json */ ++ ++/** ++ * qcrypto_hash_supports: ++ * @alg: the hash algorithm ++ * ++ * Determine if @alg hash algorithm is supported by the ++ * current configured build. ++ * ++ * Returns: true if the algorithm is supported, false otherwise ++ */ ++gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg); ++ ++ ++/** ++ * qcrypto_hash_digest_len: ++ * @alg: the hash algorithm ++ * ++ * Determine the size of the hash digest in bytes ++ * ++ * Returns: the digest length in bytes ++ */ ++size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg); ++ ++/** ++ * qcrypto_hash_bytesv: ++ * @alg: the hash algorithm ++ * @iov: the array of memory regions to hash ++ * @niov: the length of @iov ++ * @result: pointer to hold output hash ++ * @resultlen: pointer to hold length of @result ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory regions ++ * present in @iov. The @result pointer will be ++ * filled with raw bytes representing the computed ++ * hash, which will have length @resultlen. The ++ * memory pointer in @result must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ uint8_t **result, ++ size_t *resultlen, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_bytes: ++ * @alg: the hash algorithm ++ * @buf: the memory region to hash ++ * @len: the length of @buf ++ * @result: pointer to hold output hash ++ * @resultlen: pointer to hold length of @result ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory region ++ * @buf of length @len. The @result pointer will be ++ * filled with raw bytes representing the computed ++ * hash, which will have length @resultlen. The ++ * memory pointer in @result must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ uint8_t **result, ++ size_t *resultlen, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_digestv: ++ * @alg: the hash algorithm ++ * @iov: the array of memory regions to hash ++ * @niov: the length of @iov ++ * @digest: pointer to hold output hash ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory regions ++ * present in @iov. The @digest pointer will be ++ * filled with the printable hex digest of the computed ++ * hash, which will be terminated by '\0'. The ++ * memory pointer in @digest must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ char **digest, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_digest: ++ * @alg: the hash algorithm ++ * @buf: the memory region to hash ++ * @len: the length of @buf ++ * @digest: pointer to hold output hash ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory region ++ * @buf of length @len. The @digest pointer will be ++ * filled with the printable hex digest of the computed ++ * hash, which will be terminated by '\0'. The ++ * memory pointer in @digest must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_digest(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ char **digest, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_base64v: ++ * @alg: the hash algorithm ++ * @iov: the array of memory regions to hash ++ * @niov: the length of @iov ++ * @base64: pointer to hold output hash ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory regions ++ * present in @iov. The @base64 pointer will be ++ * filled with the base64 encoding of the computed ++ * hash, which will be terminated by '\0'. The ++ * memory pointer in @base64 must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ char **base64, ++ Error **errp); ++ ++/** ++ * qcrypto_hash_base64: ++ * @alg: the hash algorithm ++ * @buf: the memory region to hash ++ * @len: the length of @buf ++ * @base64: pointer to hold output hash ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Computes the hash across all the memory region ++ * @buf of length @len. The @base64 pointer will be ++ * filled with the base64 encoding of the computed ++ * hash, which will be terminated by '\0'. The ++ * memory pointer in @base64 must be released ++ * with a call to g_free() when no longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qcrypto_hash_base64(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ char **base64, ++ Error **errp); ++ ++#endif /* QCRYPTO_HASH_H */ +diff --git a/include/crypto/tlscreds.h b/include/crypto/tlscreds.h +new file mode 100644 +index 00000000..2a8a8570 +--- /dev/null ++++ b/include/crypto/tlscreds.h +@@ -0,0 +1,67 @@ ++/* ++ * QEMU crypto TLS credential support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDS_H ++#define QCRYPTO_TLSCREDS_H ++ ++#include "qapi/qapi-types-crypto.h" ++#include "qom/object.h" ++ ++#define TYPE_QCRYPTO_TLS_CREDS "tls-creds" ++typedef struct QCryptoTLSCreds QCryptoTLSCreds; ++typedef struct QCryptoTLSCredsClass QCryptoTLSCredsClass; ++DECLARE_OBJ_CHECKERS(QCryptoTLSCreds, QCryptoTLSCredsClass, QCRYPTO_TLS_CREDS, ++ TYPE_QCRYPTO_TLS_CREDS) ++ ++ ++#define QCRYPTO_TLS_CREDS_DH_PARAMS "dh-params.pem" ++ ++ ++typedef bool (*CryptoTLSCredsReload)(QCryptoTLSCreds *, Error **); ++/** ++ * QCryptoTLSCreds: ++ * ++ * The QCryptoTLSCreds object is an abstract base for different ++ * types of TLS handshake credentials. Most commonly the ++ * QCryptoTLSCredsX509 subclass will be used to provide x509 ++ * certificate credentials. ++ */ ++ ++struct QCryptoTLSCredsClass { ++ ObjectClass parent_class; ++ CryptoTLSCredsReload reload; ++}; ++ ++/** ++ * qcrypto_tls_creds_check_endpoint: ++ * @creds: pointer to a TLS credentials object ++ * @endpoint: type of network endpoint that will be using the credentials ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Check whether the credentials is setup according to ++ * the type of @endpoint argument. ++ * ++ * Returns true if the credentials is setup for the endpoint, false otherwise ++ */ ++bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds, ++ QCryptoTLSCredsEndpoint endpoint, ++ Error **errp); ++ ++#endif /* QCRYPTO_TLSCREDS_H */ +diff --git a/include/crypto/tlscredsanon.h b/include/crypto/tlscredsanon.h +new file mode 100644 +index 00000000..bd3023f9 +--- /dev/null ++++ b/include/crypto/tlscredsanon.h +@@ -0,0 +1,100 @@ ++/* ++ * QEMU crypto TLS anonymous credential support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDSANON_H ++#define QCRYPTO_TLSCREDSANON_H ++ ++#include "crypto/tlscreds.h" ++#include "qom/object.h" ++ ++#define TYPE_QCRYPTO_TLS_CREDS_ANON "tls-creds-anon" ++typedef struct QCryptoTLSCredsAnon QCryptoTLSCredsAnon; ++DECLARE_INSTANCE_CHECKER(QCryptoTLSCredsAnon, QCRYPTO_TLS_CREDS_ANON, ++ TYPE_QCRYPTO_TLS_CREDS_ANON) ++ ++ ++typedef struct QCryptoTLSCredsAnonClass QCryptoTLSCredsAnonClass; ++ ++/** ++ * QCryptoTLSCredsAnon: ++ * ++ * The QCryptoTLSCredsAnon object provides a representation ++ * of anonymous credentials used perform a TLS handshake. ++ * This is primarily provided for backwards compatibility and ++ * its use is discouraged as it has poor security characteristics ++ * due to lacking MITM attack protection amongst other problems. ++ * ++ * This is a user creatable object, which can be instantiated ++ * via object_new_propv(): ++ * ++ * ++ * Creating anonymous TLS credential objects in code ++ * ++ * Object *obj; ++ * Error *err = NULL; ++ * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_ANON, ++ * "tlscreds0", ++ * &err, ++ * "endpoint", "server", ++ * "dir", "/path/x509/cert/dir", ++ * "verify-peer", "yes", ++ * NULL); ++ * ++ * ++ * ++ * Or via QMP: ++ * ++ * ++ * Creating anonymous TLS credential objects via QMP ++ * ++ * { ++ * "execute": "object-add", "arguments": { ++ * "id": "tlscreds0", ++ * "qom-type": "tls-creds-anon", ++ * "props": { ++ * "endpoint": "server", ++ * "dir": "/path/to/x509/cert/dir", ++ * "verify-peer": false ++ * } ++ * } ++ * } ++ * ++ * ++ * ++ * ++ * Or via the CLI: ++ * ++ * ++ * Creating anonymous TLS credential objects via CLI ++ * ++ * qemu-system-x86_64 -object tls-creds-anon,id=tlscreds0,\ ++ * endpoint=server,verify-peer=off,\ ++ * dir=/path/to/x509/certdir/ ++ * ++ * ++ * ++ */ ++ ++struct QCryptoTLSCredsAnonClass { ++ QCryptoTLSCredsClass parent_class; ++}; ++ ++ ++#endif /* QCRYPTO_TLSCREDSANON_H */ +diff --git a/include/crypto/tlscredspsk.h b/include/crypto/tlscredspsk.h +new file mode 100644 +index 00000000..bcd07dc4 +--- /dev/null ++++ b/include/crypto/tlscredspsk.h +@@ -0,0 +1,95 @@ ++/* ++ * QEMU crypto TLS Pre-Shared Key (PSK) support ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDSPSK_H ++#define QCRYPTO_TLSCREDSPSK_H ++ ++#include "crypto/tlscreds.h" ++#include "qom/object.h" ++ ++#define TYPE_QCRYPTO_TLS_CREDS_PSK "tls-creds-psk" ++typedef struct QCryptoTLSCredsPSK QCryptoTLSCredsPSK; ++DECLARE_INSTANCE_CHECKER(QCryptoTLSCredsPSK, QCRYPTO_TLS_CREDS_PSK, ++ TYPE_QCRYPTO_TLS_CREDS_PSK) ++ ++typedef struct QCryptoTLSCredsPSKClass QCryptoTLSCredsPSKClass; ++ ++#define QCRYPTO_TLS_CREDS_PSKFILE "keys.psk" ++ ++/** ++ * QCryptoTLSCredsPSK: ++ * ++ * The QCryptoTLSCredsPSK object provides a representation ++ * of the Pre-Shared Key credential used to perform a TLS handshake. ++ * ++ * This is a user creatable object, which can be instantiated ++ * via object_new_propv(): ++ * ++ * ++ * Creating TLS-PSK credential objects in code ++ * ++ * Object *obj; ++ * Error *err = NULL; ++ * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_PSK, ++ * "tlscreds0", ++ * &err, ++ * "dir", "/path/to/dir", ++ * "endpoint", "client", ++ * NULL); ++ * ++ * ++ * ++ * Or via QMP: ++ * ++ * ++ * Creating TLS-PSK credential objects via QMP ++ * ++ * { ++ * "execute": "object-add", "arguments": { ++ * "id": "tlscreds0", ++ * "qom-type": "tls-creds-psk", ++ * "props": { ++ * "dir": "/path/to/dir", ++ * "endpoint": "client" ++ * } ++ * } ++ * } ++ * ++ * ++ * ++ * Or via the CLI: ++ * ++ * ++ * Creating TLS-PSK credential objects via CLI ++ * ++ * qemu-system-x86_64 --object tls-creds-psk,id=tlscreds0,\ ++ * endpoint=client,dir=/path/to/dir[,username=qemu] ++ * ++ * ++ * ++ * The PSK file can be created and managed using psktool. ++ */ ++ ++struct QCryptoTLSCredsPSKClass { ++ QCryptoTLSCredsClass parent_class; ++}; ++ ++ ++#endif /* QCRYPTO_TLSCREDSPSK_H */ +diff --git a/include/crypto/tlscredsx509.h b/include/crypto/tlscredsx509.h +new file mode 100644 +index 00000000..c4daba21 +--- /dev/null ++++ b/include/crypto/tlscredsx509.h +@@ -0,0 +1,104 @@ ++/* ++ * QEMU crypto TLS x509 credential support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDSX509_H ++#define QCRYPTO_TLSCREDSX509_H ++ ++#include "crypto/tlscreds.h" ++#include "qom/object.h" ++ ++#define TYPE_QCRYPTO_TLS_CREDS_X509 "tls-creds-x509" ++typedef struct QCryptoTLSCredsX509 QCryptoTLSCredsX509; ++DECLARE_INSTANCE_CHECKER(QCryptoTLSCredsX509, QCRYPTO_TLS_CREDS_X509, ++ TYPE_QCRYPTO_TLS_CREDS_X509) ++ ++typedef struct QCryptoTLSCredsX509Class QCryptoTLSCredsX509Class; ++ ++#define QCRYPTO_TLS_CREDS_X509_CA_CERT "ca-cert.pem" ++#define QCRYPTO_TLS_CREDS_X509_CA_CRL "ca-crl.pem" ++#define QCRYPTO_TLS_CREDS_X509_SERVER_KEY "server-key.pem" ++#define QCRYPTO_TLS_CREDS_X509_SERVER_CERT "server-cert.pem" ++#define QCRYPTO_TLS_CREDS_X509_CLIENT_KEY "client-key.pem" ++#define QCRYPTO_TLS_CREDS_X509_CLIENT_CERT "client-cert.pem" ++ ++ ++/** ++ * QCryptoTLSCredsX509: ++ * ++ * The QCryptoTLSCredsX509 object provides a representation ++ * of x509 credentials used to perform a TLS handshake. ++ * ++ * This is a user creatable object, which can be instantiated ++ * via object_new_propv(): ++ * ++ * ++ * Creating x509 TLS credential objects in code ++ * ++ * Object *obj; ++ * Error *err = NULL; ++ * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_X509, ++ * "tlscreds0", ++ * &err, ++ * "endpoint", "server", ++ * "dir", "/path/x509/cert/dir", ++ * "verify-peer", "yes", ++ * NULL); ++ * ++ * ++ * ++ * Or via QMP: ++ * ++ * ++ * Creating x509 TLS credential objects via QMP ++ * ++ * { ++ * "execute": "object-add", "arguments": { ++ * "id": "tlscreds0", ++ * "qom-type": "tls-creds-x509", ++ * "props": { ++ * "endpoint": "server", ++ * "dir": "/path/to/x509/cert/dir", ++ * "verify-peer": false ++ * } ++ * } ++ * } ++ * ++ * ++ * ++ * ++ * Or via the CLI: ++ * ++ * ++ * Creating x509 TLS credential objects via CLI ++ * ++ * qemu-system-x86_64 -object tls-creds-x509,id=tlscreds0,\ ++ * endpoint=server,verify-peer=off,\ ++ * dir=/path/to/x509/certdir/ ++ * ++ * ++ * ++ */ ++ ++struct QCryptoTLSCredsX509Class { ++ QCryptoTLSCredsClass parent_class; ++}; ++ ++ ++#endif /* QCRYPTO_TLSCREDSX509_H */ +diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h +new file mode 100644 +index 00000000..f694a5c3 +--- /dev/null ++++ b/include/crypto/tlssession.h +@@ -0,0 +1,356 @@ ++/* ++ * QEMU crypto TLS session support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSSESSION_H ++#define QCRYPTO_TLSSESSION_H ++ ++#include "crypto/tlscreds.h" ++ ++/** ++ * QCryptoTLSSession: ++ * ++ * The QCryptoTLSSession object encapsulates the ++ * logic to integrate with a TLS providing library such ++ * as GNUTLS, to setup and run TLS sessions. ++ * ++ * The API is designed such that it has no assumption about ++ * the type of transport it is running over. It may be a ++ * traditional TCP socket, or something else entirely. The ++ * only requirement is a full-duplex stream of some kind. ++ * ++ * ++ * Using TLS session objects ++ * ++ * static ssize_t mysock_send(const char *buf, size_t len, ++ * void *opaque) ++ * { ++ * int fd = GPOINTER_TO_INT(opaque); ++ * ++ * return write(*fd, buf, len); ++ * } ++ * ++ * static ssize_t mysock_recv(const char *buf, size_t len, ++ * void *opaque) ++ * { ++ * int fd = GPOINTER_TO_INT(opaque); ++ * ++ * return read(*fd, buf, len); ++ * } ++ * ++ * static int mysock_run_tls(int sockfd, ++ * QCryptoTLSCreds *creds, ++ * Error **errp) ++ * { ++ * QCryptoTLSSession *sess; ++ * ++ * sess = qcrypto_tls_session_new(creds, ++ * "vnc.example.com", ++ * NULL, ++ * QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, ++ * errp); ++ * if (sess == NULL) { ++ * return -1; ++ * } ++ * ++ * qcrypto_tls_session_set_callbacks(sess, ++ * mysock_send, ++ * mysock_recv ++ * GINT_TO_POINTER(fd)); ++ * ++ * while (1) { ++ * if (qcrypto_tls_session_handshake(sess, errp) < 0) { ++ * qcrypto_tls_session_free(sess); ++ * return -1; ++ * } ++ * ++ * switch(qcrypto_tls_session_get_handshake_status(sess)) { ++ * case QCRYPTO_TLS_HANDSHAKE_COMPLETE: ++ * if (qcrypto_tls_session_check_credentials(sess, errp) < )) { ++ * qcrypto_tls_session_free(sess); ++ * return -1; ++ * } ++ * goto done; ++ * case QCRYPTO_TLS_HANDSHAKE_RECVING: ++ * ...wait for GIO_IN event on fd... ++ * break; ++ * case QCRYPTO_TLS_HANDSHAKE_SENDING: ++ * ...wait for GIO_OUT event on fd... ++ * break; ++ * } ++ * } ++ * done: ++ * ++ * ....send/recv payload data on sess... ++ * ++ * qcrypto_tls_session_free(sess): ++ * } ++ * ++ * ++ */ ++ ++typedef struct QCryptoTLSSession QCryptoTLSSession; ++ ++#define QCRYPTO_TLS_SESSION_ERR_BLOCK -2 ++ ++/** ++ * qcrypto_tls_session_new: ++ * @creds: pointer to a TLS credentials object ++ * @hostname: optional hostname to validate ++ * @aclname: optional ACL to validate peer credentials against ++ * @endpoint: role of the TLS session, client or server ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a new TLS session object that will be used to ++ * negotiate a TLS session over an arbitrary data channel. ++ * The session object can operate as either the server or ++ * client, according to the value of the @endpoint argument. ++ * ++ * For clients, the @hostname parameter should hold the full ++ * unmodified hostname as requested by the user. This will ++ * be used to verify the against the hostname reported in ++ * the server's credentials (aka x509 certificate). ++ * ++ * The @aclname parameter (optionally) specifies the name ++ * of an access control list that will be used to validate ++ * the peer's credentials. For x509 credentials, the ACL ++ * will be matched against the CommonName shown in the peer's ++ * certificate. If the session is acting as a server, setting ++ * an ACL will require that the client provide a validate ++ * x509 client certificate. ++ * ++ * After creating the session object, the I/O callbacks ++ * must be set using the qcrypto_tls_session_set_callbacks() ++ * method. A TLS handshake sequence must then be completed ++ * using qcrypto_tls_session_handshake(), before payload ++ * data is permitted to be sent/received. ++ * ++ * The session object must be released by calling ++ * qcrypto_tls_session_free() when no longer required ++ * ++ * Returns: a TLS session object, or NULL on error. ++ */ ++QCryptoTLSSession *qcrypto_tls_session_new(QCryptoTLSCreds *creds, ++ const char *hostname, ++ const char *aclname, ++ QCryptoTLSCredsEndpoint endpoint, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_free: ++ * @sess: the TLS session object ++ * ++ * Release all memory associated with the TLS session ++ * object previously allocated by qcrypto_tls_session_new() ++ */ ++void qcrypto_tls_session_free(QCryptoTLSSession *sess); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) ++ ++/** ++ * qcrypto_tls_session_check_credentials: ++ * @sess: the TLS session object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Validate the peer's credentials after a successful ++ * TLS handshake. It is an error to call this before ++ * qcrypto_tls_session_get_handshake_status() returns ++ * QCRYPTO_TLS_HANDSHAKE_COMPLETE ++ * ++ * Returns 0 if the credentials validated, -1 on error ++ */ ++int qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess, ++ Error **errp); ++ ++/* ++ * These must return QCRYPTO_TLS_SESSION_ERR_BLOCK if the I/O ++ * would block, but on other errors, must fill 'errp' ++ */ ++typedef ssize_t (*QCryptoTLSSessionWriteFunc)(const char *buf, ++ size_t len, ++ void *opaque, ++ Error **errp); ++typedef ssize_t (*QCryptoTLSSessionReadFunc)(char *buf, ++ size_t len, ++ void *opaque, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_set_callbacks: ++ * @sess: the TLS session object ++ * @writeFunc: callback for sending data ++ * @readFunc: callback to receiving data ++ * @opaque: data to pass to callbacks ++ * ++ * Sets the callback functions that are to be used for sending ++ * and receiving data on the underlying data channel. Typically ++ * the callbacks to write/read to/from a TCP socket, but there ++ * is no assumption made about the type of channel used. ++ * ++ * The @writeFunc callback will be passed the encrypted ++ * data to send to the remote peer. ++ * ++ * The @readFunc callback will be passed a pointer to fill ++ * with encrypted data received from the remote peer ++ */ ++void qcrypto_tls_session_set_callbacks(QCryptoTLSSession *sess, ++ QCryptoTLSSessionWriteFunc writeFunc, ++ QCryptoTLSSessionReadFunc readFunc, ++ void *opaque); ++ ++/** ++ * qcrypto_tls_session_write: ++ * @sess: the TLS session object ++ * @buf: the plain text to send ++ * @len: the length of @buf ++ * @errp: pointer to hold returned error object ++ * ++ * Encrypt @len bytes of the data in @buf and send ++ * it to the remote peer using the callback previously ++ * registered with qcrypto_tls_session_set_callbacks() ++ * ++ * It is an error to call this before ++ * qcrypto_tls_session_get_handshake_status() returns ++ * QCRYPTO_TLS_HANDSHAKE_COMPLETE ++ * ++ * Returns: the number of bytes sent, ++ * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the write would block, ++ * or -1 on error. ++ */ ++ssize_t qcrypto_tls_session_write(QCryptoTLSSession *sess, ++ const char *buf, ++ size_t len, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_read: ++ * @sess: the TLS session object ++ * @buf: to fill with plain text received ++ * @len: the length of @buf ++ * @gracefulTermination: treat premature termination as graceful EOF ++ * @errp: pointer to hold returned error object ++ * ++ * Receive up to @len bytes of data from the remote peer ++ * using the callback previously registered with ++ * qcrypto_tls_session_set_callbacks(), decrypt it and ++ * store it in @buf. ++ * ++ * If @gracefulTermination is true, then a premature termination ++ * of the TLS session will be treated as indicating EOF, as ++ * opposed to an error. ++ * ++ * It is an error to call this before ++ * qcrypto_tls_session_get_handshake_status() returns ++ * QCRYPTO_TLS_HANDSHAKE_COMPLETE ++ * ++ * Returns: the number of bytes received, ++ * or QCRYPTO_TLS_SESSION_ERR_BLOCK if the receive would block, ++ * or -1 on error. ++ */ ++ssize_t qcrypto_tls_session_read(QCryptoTLSSession *sess, ++ char *buf, ++ size_t len, ++ bool gracefulTermination, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_check_pending: ++ * @sess: the TLS session object ++ * ++ * Check if there are unread data in the TLS buffers that have ++ * already been read from the underlying data source. ++ * ++ * Returns: the number of bytes available or zero ++ */ ++size_t qcrypto_tls_session_check_pending(QCryptoTLSSession *sess); ++ ++/** ++ * qcrypto_tls_session_handshake: ++ * @sess: the TLS session object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Start, or continue, a TLS handshake sequence. If ++ * the underlying data channel is non-blocking, then ++ * this method may return control before the handshake ++ * is complete. On non-blocking channels the ++ * qcrypto_tls_session_get_handshake_status() method ++ * should be used to determine whether the handshake ++ * has completed, or is waiting to send or receive ++ * data. In the latter cases, the caller should setup ++ * an event loop watch and call this method again ++ * once the underlying data channel is ready to read ++ * or write again ++ */ ++int qcrypto_tls_session_handshake(QCryptoTLSSession *sess, ++ Error **errp); ++ ++typedef enum { ++ QCRYPTO_TLS_HANDSHAKE_COMPLETE, ++ QCRYPTO_TLS_HANDSHAKE_SENDING, ++ QCRYPTO_TLS_HANDSHAKE_RECVING, ++} QCryptoTLSSessionHandshakeStatus; ++ ++/** ++ * qcrypto_tls_session_get_handshake_status: ++ * @sess: the TLS session object ++ * ++ * Check the status of the TLS handshake. This ++ * is used with non-blocking data channels to ++ * determine whether the handshake is waiting ++ * to send or receive further data to/from the ++ * remote peer. ++ * ++ * Once this returns QCRYPTO_TLS_HANDSHAKE_COMPLETE ++ * it is permitted to send/receive payload data on ++ * the channel ++ */ ++QCryptoTLSSessionHandshakeStatus ++qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess); ++ ++/** ++ * qcrypto_tls_session_get_key_size: ++ * @sess: the TLS session object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Check the size of the data channel encryption key ++ * ++ * Returns: the length in bytes of the encryption key ++ * or -1 on error ++ */ ++int qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, ++ Error **errp); ++ ++/** ++ * qcrypto_tls_session_get_peer_name: ++ * @sess: the TLS session object ++ * ++ * Get the identified name of the remote peer. If the ++ * TLS session was negotiated using x509 certificate ++ * credentials, this will return the CommonName from ++ * the peer's certificate. If no identified name is ++ * available it will return NULL. ++ * ++ * The returned data must be released with g_free() ++ * when no longer required. ++ * ++ * Returns: the peer's name or NULL. ++ */ ++char *qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess); ++ ++#endif /* QCRYPTO_TLSSESSION_H */ +diff --git a/include/exec/hwaddr.h b/include/exec/hwaddr.h +new file mode 100644 +index 00000000..50fbb2d9 +--- /dev/null ++++ b/include/exec/hwaddr.h +@@ -0,0 +1,26 @@ ++/* Define hwaddr if it exists. */ ++ ++#ifndef HWADDR_H ++#define HWADDR_H ++ ++ ++#define HWADDR_BITS 64 ++/* hwaddr is the type of a physical address (its size can ++ be different from 'target_ulong'). */ ++ ++typedef uint64_t hwaddr; ++#define HWADDR_MAX UINT64_MAX ++#define HWADDR_FMT_plx "%016" PRIx64 ++#define HWADDR_PRId PRId64 ++#define HWADDR_PRIi PRIi64 ++#define HWADDR_PRIo PRIo64 ++#define HWADDR_PRIu PRIu64 ++#define HWADDR_PRIx PRIx64 ++#define HWADDR_PRIX PRIX64 ++ ++typedef struct MemMapEntry { ++ hwaddr base; ++ hwaddr size; ++} MemMapEntry; ++ ++#endif +diff --git a/include/glib-compat.h b/include/glib-compat.h +new file mode 100644 +index 00000000..86be439b +--- /dev/null ++++ b/include/glib-compat.h +@@ -0,0 +1,131 @@ ++/* ++ * GLIB Compatibility Functions ++ * ++ * Copyright IBM, Corp. 2013 ++ * ++ * Authors: ++ * Anthony Liguori ++ * Michael Tokarev ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_GLIB_COMPAT_H ++#define QEMU_GLIB_COMPAT_H ++ ++/* Ask for warnings for anything that was marked deprecated in ++ * the defined version, or before. It is a candidate for rewrite. ++ */ ++#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_66 ++ ++/* Ask for warnings if code tries to use function that did not ++ * exist in the defined version. These risk breaking builds ++ */ ++#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_66 ++ ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wdeprecated-declarations" ++ ++#include ++#if defined(G_OS_UNIX) ++#include ++#include ++#include ++#endif ++ ++/* ++ * Note that because of the GLIB_VERSION_MAX_ALLOWED constant above, allowing ++ * use of functions from newer GLib via this compat header needs a little ++ * trickery to prevent warnings being emitted. ++ * ++ * Consider a function from newer glib-X.Y that we want to use ++ * ++ * int g_foo(const char *wibble) ++ * ++ * We must define a static inline function with the same signature that does ++ * what we need, but with a "_compat" suffix e.g. ++ * ++ * static inline void g_foo_compat(const char *wibble) ++ * { ++ * #if GLIB_CHECK_VERSION(X, Y, 0) ++ * g_foo(wibble) ++ * #else ++ * g_something_equivalent_in_older_glib(wibble); ++ * #endif ++ * } ++ * ++ * The #pragma at the top of this file turns off -Wdeprecated-declarations, ++ * ensuring this wrapper function impl doesn't trigger the compiler warning ++ * about using too new glib APIs. Finally we can do ++ * ++ * #define g_foo(a) g_foo_compat(a) ++ * ++ * So now the code elsewhere in QEMU, which *does* have the ++ * -Wdeprecated-declarations warning active, can call g_foo(...) as normal, ++ * without generating warnings. ++ */ ++ ++/* ++ * g_memdup2_qemu: ++ * @mem: (nullable): the memory to copy. ++ * @byte_size: the number of bytes to copy. ++ * ++ * Allocates @byte_size bytes of memory, and copies @byte_size bytes into it ++ * from @mem. If @mem is %NULL it returns %NULL. ++ * ++ * This replaces g_memdup(), which was prone to integer overflows when ++ * converting the argument from a #gsize to a #guint. ++ * ++ * This static inline version is a backport of the new public API from ++ * GLib 2.68, kept internal to GLib for backport to older stable releases. ++ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2319. ++ * ++ * Returns: (nullable): a pointer to the newly-allocated copy of the memory, ++ * or %NULL if @mem is %NULL. ++ */ ++static inline gpointer g_memdup2_qemu(gconstpointer mem, gsize byte_size) ++{ ++#if GLIB_CHECK_VERSION(2, 68, 0) ++ return g_memdup2(mem, byte_size); ++#else ++ gpointer new_mem; ++ ++ if (mem && byte_size != 0) { ++ new_mem = g_malloc(byte_size); ++ memcpy(new_mem, mem, byte_size); ++ } else { ++ new_mem = NULL; ++ } ++ ++ return new_mem; ++#endif ++} ++#define g_memdup2(m, s) g_memdup2_qemu(m, s) ++ ++static inline bool ++qemu_g_test_slow(void) ++{ ++ static int cached = -1; ++ if (cached == -1) { ++ cached = g_test_slow() || getenv("G_TEST_SLOW") != NULL; ++ } ++ return cached; ++} ++ ++#undef g_test_slow ++#undef g_test_thorough ++#undef g_test_quick ++#define g_test_slow() qemu_g_test_slow() ++#define g_test_thorough() qemu_g_test_slow() ++#define g_test_quick() (!qemu_g_test_slow()) ++ ++#pragma GCC diagnostic pop ++ ++#ifndef G_NORETURN ++#define G_NORETURN G_GNUC_NORETURN ++#endif ++ ++#endif +diff --git a/include/hw/block/block.h b/include/hw/block/block.h +new file mode 100644 +index 00000000..de3946a5 +--- /dev/null ++++ b/include/hw/block/block.h +@@ -0,0 +1,110 @@ ++/* ++ * Common code for block device models ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef HW_BLOCK_H ++#define HW_BLOCK_H ++ ++#include "exec/hwaddr.h" ++#include "qapi/qapi-types-block-core.h" ++#include "hw/qdev-properties-system.h" ++ ++/* Configuration */ ++ ++typedef struct BlockConf { ++ BlockBackend *blk; ++ OnOffAuto backend_defaults; ++ uint32_t physical_block_size; ++ uint32_t logical_block_size; ++ uint32_t min_io_size; ++ uint32_t opt_io_size; ++ int32_t bootindex; ++ uint32_t discard_granularity; ++ /* geometry, not all devices use this */ ++ uint32_t cyls, heads, secs; ++ uint32_t lcyls, lheads, lsecs; ++ OnOffAuto wce; ++ bool share_rw; ++ OnOffAuto account_invalid, account_failed; ++ BlockdevOnError rerror; ++ BlockdevOnError werror; ++} BlockConf; ++ ++static inline unsigned int get_physical_block_exp(BlockConf *conf) ++{ ++ unsigned int exp = 0, size; ++ ++ for (size = conf->physical_block_size; ++ size > conf->logical_block_size; ++ size >>= 1) { ++ exp++; ++ } ++ ++ return exp; ++} ++ ++#define DEFINE_BLOCK_PROPERTIES_BASE(_state, _conf) \ ++ DEFINE_PROP_ON_OFF_AUTO("backend_defaults", _state, \ ++ _conf.backend_defaults, ON_OFF_AUTO_AUTO), \ ++ DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ ++ _conf.logical_block_size), \ ++ DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ ++ _conf.physical_block_size), \ ++ DEFINE_PROP_SIZE32("min_io_size", _state, _conf.min_io_size, 0), \ ++ DEFINE_PROP_SIZE32("opt_io_size", _state, _conf.opt_io_size, 0), \ ++ DEFINE_PROP_SIZE32("discard_granularity", _state, \ ++ _conf.discard_granularity, -1), \ ++ DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, \ ++ ON_OFF_AUTO_AUTO), \ ++ DEFINE_PROP_BOOL("share-rw", _state, _conf.share_rw, false), \ ++ DEFINE_PROP_ON_OFF_AUTO("account-invalid", _state, \ ++ _conf.account_invalid, ON_OFF_AUTO_AUTO), \ ++ DEFINE_PROP_ON_OFF_AUTO("account-failed", _state, \ ++ _conf.account_failed, ON_OFF_AUTO_AUTO) ++ ++#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ ++ DEFINE_PROP_DRIVE("drive", _state, _conf.blk), \ ++ DEFINE_BLOCK_PROPERTIES_BASE(_state, _conf) ++ ++#define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf) \ ++ DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0), \ ++ DEFINE_PROP_UINT32("heads", _state, _conf.heads, 0), \ ++ DEFINE_PROP_UINT32("secs", _state, _conf.secs, 0), \ ++ DEFINE_PROP_UINT32("lcyls", _state, _conf.lcyls, 0), \ ++ DEFINE_PROP_UINT32("lheads", _state, _conf.lheads, 0), \ ++ DEFINE_PROP_UINT32("lsecs", _state, _conf.lsecs, 0) ++ ++#define DEFINE_BLOCK_ERROR_PROPERTIES(_state, _conf) \ ++ DEFINE_PROP_BLOCKDEV_ON_ERROR("rerror", _state, _conf.rerror, \ ++ BLOCKDEV_ON_ERROR_AUTO), \ ++ DEFINE_PROP_BLOCKDEV_ON_ERROR("werror", _state, _conf.werror, \ ++ BLOCKDEV_ON_ERROR_AUTO) ++ ++/* Backend access helpers */ ++ ++bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, ++ void *buf, hwaddr size, Error **errp); ++ ++/* Configuration helpers */ ++ ++bool blkconf_geometry(BlockConf *conf, int *trans, ++ unsigned cyls_max, unsigned heads_max, unsigned secs_max, ++ Error **errp); ++bool blkconf_blocksizes(BlockConf *conf, Error **errp); ++bool blkconf_apply_backend_options(BlockConf *conf, bool readonly, ++ bool resizable, Error **errp); ++ ++/* Hard disk geometry */ ++ ++void hd_geometry_guess(BlockBackend *blk, ++ uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, ++ int *ptrans); ++int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs); ++ ++#endif +diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h +new file mode 100644 +index 00000000..77bfcbdf +--- /dev/null ++++ b/include/hw/qdev-core.h +@@ -0,0 +1,1111 @@ ++#ifndef QDEV_CORE_H ++#define QDEV_CORE_H ++ ++#include "qemu/atomic.h" ++#include "qemu/queue.h" ++#include "qemu/bitmap.h" ++#include "qemu/rcu.h" ++#include "qemu/rcu_queue.h" ++#include "qom/object.h" ++#include "hw/hotplug.h" ++#include "hw/resettable.h" ++ ++/** ++ * DOC: The QEMU Device API ++ * ++ * All modern devices should represented as a derived QOM class of ++ * TYPE_DEVICE. The device API introduces the additional methods of ++ * @realize and @unrealize to represent additional stages in a device ++ * objects life cycle. ++ * ++ * Realization ++ * ----------- ++ * ++ * Devices are constructed in two stages: ++ * ++ * 1) object instantiation via object_initialize() and ++ * 2) device realization via the #DeviceState.realized property ++ * ++ * The former may not fail (and must not abort or exit, since it is called ++ * during device introspection already), and the latter may return error ++ * information to the caller and must be re-entrant. ++ * Trivial field initializations should go into #TypeInfo.instance_init. ++ * Operations depending on @props static properties should go into @realize. ++ * After successful realization, setting static properties will fail. ++ * ++ * As an interim step, the #DeviceState.realized property can also be ++ * set with qdev_realize(). In the future, devices will propagate this ++ * state change to their children and along busses they expose. The ++ * point in time will be deferred to machine creation, so that values ++ * set in @realize will not be introspectable beforehand. Therefore ++ * devices must not create children during @realize; they should ++ * initialize them via object_initialize() in their own ++ * #TypeInfo.instance_init and forward the realization events ++ * appropriately. ++ * ++ * Any type may override the @realize and/or @unrealize callbacks but needs ++ * to call the parent type's implementation if keeping their functionality ++ * is desired. Refer to QOM documentation for further discussion and examples. ++ * ++ * .. note:: ++ * Since TYPE_DEVICE doesn't implement @realize and @unrealize, types ++ * derived directly from it need not call their parent's @realize and ++ * @unrealize. For other types consult the documentation and ++ * implementation of the respective parent types. ++ * ++ * Hiding a device ++ * --------------- ++ * ++ * To hide a device, a DeviceListener function hide_device() needs to ++ * be registered. It can be used to defer adding a device and ++ * therefore hide it from the guest. The handler registering to this ++ * DeviceListener can save the QOpts passed to it for re-using it ++ * later. It must return if it wants the device to be hidden or ++ * visible. When the handler function decides the device shall be ++ * visible it will be added with qdev_device_add() and realized as any ++ * other device. Otherwise qdev_device_add() will return early without ++ * adding the device. The guest will not see a "hidden" device until ++ * it was marked visible and qdev_device_add called again. ++ * ++ */ ++ ++enum { ++ DEV_NVECTORS_UNSPECIFIED = -1, ++}; ++ ++#define TYPE_DEVICE "device" ++OBJECT_DECLARE_TYPE(DeviceState, DeviceClass, DEVICE) ++ ++typedef enum DeviceCategory { ++ DEVICE_CATEGORY_BRIDGE, ++ DEVICE_CATEGORY_USB, ++ DEVICE_CATEGORY_STORAGE, ++ DEVICE_CATEGORY_NETWORK, ++ DEVICE_CATEGORY_INPUT, ++ DEVICE_CATEGORY_DISPLAY, ++ DEVICE_CATEGORY_SOUND, ++ DEVICE_CATEGORY_MISC, ++ DEVICE_CATEGORY_CPU, ++ DEVICE_CATEGORY_WATCHDOG, ++ DEVICE_CATEGORY_MAX ++} DeviceCategory; ++ ++typedef void (*DeviceRealize)(DeviceState *dev, Error **errp); ++typedef void (*DeviceUnrealize)(DeviceState *dev); ++typedef void (*DeviceReset)(DeviceState *dev); ++typedef void (*BusRealize)(BusState *bus, Error **errp); ++typedef void (*BusUnrealize)(BusState *bus); ++ ++/** ++ * struct DeviceClass - The base class for all devices. ++ * @props: Properties accessing state fields. ++ * @realize: Callback function invoked when the #DeviceState:realized ++ * property is changed to %true. ++ * @unrealize: Callback function invoked when the #DeviceState:realized ++ * property is changed to %false. ++ * @hotpluggable: indicates if #DeviceClass is hotpluggable, available ++ * as readonly "hotpluggable" property of #DeviceState instance ++ * ++ */ ++struct DeviceClass { ++ /* private: */ ++ ObjectClass parent_class; ++ ++ /* public: */ ++ ++ /** ++ * @categories: device categories device belongs to ++ */ ++ DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX); ++ /** ++ * @fw_name: name used to identify device to firmware interfaces ++ */ ++ const char *fw_name; ++ /** ++ * @desc: human readable description of device ++ */ ++ const char *desc; ++ ++ /** ++ * @props_: properties associated with device, should only be ++ * assigned by using device_class_set_props(). The underscore ++ * ensures a compile-time error if someone attempts to assign ++ * dc->props directly. ++ */ ++ Property *props_; ++ ++ /** ++ * @user_creatable: Can user instantiate with -device / device_add? ++ * ++ * All devices should support instantiation with device_add, and ++ * this flag should not exist. But we're not there, yet. Some ++ * devices fail to instantiate with cryptic error messages. ++ * Others instantiate, but don't work. Exposing users to such ++ * behavior would be cruel; clearing this flag will protect them. ++ * It should never be cleared without a comment explaining why it ++ * is cleared. ++ * ++ * TODO remove once we're there ++ */ ++ bool user_creatable; ++ bool hotpluggable; ++ ++ /* callbacks */ ++ /** ++ * @reset: deprecated device reset method pointer ++ * ++ * Modern code should use the ResettableClass interface to ++ * implement a multi-phase reset. ++ * ++ * TODO: remove once every reset callback is unused ++ */ ++ DeviceReset reset; ++ DeviceRealize realize; ++ DeviceUnrealize unrealize; ++ ++ /** ++ * @vmsd: device state serialisation description for ++ * migration/save/restore ++ */ ++ const VMStateDescription *vmsd; ++ ++ /** ++ * @bus_type: bus type ++ * private: to qdev / bus. ++ */ ++ const char *bus_type; ++}; ++ ++typedef struct NamedGPIOList NamedGPIOList; ++ ++struct NamedGPIOList { ++ char *name; ++ qemu_irq *in; ++ int num_in; ++ int num_out; ++ QLIST_ENTRY(NamedGPIOList) node; ++}; ++ ++typedef struct Clock Clock; ++typedef struct NamedClockList NamedClockList; ++ ++struct NamedClockList { ++ char *name; ++ Clock *clock; ++ bool output; ++ bool alias; ++ QLIST_ENTRY(NamedClockList) node; ++}; ++ ++typedef struct { ++ bool engaged_in_io; ++} MemReentrancyGuard; ++ ++ ++typedef QLIST_HEAD(, NamedGPIOList) NamedGPIOListHead; ++typedef QLIST_HEAD(, NamedClockList) NamedClockListHead; ++typedef QLIST_HEAD(, BusState) BusStateHead; ++ ++/** ++ * struct DeviceState - common device state, accessed with qdev helpers ++ * ++ * This structure should not be accessed directly. We declare it here ++ * so that it can be embedded in individual device state structures. ++ */ ++struct DeviceState { ++ /* private: */ ++ Object parent_obj; ++ /* public: */ ++ ++ /** ++ * @id: global device id ++ */ ++ char *id; ++ /** ++ * @canonical_path: canonical path of realized device in the QOM tree ++ */ ++ char *canonical_path; ++ /** ++ * @realized: has device been realized? ++ */ ++ bool realized; ++ /** ++ * @pending_deleted_event: track pending deletion events during unplug ++ */ ++ bool pending_deleted_event; ++ /** ++ * @pending_deleted_expires_ms: optional timeout for deletion events ++ */ ++ int64_t pending_deleted_expires_ms; ++ /** ++ * @opts: QDict of options for the device ++ */ ++ QDict *opts; ++ /** ++ * @hotplugged: was device added after PHASE_MACHINE_READY? ++ */ ++ int hotplugged; ++ /** ++ * @allow_unplug_during_migration: can device be unplugged during migration ++ */ ++ bool allow_unplug_during_migration; ++ /** ++ * @parent_bus: bus this device belongs to ++ */ ++ BusState *parent_bus; ++ /** ++ * @gpios: QLIST of named GPIOs the device provides. ++ */ ++ NamedGPIOListHead gpios; ++ /** ++ * @clocks: QLIST of named clocks the device provides. ++ */ ++ NamedClockListHead clocks; ++ /** ++ * @child_bus: QLIST of child buses ++ */ ++ BusStateHead child_bus; ++ /** ++ * @num_child_bus: number of @child_bus entries ++ */ ++ int num_child_bus; ++ /** ++ * @instance_id_alias: device alias for handling legacy migration setups ++ */ ++ int instance_id_alias; ++ /** ++ * @alias_required_for_version: indicates @instance_id_alias is ++ * needed for migration ++ */ ++ int alias_required_for_version; ++ /** ++ * @reset: ResettableState for the device; handled by Resettable interface. ++ */ ++ ResettableState reset; ++ /** ++ * @unplug_blockers: list of reasons to block unplugging of device ++ */ ++ GSList *unplug_blockers; ++ /** ++ * @mem_reentrancy_guard: Is the device currently in mmio/pio/dma? ++ * ++ * Used to prevent re-entrancy confusing things. ++ */ ++ MemReentrancyGuard mem_reentrancy_guard; ++}; ++ ++typedef struct DeviceListener DeviceListener; ++struct DeviceListener { ++ void (*realize)(DeviceListener *listener, DeviceState *dev); ++ void (*unrealize)(DeviceListener *listener, DeviceState *dev); ++ /* ++ * This callback is called upon init of the DeviceState and ++ * informs qdev if a device should be visible or hidden. We can ++ * hide a failover device depending for example on the device ++ * opts. ++ * ++ * On errors, it returns false and errp is set. Device creation ++ * should fail in this case. ++ */ ++ bool (*hide_device)(DeviceListener *listener, const QDict *device_opts, ++ bool from_json, Error **errp); ++ QTAILQ_ENTRY(DeviceListener) link; ++}; ++ ++#define TYPE_BUS "bus" ++DECLARE_OBJ_CHECKERS(BusState, BusClass, ++ BUS, TYPE_BUS) ++ ++struct BusClass { ++ ObjectClass parent_class; ++ ++ /* FIXME first arg should be BusState */ ++ void (*print_dev)(Monitor *mon, DeviceState *dev, int indent); ++ char *(*get_dev_path)(DeviceState *dev); ++ ++ /* ++ * This callback is used to create Open Firmware device path in accordance ++ * with OF spec http://forthworks.com/standards/of1275.pdf. Individual bus ++ * bindings can be found at http://playground.sun.com/1275/bindings/. ++ */ ++ char *(*get_fw_dev_path)(DeviceState *dev); ++ ++ /* ++ * Return whether the device can be added to @bus, ++ * based on the address that was set (via device properties) ++ * before realize. If not, on return @errp contains the ++ * human-readable error message. ++ */ ++ bool (*check_address)(BusState *bus, DeviceState *dev, Error **errp); ++ ++ BusRealize realize; ++ BusUnrealize unrealize; ++ ++ /* maximum devices allowed on the bus, 0: no limit. */ ++ int max_dev; ++ /* number of automatically allocated bus ids (e.g. ide.0) */ ++ int automatic_ids; ++}; ++ ++typedef struct BusChild { ++ struct rcu_head rcu; ++ DeviceState *child; ++ int index; ++ QTAILQ_ENTRY(BusChild) sibling; ++} BusChild; ++ ++#define QDEV_HOTPLUG_HANDLER_PROPERTY "hotplug-handler" ++ ++typedef QTAILQ_HEAD(, BusChild) BusChildHead; ++typedef QLIST_ENTRY(BusState) BusStateEntry; ++ ++/** ++ * struct BusState: ++ * @obj: parent object ++ * @parent: parent Device ++ * @name: name of bus ++ * @hotplug_handler: link to a hotplug handler associated with bus. ++ * @max_index: max number of child buses ++ * @realized: is the bus itself realized? ++ * @full: is the bus full? ++ * @num_children: current number of child buses ++ */ ++struct BusState { ++ /* private: */ ++ Object obj; ++ /* public: */ ++ DeviceState *parent; ++ char *name; ++ HotplugHandler *hotplug_handler; ++ int max_index; ++ bool realized; ++ bool full; ++ int num_children; ++ ++ /** ++ * @children: an RCU protected QTAILQ, thus readers must use RCU ++ * to access it, and writers must hold the big qemu lock ++ */ ++ BusChildHead children; ++ /** ++ * @sibling: next bus ++ */ ++ BusStateEntry sibling; ++ /** ++ * @reset: ResettableState for the bus; handled by Resettable interface. ++ */ ++ ResettableState reset; ++}; ++ ++/** ++ * typedef GlobalProperty - a global property type ++ * ++ * @used: Set to true if property was used when initializing a device. ++ * @optional: If set to true, GlobalProperty will be skipped without errors ++ * if the property doesn't exist. ++ * ++ * An error is fatal for non-hotplugged devices, when the global is applied. ++ */ ++typedef struct GlobalProperty { ++ const char *driver; ++ const char *property; ++ const char *value; ++ bool used; ++ bool optional; ++} GlobalProperty; ++ ++static inline void ++compat_props_add(GPtrArray *arr, ++ GlobalProperty props[], size_t nelem) ++{ ++ int i; ++ for (i = 0; i < nelem; i++) { ++ g_ptr_array_add(arr, (void *)&props[i]); ++ } ++} ++ ++/*** Board API. This should go away once we have a machine config file. ***/ ++ ++/** ++ * qdev_new: Create a device on the heap ++ * @name: device type to create (we assert() that this type exists) ++ * ++ * This only allocates the memory and initializes the device state ++ * structure, ready for the caller to set properties if they wish. ++ * The device still needs to be realized. ++ * ++ * Return: a derived DeviceState object with a reference count of 1. ++ */ ++DeviceState *qdev_new(const char *name); ++ ++/** ++ * qdev_try_new: Try to create a device on the heap ++ * @name: device type to create ++ * ++ * This is like qdev_new(), except it returns %NULL when type @name ++ * does not exist, rather than asserting. ++ * ++ * Return: a derived DeviceState object with a reference count of 1 or ++ * NULL if type @name does not exist. ++ */ ++DeviceState *qdev_try_new(const char *name); ++ ++/** ++ * qdev_is_realized() - check if device is realized ++ * @dev: The device to check. ++ * ++ * Context: May be called outside big qemu lock. ++ * Return: true if the device has been fully constructed, false otherwise. ++ */ ++static inline bool qdev_is_realized(DeviceState *dev) ++{ ++ return qatomic_load_acquire(&dev->realized); ++} ++ ++/** ++ * qdev_realize: Realize @dev. ++ * @dev: device to realize ++ * @bus: bus to plug it into (may be NULL) ++ * @errp: pointer to error object ++ * ++ * "Realize" the device, i.e. perform the second phase of device ++ * initialization. ++ * @dev must not be plugged into a bus already. ++ * If @bus, plug @dev into @bus. This takes a reference to @dev. ++ * If @dev has no QOM parent, make one up, taking another reference. ++ * ++ * If you created @dev using qdev_new(), you probably want to use ++ * qdev_realize_and_unref() instead. ++ * ++ * Return: true on success, else false setting @errp with error ++ */ ++bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp); ++ ++/** ++ * qdev_realize_and_unref: Realize @dev and drop a reference ++ * @dev: device to realize ++ * @bus: bus to plug it into (may be NULL) ++ * @errp: pointer to error object ++ * ++ * Realize @dev and drop a reference. ++ * This is like qdev_realize(), except the caller must hold a ++ * (private) reference, which is dropped on return regardless of ++ * success or failure. Intended use:: ++ * ++ * dev = qdev_new(); ++ * [...] ++ * qdev_realize_and_unref(dev, bus, errp); ++ * ++ * Now @dev can go away without further ado. ++ * ++ * If you are embedding the device into some other QOM device and ++ * initialized it via some variant on object_initialize_child() then ++ * do not use this function, because that family of functions arrange ++ * for the only reference to the child device to be held by the parent ++ * via the child<> property, and so the reference-count-drop done here ++ * would be incorrect. For that use case you want qdev_realize(). ++ * ++ * Return: true on success, else false setting @errp with error ++ */ ++bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); ++ ++/** ++ * qdev_unrealize: Unrealize a device ++ * @dev: device to unrealize ++ * ++ * This function will "unrealize" a device, which is the first phase ++ * of correctly destroying a device that has been realized. It will: ++ * ++ * - unrealize any child buses by calling qbus_unrealize() ++ * (this will recursively unrealize any devices on those buses) ++ * - call the unrealize method of @dev ++ * ++ * The device can then be freed by causing its reference count to go ++ * to zero. ++ * ++ * Warning: most devices in QEMU do not expect to be unrealized. Only ++ * devices which are hot-unpluggable should be unrealized (as part of ++ * the unplugging process); all other devices are expected to last for ++ * the life of the simulation and should not be unrealized and freed. ++ */ ++void qdev_unrealize(DeviceState *dev); ++void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, ++ int required_for_version); ++HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); ++HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); ++bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); ++ ++/** ++ * qdev_get_hotplug_handler() - Get handler responsible for device wiring ++ * @dev: the device we want the HOTPLUG_HANDLER for. ++ * ++ * Note: in case @dev has a parent bus, it will be returned as handler unless ++ * machine handler overrides it. ++ * ++ * Return: pointer to object that implements TYPE_HOTPLUG_HANDLER interface ++ * or NULL if there aren't any. ++ */ ++HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); ++void qdev_unplug(DeviceState *dev, Error **errp); ++void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, ++ DeviceState *dev, Error **errp); ++void qdev_machine_creation_done(void); ++bool qdev_machine_modified(void); ++ ++/** ++ * qdev_add_unplug_blocker: Add an unplug blocker to a device ++ * ++ * @dev: Device to be blocked from unplug ++ * @reason: Reason for blocking ++ */ ++void qdev_add_unplug_blocker(DeviceState *dev, Error *reason); ++ ++/** ++ * qdev_del_unplug_blocker: Remove an unplug blocker from a device ++ * ++ * @dev: Device to be unblocked ++ * @reason: Pointer to the Error used with qdev_add_unplug_blocker. ++ * Used as a handle to lookup the blocker for deletion. ++ */ ++void qdev_del_unplug_blocker(DeviceState *dev, Error *reason); ++ ++/** ++ * qdev_unplug_blocked: Confirm if a device is blocked from unplug ++ * ++ * @dev: Device to be tested ++ * @errp: The reasons why the device is blocked, if any ++ * ++ * Returns: true (also setting @errp) if device is blocked from unplug, ++ * false otherwise ++ */ ++bool qdev_unplug_blocked(DeviceState *dev, Error **errp); ++ ++/** ++ * typedef GpioPolarity - Polarity of a GPIO line ++ * ++ * GPIO lines use either positive (active-high) logic, ++ * or negative (active-low) logic. ++ * ++ * In active-high logic (%GPIO_POLARITY_ACTIVE_HIGH), a pin is ++ * active when the voltage on the pin is high (relative to ground); ++ * whereas in active-low logic (%GPIO_POLARITY_ACTIVE_LOW), a pin ++ * is active when the voltage on the pin is low (or grounded). ++ */ ++typedef enum { ++ GPIO_POLARITY_ACTIVE_LOW, ++ GPIO_POLARITY_ACTIVE_HIGH ++} GpioPolarity; ++ ++/** ++ * qdev_get_gpio_in: Get one of a device's anonymous input GPIO lines ++ * @dev: Device whose GPIO we want ++ * @n: Number of the anonymous GPIO line (which must be in range) ++ * ++ * Returns the qemu_irq corresponding to an anonymous input GPIO line ++ * (which the device has set up with qdev_init_gpio_in()). The index ++ * @n of the GPIO line must be valid (i.e. be at least 0 and less than ++ * the total number of anonymous input GPIOs the device has); this ++ * function will assert() if passed an invalid index. ++ * ++ * This function is intended to be used by board code or SoC "container" ++ * device models to wire up the GPIO lines; usually the return value ++ * will be passed to qdev_connect_gpio_out() or a similar function to ++ * connect another device's output GPIO line to this input. ++ * ++ * For named input GPIO lines, use qdev_get_gpio_in_named(). ++ * ++ * Return: qemu_irq corresponding to anonymous input GPIO line ++ */ ++qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); ++ ++/** ++ * qdev_get_gpio_in_named: Get one of a device's named input GPIO lines ++ * @dev: Device whose GPIO we want ++ * @name: Name of the input GPIO array ++ * @n: Number of the GPIO line in that array (which must be in range) ++ * ++ * Returns the qemu_irq corresponding to a single input GPIO line ++ * in a named array of input GPIO lines on a device (which the device ++ * has set up with qdev_init_gpio_in_named()). ++ * The @name string must correspond to an input GPIO array which exists on ++ * the device, and the index @n of the GPIO line must be valid (i.e. ++ * be at least 0 and less than the total number of input GPIOs in that ++ * array); this function will assert() if passed an invalid name or index. ++ * ++ * For anonymous input GPIO lines, use qdev_get_gpio_in(). ++ * ++ * Return: qemu_irq corresponding to named input GPIO line ++ */ ++qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n); ++ ++/** ++ * qdev_connect_gpio_out: Connect one of a device's anonymous output GPIO lines ++ * @dev: Device whose GPIO to connect ++ * @n: Number of the anonymous output GPIO line (which must be in range) ++ * @pin: qemu_irq to connect the output line to ++ * ++ * This function connects an anonymous output GPIO line on a device ++ * up to an arbitrary qemu_irq, so that when the device asserts that ++ * output GPIO line, the qemu_irq's callback is invoked. ++ * The index @n of the GPIO line must be valid (i.e. be at least 0 and ++ * less than the total number of anonymous output GPIOs the device has ++ * created with qdev_init_gpio_out()); otherwise this function will assert(). ++ * ++ * Outbound GPIO lines can be connected to any qemu_irq, but the common ++ * case is connecting them to another device's inbound GPIO line, using ++ * the qemu_irq returned by qdev_get_gpio_in() or qdev_get_gpio_in_named(). ++ * ++ * It is not valid to try to connect one outbound GPIO to multiple ++ * qemu_irqs at once, or to connect multiple outbound GPIOs to the ++ * same qemu_irq. (Warning: there is no assertion or other guard to ++ * catch this error: the model will just not do the right thing.) ++ * Instead, for fan-out you can use the TYPE_SPLIT_IRQ device: connect ++ * a device's outbound GPIO to the splitter's input, and connect each ++ * of the splitter's outputs to a different device. For fan-in you ++ * can use the TYPE_OR_IRQ device, which is a model of a logical OR ++ * gate with multiple inputs and one output. ++ * ++ * For named output GPIO lines, use qdev_connect_gpio_out_named(). ++ */ ++void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); ++ ++/** ++ * qdev_connect_gpio_out_named: Connect one of a device's named output ++ * GPIO lines ++ * @dev: Device whose GPIO to connect ++ * @name: Name of the output GPIO array ++ * @n: Number of the output GPIO line within that array (which must be in range) ++ * @input_pin: qemu_irq to connect the output line to ++ * ++ * This function connects a single GPIO output in a named array of output ++ * GPIO lines on a device up to an arbitrary qemu_irq, so that when the ++ * device asserts that output GPIO line, the qemu_irq's callback is invoked. ++ * The @name string must correspond to an output GPIO array which exists on ++ * the device, and the index @n of the GPIO line must be valid (i.e. ++ * be at least 0 and less than the total number of output GPIOs in that ++ * array); this function will assert() if passed an invalid name or index. ++ * ++ * Outbound GPIO lines can be connected to any qemu_irq, but the common ++ * case is connecting them to another device's inbound GPIO line, using ++ * the qemu_irq returned by qdev_get_gpio_in() or qdev_get_gpio_in_named(). ++ * ++ * It is not valid to try to connect one outbound GPIO to multiple ++ * qemu_irqs at once, or to connect multiple outbound GPIOs to the ++ * same qemu_irq; see qdev_connect_gpio_out() for details. ++ * ++ * For anonymous output GPIO lines, use qdev_connect_gpio_out(). ++ */ ++void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, ++ qemu_irq input_pin); ++ ++/** ++ * qdev_get_gpio_out_connector: Get the qemu_irq connected to an output GPIO ++ * @dev: Device whose output GPIO we are interested in ++ * @name: Name of the output GPIO array ++ * @n: Number of the output GPIO line within that array ++ * ++ * Returns whatever qemu_irq is currently connected to the specified ++ * output GPIO line of @dev. This will be NULL if the output GPIO line ++ * has never been wired up to the anything. Note that the qemu_irq ++ * returned does not belong to @dev -- it will be the input GPIO or ++ * IRQ of whichever device the board code has connected up to @dev's ++ * output GPIO. ++ * ++ * You probably don't need to use this function -- it is used only ++ * by the platform-bus subsystem. ++ * ++ * Return: qemu_irq associated with GPIO or NULL if un-wired. ++ */ ++qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n); ++ ++/** ++ * qdev_intercept_gpio_out: Intercept an existing GPIO connection ++ * @dev: Device to intercept the outbound GPIO line from ++ * @icpt: New qemu_irq to connect instead ++ * @name: Name of the output GPIO array ++ * @n: Number of the GPIO line in the array ++ * ++ * .. note:: ++ * This function is provided only for use by the qtest testing framework ++ * and is not suitable for use in non-testing parts of QEMU. ++ * ++ * This function breaks an existing connection of an outbound GPIO ++ * line from @dev, and replaces it with the new qemu_irq @icpt, as if ++ * ``qdev_connect_gpio_out_named(dev, icpt, name, n)`` had been called. ++ * The previously connected qemu_irq is returned, so it can be restored ++ * by a second call to qdev_intercept_gpio_out() if desired. ++ * ++ * Return: old disconnected qemu_irq if one existed ++ */ ++qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt, ++ const char *name, int n); ++ ++BusState *qdev_get_child_bus(DeviceState *dev, const char *name); ++ ++/*** Device API. ***/ ++ ++/** ++ * qdev_init_gpio_in: create an array of anonymous input GPIO lines ++ * @dev: Device to create input GPIOs for ++ * @handler: Function to call when GPIO line value is set ++ * @n: Number of GPIO lines to create ++ * ++ * Devices should use functions in the qdev_init_gpio_in* family in ++ * their instance_init or realize methods to create any input GPIO ++ * lines they need. There is no functional difference between ++ * anonymous and named GPIO lines. Stylistically, named GPIOs are ++ * preferable (easier to understand at callsites) unless a device ++ * has exactly one uniform kind of GPIO input whose purpose is obvious. ++ * Note that input GPIO lines can serve as 'sinks' for IRQ lines. ++ * ++ * See qdev_get_gpio_in() for how code that uses such a device can get ++ * hold of an input GPIO line to manipulate it. ++ */ ++void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n); ++ ++/** ++ * qdev_init_gpio_out: create an array of anonymous output GPIO lines ++ * @dev: Device to create output GPIOs for ++ * @pins: Pointer to qemu_irq or qemu_irq array for the GPIO lines ++ * @n: Number of GPIO lines to create ++ * ++ * Devices should use functions in the qdev_init_gpio_out* family ++ * in their instance_init or realize methods to create any output ++ * GPIO lines they need. There is no functional difference between ++ * anonymous and named GPIO lines. Stylistically, named GPIOs are ++ * preferable (easier to understand at callsites) unless a device ++ * has exactly one uniform kind of GPIO output whose purpose is obvious. ++ * ++ * The @pins argument should be a pointer to either a "qemu_irq" ++ * (if @n == 1) or a "qemu_irq []" array (if @n > 1) in the device's ++ * state structure. The device implementation can then raise and ++ * lower the GPIO line by calling qemu_set_irq(). (If anything is ++ * connected to the other end of the GPIO this will cause the handler ++ * function for that input GPIO to be called.) ++ * ++ * See qdev_connect_gpio_out() for how code that uses such a device ++ * can connect to one of its output GPIO lines. ++ * ++ * There is no need to release the @pins allocated array because it ++ * will be automatically released when @dev calls its instance_finalize() ++ * handler. ++ */ ++void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n); ++ ++/** ++ * qdev_init_gpio_out_named: create an array of named output GPIO lines ++ * @dev: Device to create output GPIOs for ++ * @pins: Pointer to qemu_irq or qemu_irq array for the GPIO lines ++ * @name: Name to give this array of GPIO lines ++ * @n: Number of GPIO lines to create in this array ++ * ++ * Like qdev_init_gpio_out(), but creates an array of GPIO output lines ++ * with a name. Code using the device can then connect these GPIO lines ++ * using qdev_connect_gpio_out_named(). ++ */ ++void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, ++ const char *name, int n); ++ ++/** ++ * qdev_init_gpio_in_named_with_opaque() - create an array of input GPIO lines ++ * @dev: Device to create input GPIOs for ++ * @handler: Function to call when GPIO line value is set ++ * @opaque: Opaque data pointer to pass to @handler ++ * @name: Name of the GPIO input (must be unique for this device) ++ * @n: Number of GPIO lines in this input set ++ */ ++void qdev_init_gpio_in_named_with_opaque(DeviceState *dev, ++ qemu_irq_handler handler, ++ void *opaque, ++ const char *name, int n); ++ ++/** ++ * qdev_init_gpio_in_named() - create an array of input GPIO lines ++ * @dev: device to add array to ++ * @handler: a &typedef qemu_irq_handler function to call when GPIO is set ++ * @name: Name of the GPIO input (must be unique for this device) ++ * @n: Number of GPIO lines in this input set ++ * ++ * Like qdev_init_gpio_in_named_with_opaque(), but the opaque pointer ++ * passed to the handler is @dev (which is the most commonly desired behaviour). ++ */ ++static inline void qdev_init_gpio_in_named(DeviceState *dev, ++ qemu_irq_handler handler, ++ const char *name, int n) ++{ ++ qdev_init_gpio_in_named_with_opaque(dev, handler, dev, name, n); ++} ++ ++/** ++ * qdev_pass_gpios: create GPIO lines on container which pass through to device ++ * @dev: Device which has GPIO lines ++ * @container: Container device which needs to expose them ++ * @name: Name of GPIO array to pass through (NULL for the anonymous GPIO array) ++ * ++ * In QEMU, complicated devices like SoCs are often modelled with a ++ * "container" QOM device which itself contains other QOM devices and ++ * which wires them up appropriately. This function allows the container ++ * to create GPIO arrays on itself which simply pass through to a GPIO ++ * array of one of its internal devices. ++ * ++ * If @dev has both input and output GPIOs named @name then both will ++ * be passed through. It is not possible to pass a subset of the array ++ * with this function. ++ * ++ * To users of the container device, the GPIO array created on @container ++ * behaves exactly like any other. ++ */ ++void qdev_pass_gpios(DeviceState *dev, DeviceState *container, ++ const char *name); ++ ++BusState *qdev_get_parent_bus(const DeviceState *dev); ++ ++/*** BUS API. ***/ ++ ++DeviceState *qdev_find_recursive(BusState *bus, const char *id); ++ ++/* Returns 0 to walk children, > 0 to skip walk, < 0 to terminate walk. */ ++typedef int (qbus_walkerfn)(BusState *bus, void *opaque); ++typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque); ++ ++void qbus_init(void *bus, size_t size, const char *typename, ++ DeviceState *parent, const char *name); ++BusState *qbus_new(const char *typename, DeviceState *parent, const char *name); ++bool qbus_realize(BusState *bus, Error **errp); ++void qbus_unrealize(BusState *bus); ++ ++/* Returns > 0 if either devfn or busfn skip walk somewhere in cursion, ++ * < 0 if either devfn or busfn terminate walk somewhere in cursion, ++ * 0 otherwise. */ ++int qbus_walk_children(BusState *bus, ++ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, ++ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, ++ void *opaque); ++int qdev_walk_children(DeviceState *dev, ++ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, ++ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, ++ void *opaque); ++ ++/** ++ * device_cold_reset() - perform a recursive cold reset on a device ++ * @dev: device to reset. ++ * ++ * Reset device @dev and perform a recursive processing using the resettable ++ * interface. It triggers a RESET_TYPE_COLD. ++ */ ++void device_cold_reset(DeviceState *dev); ++ ++/** ++ * bus_cold_reset() - perform a recursive cold reset on a bus ++ * @bus: bus to reset ++ * ++ * Reset bus @bus and perform a recursive processing using the resettable ++ * interface. It triggers a RESET_TYPE_COLD. ++ */ ++void bus_cold_reset(BusState *bus); ++ ++/** ++ * device_is_in_reset() - check device reset state ++ * @dev: device to check ++ * ++ * Return: true if the device @dev is currently being reset. ++ */ ++bool device_is_in_reset(DeviceState *dev); ++ ++/** ++ * bus_is_in_reset() - check bus reset state ++ * @bus: bus to check ++ * ++ * Return: true if the bus @bus is currently being reset. ++ */ ++bool bus_is_in_reset(BusState *bus); ++ ++/* This should go away once we get rid of the NULL bus hack */ ++BusState *sysbus_get_default(void); ++ ++char *qdev_get_fw_dev_path(DeviceState *dev); ++char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev); ++ ++/** ++ * device_class_set_props(): add a set of properties to an device ++ * @dc: the parent DeviceClass all devices inherit ++ * @props: an array of properties, terminate by DEFINE_PROP_END_OF_LIST() ++ * ++ * This will add a set of properties to the object. It will fault if ++ * you attempt to add an existing property defined by a parent class. ++ * To modify an inherited property you need to use???? ++ */ ++void device_class_set_props(DeviceClass *dc, Property *props); ++ ++/** ++ * device_class_set_parent_reset() - legacy set device reset handlers ++ * @dc: device class ++ * @dev_reset: function pointer to reset handler ++ * @parent_reset: function pointer to parents reset handler ++ * ++ * Modern code should use the ResettableClass interface to ++ * implement a multi-phase reset instead. ++ * ++ * TODO: remove the function when DeviceClass's reset method ++ * is not used anymore. ++ */ ++void device_class_set_parent_reset(DeviceClass *dc, ++ DeviceReset dev_reset, ++ DeviceReset *parent_reset); ++ ++/** ++ * device_class_set_parent_realize() - set up for chaining realize fns ++ * @dc: The device class ++ * @dev_realize: the device realize function ++ * @parent_realize: somewhere to save the parents realize function ++ * ++ * This is intended to be used when the new realize function will ++ * eventually call its parent realization function during creation. ++ * This requires storing the function call somewhere (usually in the ++ * instance structure) so you can eventually call ++ * dc->parent_realize(dev, errp) ++ */ ++void device_class_set_parent_realize(DeviceClass *dc, ++ DeviceRealize dev_realize, ++ DeviceRealize *parent_realize); ++ ++ ++/** ++ * device_class_set_parent_unrealize() - set up for chaining unrealize fns ++ * @dc: The device class ++ * @dev_unrealize: the device realize function ++ * @parent_unrealize: somewhere to save the parents unrealize function ++ * ++ * This is intended to be used when the new unrealize function will ++ * eventually call its parent unrealization function during the ++ * unrealize phase. This requires storing the function call somewhere ++ * (usually in the instance structure) so you can eventually call ++ * dc->parent_unrealize(dev); ++ */ ++void device_class_set_parent_unrealize(DeviceClass *dc, ++ DeviceUnrealize dev_unrealize, ++ DeviceUnrealize *parent_unrealize); ++ ++const VMStateDescription *qdev_get_vmsd(DeviceState *dev); ++ ++const char *qdev_fw_name(DeviceState *dev); ++ ++void qdev_assert_realized_properly(void); ++Object *qdev_get_machine(void); ++ ++/** ++ * qdev_get_human_name() - Return a human-readable name for a device ++ * @dev: The device. Must be a valid and non-NULL pointer. ++ * ++ * .. note:: ++ * This function is intended for user friendly error messages. ++ * ++ * Returns: A newly allocated string containing the device id if not null, ++ * else the object canonical path. ++ * ++ * Use g_free() to free it. ++ */ ++char *qdev_get_human_name(DeviceState *dev); ++ ++/* FIXME: make this a link<> */ ++bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp); ++ ++extern bool qdev_hot_removed; ++ ++char *qdev_get_dev_path(DeviceState *dev); ++ ++void qbus_set_hotplug_handler(BusState *bus, Object *handler); ++void qbus_set_bus_hotplug_handler(BusState *bus); ++ ++static inline bool qbus_is_hotpluggable(BusState *bus) ++{ ++ HotplugHandler *plug_handler = bus->hotplug_handler; ++ bool ret = !!plug_handler; ++ ++ if (plug_handler) { ++ HotplugHandlerClass *hdc; ++ ++ hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); ++ if (hdc->is_hotpluggable_bus) { ++ ret = hdc->is_hotpluggable_bus(plug_handler, bus); ++ } ++ } ++ return ret; ++} ++ ++/** ++ * qbus_mark_full: Mark this bus as full, so no more devices can be attached ++ * @bus: Bus to mark as full ++ * ++ * By default, QEMU will allow devices to be plugged into a bus up ++ * to the bus class's device count limit. Calling this function ++ * marks a particular bus as full, so that no more devices can be ++ * plugged into it. In particular this means that the bus will not ++ * be considered as a candidate for plugging in devices created by ++ * the user on the commandline or via the monitor. ++ * If a machine has multiple buses of a given type, such as I2C, ++ * where some of those buses in the real hardware are used only for ++ * internal devices and some are exposed via expansion ports, you ++ * can use this function to mark the internal-only buses as full ++ * after you have created all their internal devices. Then user ++ * created devices will appear on the expansion-port bus where ++ * guest software expects them. ++ */ ++static inline void qbus_mark_full(BusState *bus) ++{ ++ bus->full = true; ++} ++ ++void device_listener_register(DeviceListener *listener); ++void device_listener_unregister(DeviceListener *listener); ++ ++/** ++ * qdev_should_hide_device() - check if device should be hidden ++ * ++ * @opts: options QDict ++ * @from_json: true if @opts entries are typed, false for all strings ++ * @errp: pointer to error object ++ * ++ * When a device is added via qdev_device_add() this will be called. ++ * ++ * Return: if the device should be added now or not. ++ */ ++bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp); ++ ++typedef enum MachineInitPhase { ++ /* current_machine is NULL. */ ++ PHASE_NO_MACHINE, ++ ++ /* current_machine is not NULL, but current_machine->accel is NULL. */ ++ PHASE_MACHINE_CREATED, ++ ++ /* ++ * current_machine->accel is not NULL, but the machine properties have ++ * not been validated and machine_class->init has not yet been called. ++ */ ++ PHASE_ACCEL_CREATED, ++ ++ /* ++ * Late backend objects have been created and initialized. ++ */ ++ PHASE_LATE_BACKENDS_CREATED, ++ ++ /* ++ * machine_class->init has been called, thus creating any embedded ++ * devices and validating machine properties. Devices created at ++ * this time are considered to be cold-plugged. ++ */ ++ PHASE_MACHINE_INITIALIZED, ++ ++ /* ++ * QEMU is ready to start CPUs and devices created at this time ++ * are considered to be hot-plugged. The monitor is not restricted ++ * to "preconfig" commands. ++ */ ++ PHASE_MACHINE_READY, ++} MachineInitPhase; ++ ++bool phase_check(MachineInitPhase phase); ++void phase_advance(MachineInitPhase phase); ++ ++#endif +diff --git a/include/io/channel-file.h b/include/io/channel-file.h +new file mode 100644 +index 00000000..d373a4e4 +--- /dev/null ++++ b/include/io/channel-file.h +@@ -0,0 +1,110 @@ ++/* ++ * QEMU I/O channels files driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_FILE_H ++#define QIO_CHANNEL_FILE_H ++ ++#include "io/channel.h" ++#include "qom/object.h" ++ ++#define TYPE_QIO_CHANNEL_FILE "qio-channel-file" ++OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelFile, QIO_CHANNEL_FILE) ++ ++ ++/** ++ * QIOChannelFile: ++ * ++ * The QIOChannelFile object provides a channel implementation ++ * that is able to perform I/O on block devices, character ++ * devices, FIFOs, pipes and plain files. While it is technically ++ * able to work on sockets too on the UNIX platform, this is not ++ * portable to Windows and lacks some extra sockets specific ++ * functionality. So the QIOChannelSocket object is recommended ++ * for that use case. ++ * ++ */ ++ ++struct QIOChannelFile { ++ QIOChannel parent; ++ int fd; ++}; ++ ++ ++/** ++ * qio_channel_file_new_fd: ++ * @fd: the file descriptor ++ * ++ * Create a new IO channel object for a file represented ++ * by the @fd parameter. @fd can be associated with a ++ * block device, character device, fifo, pipe, or a ++ * regular file. For sockets, the QIOChannelSocket class ++ * should be used instead, as this provides greater ++ * functionality and cross platform portability. ++ * ++ * The channel will own the passed in file descriptor ++ * and will take responsibility for closing it, so the ++ * caller must not close it. If appropriate the caller ++ * should dup() its FD before opening the channel. ++ * ++ * Returns: the new channel object ++ */ ++QIOChannelFile * ++qio_channel_file_new_fd(int fd); ++ ++/** ++ * qio_channel_file_new_dupfd: ++ * @fd: the file descriptor ++ * @errp: pointer to initialized error object ++ * ++ * Create a new IO channel object for a file represented by the @fd ++ * parameter. Like qio_channel_file_new_fd(), but the @fd is first ++ * duplicated with dup(). ++ * ++ * The channel will own the duplicated file descriptor and will take ++ * responsibility for closing it, the original FD is owned by the ++ * caller. ++ * ++ * Returns: the new channel object ++ */ ++QIOChannelFile * ++qio_channel_file_new_dupfd(int fd, Error **errp); ++ ++/** ++ * qio_channel_file_new_path: ++ * @path: the file path ++ * @flags: the open flags (O_RDONLY|O_WRONLY|O_RDWR, etc) ++ * @mode: the file creation mode if O_CREAT is set in @flags ++ * @errp: pointer to initialized error object ++ * ++ * Create a new IO channel object for a file represented ++ * by the @path parameter. @path can point to any ++ * type of file on which sequential I/O can be ++ * performed, whether it be a plain file, character ++ * device or block device. ++ * ++ * Returns: the new channel object ++ */ ++QIOChannelFile * ++qio_channel_file_new_path(const char *path, ++ int flags, ++ mode_t mode, ++ Error **errp); ++ ++#endif /* QIO_CHANNEL_FILE_H */ +diff --git a/include/io/channel-socket.h b/include/io/channel-socket.h +new file mode 100644 +index 00000000..ab15577d +--- /dev/null ++++ b/include/io/channel-socket.h +@@ -0,0 +1,265 @@ ++/* ++ * QEMU I/O channels sockets driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_SOCKET_H ++#define QIO_CHANNEL_SOCKET_H ++ ++#include "io/channel.h" ++#include "io/task.h" ++#include "qemu/sockets.h" ++#include "qom/object.h" ++ ++#define TYPE_QIO_CHANNEL_SOCKET "qio-channel-socket" ++OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelSocket, QIO_CHANNEL_SOCKET) ++ ++ ++/** ++ * QIOChannelSocket: ++ * ++ * The QIOChannelSocket class provides a channel implementation ++ * that can transport data over a UNIX socket or TCP socket. ++ * Beyond the core channel API, it also provides functionality ++ * for accepting client connections, tuning some socket ++ * parameters and getting socket address strings. ++ */ ++ ++struct QIOChannelSocket { ++ QIOChannel parent; ++ int fd; ++ struct sockaddr_storage localAddr; ++ socklen_t localAddrLen; ++ struct sockaddr_storage remoteAddr; ++ socklen_t remoteAddrLen; ++ ssize_t zero_copy_queued; ++ ssize_t zero_copy_sent; ++}; ++ ++ ++/** ++ * qio_channel_socket_new: ++ * ++ * Create a channel for performing I/O on a socket ++ * connection, that is initially closed. After ++ * creating the socket, it must be setup as a client ++ * connection or server. ++ * ++ * Returns: the socket channel object ++ */ ++QIOChannelSocket * ++qio_channel_socket_new(void); ++ ++/** ++ * qio_channel_socket_new_fd: ++ * @fd: the socket file descriptor ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a channel for performing I/O on the socket ++ * connection represented by the file descriptor @fd. ++ * ++ * Returns: the socket channel object, or NULL on error ++ */ ++QIOChannelSocket * ++qio_channel_socket_new_fd(int fd, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_socket_connect_sync: ++ * @ioc: the socket channel object ++ * @addr: the address to connect to ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Attempt to connect to the address @addr. This method ++ * will run in the foreground so the caller will not regain ++ * execution control until the connection is established or ++ * an error occurs. ++ */ ++int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ Error **errp); ++ ++/** ++ * qio_channel_socket_connect_async: ++ * @ioc: the socket channel object ++ * @addr: the address to connect to ++ * @callback: the function to invoke on completion ++ * @opaque: user data to pass to @callback ++ * @destroy: the function to free @opaque ++ * @context: the context to run the async task. If %NULL, the default ++ * context will be used. ++ * ++ * Attempt to connect to the address @addr. This method ++ * will run in the background so the caller will regain ++ * execution control immediately. The function @callback ++ * will be invoked on completion or failure. The @addr ++ * parameter will be copied, so may be freed as soon ++ * as this function returns without waiting for completion. ++ */ ++void qio_channel_socket_connect_async(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++ ++/** ++ * qio_channel_socket_listen_sync: ++ * @ioc: the socket channel object ++ * @addr: the address to listen to ++ * @num: the expected amount of connections ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Attempt to listen to the address @addr. This method ++ * will run in the foreground so the caller will not regain ++ * execution control until the connection is established or ++ * an error occurs. ++ */ ++int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ int num, ++ Error **errp); ++ ++/** ++ * qio_channel_socket_listen_async: ++ * @ioc: the socket channel object ++ * @addr: the address to listen to ++ * @num: the expected amount of connections ++ * @callback: the function to invoke on completion ++ * @opaque: user data to pass to @callback ++ * @destroy: the function to free @opaque ++ * @context: the context to run the async task. If %NULL, the default ++ * context will be used. ++ * ++ * Attempt to listen to the address @addr. This method ++ * will run in the background so the caller will regain ++ * execution control immediately. The function @callback ++ * will be invoked on completion or failure. The @addr ++ * parameter will be copied, so may be freed as soon ++ * as this function returns without waiting for completion. ++ */ ++void qio_channel_socket_listen_async(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ int num, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++ ++/** ++ * qio_channel_socket_dgram_sync: ++ * @ioc: the socket channel object ++ * @localAddr: the address to local bind address ++ * @remoteAddr: the address to remote peer address ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Attempt to initialize a datagram socket bound to ++ * @localAddr and communicating with peer @remoteAddr. ++ * This method will run in the foreground so the caller ++ * will not regain execution control until the socket ++ * is established or an error occurs. ++ */ ++int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, ++ SocketAddress *localAddr, ++ SocketAddress *remoteAddr, ++ Error **errp); ++ ++/** ++ * qio_channel_socket_dgram_async: ++ * @ioc: the socket channel object ++ * @localAddr: the address to local bind address ++ * @remoteAddr: the address to remote peer address ++ * @callback: the function to invoke on completion ++ * @opaque: user data to pass to @callback ++ * @destroy: the function to free @opaque ++ * @context: the context to run the async task. If %NULL, the default ++ * context will be used. ++ * ++ * Attempt to initialize a datagram socket bound to ++ * @localAddr and communicating with peer @remoteAddr. ++ * This method will run in the background so the caller ++ * will regain execution control immediately. The function ++ * @callback will be invoked on completion or failure. ++ * The @localAddr and @remoteAddr parameters will be copied, ++ * so may be freed as soon as this function returns without ++ * waiting for completion. ++ */ ++void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, ++ SocketAddress *localAddr, ++ SocketAddress *remoteAddr, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++ ++/** ++ * qio_channel_socket_get_local_address: ++ * @ioc: the socket channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Get the string representation of the local socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++SocketAddress * ++qio_channel_socket_get_local_address(QIOChannelSocket *ioc, ++ Error **errp); ++ ++/** ++ * qio_channel_socket_get_remote_address: ++ * @ioc: the socket channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Get the string representation of the local socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: the socket address struct, or NULL on error ++ */ ++SocketAddress * ++qio_channel_socket_get_remote_address(QIOChannelSocket *ioc, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_socket_accept: ++ * @ioc: the socket channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * If the socket represents a server, then this accepts ++ * a new client connection. The returned channel will ++ * represent the connected client socket. ++ * ++ * Returns: the new client channel, or NULL on error ++ */ ++QIOChannelSocket * ++qio_channel_socket_accept(QIOChannelSocket *ioc, ++ Error **errp); ++ ++ ++#endif /* QIO_CHANNEL_SOCKET_H */ +diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h +new file mode 100644 +index 00000000..26c67f17 +--- /dev/null ++++ b/include/io/channel-tls.h +@@ -0,0 +1,146 @@ ++/* ++ * QEMU I/O channels TLS driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_TLS_H ++#define QIO_CHANNEL_TLS_H ++ ++#include "io/channel.h" ++#include "io/task.h" ++#include "crypto/tlssession.h" ++#include "qom/object.h" ++ ++#define TYPE_QIO_CHANNEL_TLS "qio-channel-tls" ++OBJECT_DECLARE_SIMPLE_TYPE(QIOChannelTLS, QIO_CHANNEL_TLS) ++ ++ ++/** ++ * QIOChannelTLS ++ * ++ * The QIOChannelTLS class provides a channel wrapper which ++ * can transparently run the TLS encryption protocol. It is ++ * usually used over a TCP socket, but there is actually no ++ * technical restriction on which type of master channel is ++ * used as the transport. ++ * ++ * This channel object is capable of running as either a ++ * TLS server or TLS client. ++ */ ++ ++struct QIOChannelTLS { ++ QIOChannel parent; ++ QIOChannel *master; ++ QCryptoTLSSession *session; ++ QIOChannelShutdown shutdown; ++ guint hs_ioc_tag; ++}; ++ ++/** ++ * qio_channel_tls_new_server: ++ * @master: the underlying channel object ++ * @creds: the credentials to use for TLS handshake ++ * @aclname: the access control list for validating clients ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a new TLS channel that runs the server side of ++ * a TLS session. The TLS session handshake will use the ++ * credentials provided in @creds. If the @aclname parameter ++ * is non-NULL, then the client will have to provide ++ * credentials (ie a x509 client certificate) which will ++ * then be validated against the ACL. ++ * ++ * After creating the channel, it is mandatory to call ++ * the qio_channel_tls_handshake() method before attempting ++ * todo any I/O on the channel. ++ * ++ * Once the handshake has completed, all I/O should be done ++ * via the new TLS channel object and not the original ++ * master channel ++ * ++ * Returns: the new TLS channel object, or NULL ++ */ ++QIOChannelTLS * ++qio_channel_tls_new_server(QIOChannel *master, ++ QCryptoTLSCreds *creds, ++ const char *aclname, ++ Error **errp); ++ ++/** ++ * qio_channel_tls_new_client: ++ * @master: the underlying channel object ++ * @creds: the credentials to use for TLS handshake ++ * @hostname: the user specified server hostname ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a new TLS channel that runs the client side of ++ * a TLS session. The TLS session handshake will use the ++ * credentials provided in @creds. The @hostname parameter ++ * should provide the user specified hostname of the server ++ * and will be validated against the server's credentials ++ * (ie CommonName of the x509 certificate) ++ * ++ * After creating the channel, it is mandatory to call ++ * the qio_channel_tls_handshake() method before attempting ++ * todo any I/O on the channel. ++ * ++ * Once the handshake has completed, all I/O should be done ++ * via the new TLS channel object and not the original ++ * master channel ++ * ++ * Returns: the new TLS channel object, or NULL ++ */ ++QIOChannelTLS * ++qio_channel_tls_new_client(QIOChannel *master, ++ QCryptoTLSCreds *creds, ++ const char *hostname, ++ Error **errp); ++ ++/** ++ * qio_channel_tls_handshake: ++ * @ioc: the TLS channel object ++ * @func: the callback to invoke when completed ++ * @opaque: opaque data to pass to @func ++ * @destroy: optional callback to free @opaque ++ * @context: the context that TLS handshake will run with. If %NULL, ++ * the default context will be used ++ * ++ * Perform the TLS session handshake. This method ++ * will return immediately and the handshake will ++ * continue in the background, provided the main ++ * loop is running. When the handshake is complete, ++ * or fails, the @func callback will be invoked. ++ */ ++void qio_channel_tls_handshake(QIOChannelTLS *ioc, ++ QIOTaskFunc func, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++/** ++ * qio_channel_tls_get_session: ++ * @ioc: the TLS channel object ++ * ++ * Get the TLS session used by the channel. ++ * ++ * Returns: the TLS session ++ */ ++QCryptoTLSSession * ++qio_channel_tls_get_session(QIOChannelTLS *ioc); ++ ++#endif /* QIO_CHANNEL_TLS_H */ +diff --git a/include/io/channel-util.h b/include/io/channel-util.h +new file mode 100644 +index 00000000..fa18a375 +--- /dev/null ++++ b/include/io/channel-util.h +@@ -0,0 +1,75 @@ ++/* ++ * QEMU I/O channels utility APIs ++ * ++ * Copyright (c) 2016 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_UTIL_H ++#define QIO_CHANNEL_UTIL_H ++ ++#include "io/channel.h" ++ ++/* ++ * This module provides helper functions that are useful when dealing ++ * with QIOChannel objects ++ */ ++ ++ ++/** ++ * qio_channel_new_fd: ++ * @fd: the file descriptor ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Create a channel for performing I/O on the file ++ * descriptor @fd. The particular subclass of QIOChannel ++ * that is returned will depend on what underlying object ++ * the file descriptor is associated with. It may be either ++ * a QIOChannelSocket or a QIOChannelFile instance. Upon ++ * success, the returned QIOChannel instance will own ++ * the @fd file descriptor, and take responsibility for ++ * closing it when no longer required. On failure, the ++ * caller is responsible for closing @fd. ++ * ++ * Returns: the channel object, or NULL on error ++ */ ++QIOChannel *qio_channel_new_fd(int fd, ++ Error **errp); ++ ++/** ++ * qio_channel_util_set_aio_fd_handler: ++ * @read_fd: the file descriptor for the read handler ++ * @read_ctx: the AioContext for the read handler ++ * @io_read: the read handler ++ * @write_fd: the file descriptor for the write handler ++ * @write_ctx: the AioContext for the write handler ++ * @io_write: the write handler ++ * @opaque: the opaque argument to the read and write handler ++ * ++ * Set the read and write handlers when @read_ctx and @write_ctx are non-NULL, ++ * respectively. To leave a handler in its current state, pass a NULL ++ * AioContext. To clear a handler, pass a non-NULL AioContext and a NULL ++ * handler. ++ */ ++void qio_channel_util_set_aio_fd_handler(int read_fd, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ int write_fd, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque); ++ ++#endif /* QIO_CHANNEL_UTIL_H */ +diff --git a/include/io/channel-watch.h b/include/io/channel-watch.h +new file mode 100644 +index 00000000..a36aab8f +--- /dev/null ++++ b/include/io/channel-watch.h +@@ -0,0 +1,90 @@ ++/* ++ * QEMU I/O channels watch helper APIs ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_WATCH_H ++#define QIO_CHANNEL_WATCH_H ++ ++#include "io/channel.h" ++ ++/* ++ * This module provides helper functions that will be needed by ++ * the various QIOChannel implementations, for creating watches ++ * on file descriptors / sockets ++ */ ++ ++/** ++ * qio_channel_create_fd_watch: ++ * @ioc: the channel object ++ * @fd: the file descriptor ++ * @condition: the I/O condition ++ * ++ * Create a new main loop source that is able to ++ * monitor the file descriptor @fd for the ++ * I/O conditions in @condition. This is able ++ * monitor block devices, character devices, ++ * pipes but not plain files or, on Win32, sockets. ++ * ++ * Returns: the new main loop source ++ */ ++GSource *qio_channel_create_fd_watch(QIOChannel *ioc, ++ int fd, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_create_socket_watch: ++ * @ioc: the channel object ++ * @fd: the file descriptor ++ * @condition: the I/O condition ++ * ++ * Create a new main loop source that is able to ++ * monitor the file descriptor @fd for the ++ * I/O conditions in @condition. This is equivalent ++ * to qio_channel_create_fd_watch on POSIX systems ++ * but not on Windows. ++ * ++ * Returns: the new main loop source ++ */ ++GSource *qio_channel_create_socket_watch(QIOChannel *ioc, ++ int fd, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_create_fd_pair_watch: ++ * @ioc: the channel object ++ * @fdread: the file descriptor for reading ++ * @fdwrite: the file descriptor for writing ++ * @condition: the I/O condition ++ * ++ * Create a new main loop source that is able to ++ * monitor the pair of file descriptors @fdread ++ * and @fdwrite for the I/O conditions in @condition. ++ * This is intended for monitoring unidirectional ++ * file descriptors such as pipes, where a pair ++ * of descriptors is required for bidirectional ++ * I/O ++ * ++ * Returns: the new main loop source ++ */ ++GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc, ++ int fdread, ++ int fdwrite, ++ GIOCondition condition); ++ ++#endif /* QIO_CHANNEL_WATCH_H */ +diff --git a/include/io/channel.h b/include/io/channel.h +new file mode 100644 +index 00000000..bdf0bca9 +--- /dev/null ++++ b/include/io/channel.h +@@ -0,0 +1,1005 @@ ++/* ++ * QEMU I/O channels ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_CHANNEL_H ++#define QIO_CHANNEL_H ++ ++#include "qom/object.h" ++#include "qemu/coroutine-core.h" ++#include "block/aio.h" ++ ++#define TYPE_QIO_CHANNEL "qio-channel" ++OBJECT_DECLARE_TYPE(QIOChannel, QIOChannelClass, ++ QIO_CHANNEL) ++ ++ ++#define QIO_CHANNEL_ERR_BLOCK -2 ++ ++#define QIO_CHANNEL_WRITE_FLAG_ZERO_COPY 0x1 ++ ++#define QIO_CHANNEL_READ_FLAG_MSG_PEEK 0x1 ++ ++typedef enum QIOChannelFeature QIOChannelFeature; ++ ++enum QIOChannelFeature { ++ QIO_CHANNEL_FEATURE_FD_PASS, ++ QIO_CHANNEL_FEATURE_SHUTDOWN, ++ QIO_CHANNEL_FEATURE_LISTEN, ++ QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY, ++ QIO_CHANNEL_FEATURE_READ_MSG_PEEK, ++ QIO_CHANNEL_FEATURE_SEEKABLE, ++}; ++ ++ ++typedef enum QIOChannelShutdown QIOChannelShutdown; ++ ++enum QIOChannelShutdown { ++ QIO_CHANNEL_SHUTDOWN_READ = 1, ++ QIO_CHANNEL_SHUTDOWN_WRITE = 2, ++ QIO_CHANNEL_SHUTDOWN_BOTH = 3, ++}; ++ ++typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc, ++ GIOCondition condition, ++ gpointer data); ++ ++/** ++ * QIOChannel: ++ * ++ * The QIOChannel defines the core API for a generic I/O channel ++ * class hierarchy. It is inspired by GIOChannel, but has the ++ * following differences ++ * ++ * - Use QOM to properly support arbitrary subclassing ++ * - Support use of iovecs for efficient I/O with multiple blocks ++ * - None of the character set translation, binary data exclusively ++ * - Direct support for QEMU Error object reporting ++ * - File descriptor passing ++ * ++ * This base class is abstract so cannot be instantiated. There ++ * will be subclasses for dealing with sockets, files, and higher ++ * level protocols such as TLS, WebSocket, etc. ++ */ ++ ++struct QIOChannel { ++ Object parent; ++ unsigned int features; /* bitmask of QIOChannelFeatures */ ++ char *name; ++ AioContext *read_ctx; ++ Coroutine *read_coroutine; ++ AioContext *write_ctx; ++ Coroutine *write_coroutine; ++ bool follow_coroutine_ctx; ++#ifdef _WIN32 ++ HANDLE event; /* For use with GSource on Win32 */ ++#endif ++}; ++ ++/** ++ * QIOChannelClass: ++ * ++ * This class defines the contract that all subclasses ++ * must follow to provide specific channel implementations. ++ * The first five callbacks are mandatory to support, others ++ * provide additional optional features. ++ * ++ * Consult the corresponding public API docs for a description ++ * of the semantics of each callback. io_shutdown in particular ++ * must be thread-safe, terminate quickly and must not block. ++ */ ++struct QIOChannelClass { ++ ObjectClass parent; ++ ++ /* Mandatory callbacks */ ++ ssize_t (*io_writev)(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp); ++ ssize_t (*io_readv)(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp); ++ int (*io_close)(QIOChannel *ioc, ++ Error **errp); ++ GSource * (*io_create_watch)(QIOChannel *ioc, ++ GIOCondition condition); ++ int (*io_set_blocking)(QIOChannel *ioc, ++ bool enabled, ++ Error **errp); ++ ++ /* Optional callbacks */ ++ ssize_t (*io_pwritev)(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ off_t offset, ++ Error **errp); ++ ssize_t (*io_preadv)(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ off_t offset, ++ Error **errp); ++ int (*io_shutdown)(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp); ++ void (*io_set_cork)(QIOChannel *ioc, ++ bool enabled); ++ void (*io_set_delay)(QIOChannel *ioc, ++ bool enabled); ++ off_t (*io_seek)(QIOChannel *ioc, ++ off_t offset, ++ int whence, ++ Error **errp); ++ void (*io_set_aio_fd_handler)(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque); ++ int (*io_flush)(QIOChannel *ioc, ++ Error **errp); ++ int (*io_peerpid)(QIOChannel *ioc, ++ unsigned int *pid, ++ Error **errp); ++}; ++ ++/* General I/O handling functions */ ++ ++/** ++ * qio_channel_has_feature: ++ * @ioc: the channel object ++ * @feature: the feature to check support of ++ * ++ * Determine whether the channel implementation supports ++ * the optional feature named in @feature. ++ * ++ * Returns: true if supported, false otherwise. ++ */ ++bool qio_channel_has_feature(QIOChannel *ioc, ++ QIOChannelFeature feature); ++ ++/** ++ * qio_channel_set_feature: ++ * @ioc: the channel object ++ * @feature: the feature to set support for ++ * ++ * Add channel support for the feature named in @feature. ++ */ ++void qio_channel_set_feature(QIOChannel *ioc, ++ QIOChannelFeature feature); ++ ++/** ++ * qio_channel_set_name: ++ * @ioc: the channel object ++ * @name: the name of the channel ++ * ++ * Sets the name of the channel, which serves as an aid ++ * to debugging. The name is used when creating GSource ++ * watches for this channel. ++ */ ++void qio_channel_set_name(QIOChannel *ioc, ++ const char *name); ++ ++/** ++ * qio_channel_readv_full: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @fds: pointer to an array that will received file handles ++ * @nfds: pointer filled with number of elements in @fds on return ++ * @flags: read flags (QIO_CHANNEL_READ_FLAG_*) ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Read data from the IO channel, storing it in the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully populated with data ++ * before the next one is used. The @niov parameter ++ * specifies the total number of elements in @iov. ++ * ++ * It is not required for all @iov to be filled with ++ * data. If the channel is in blocking mode, at least ++ * one byte of data will be read, but no more is ++ * guaranteed. If the channel is non-blocking and no ++ * data is available, it will return QIO_CHANNEL_ERR_BLOCK ++ * ++ * If the channel has passed any file descriptors, ++ * the @fds array pointer will be allocated and ++ * the elements filled with the received file ++ * descriptors. The @nfds pointer will be updated ++ * to indicate the size of the @fds array that ++ * was allocated. It is the callers responsibility ++ * to call close() on each file descriptor and to ++ * call g_free() on the array pointer in @fds. ++ * ++ * It is an error to pass a non-NULL @fds parameter ++ * unless qio_channel_has_feature() returns a true ++ * value for the QIO_CHANNEL_FEATURE_FD_PASS constant. ++ * ++ * Returns: the number of bytes read, or -1 on error, ++ * or QIO_CHANNEL_ERR_BLOCK if no data is available ++ * and the channel is non-blocking ++ */ ++ssize_t qio_channel_readv_full(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_writev_full: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @fds: an array of file handles to send ++ * @nfds: number of file handles in @fds ++ * @flags: write flags (QIO_CHANNEL_WRITE_FLAG_*) ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Write data to the IO channel, reading it from the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully sent, before the next ++ * one is used. The @niov parameter specifies the ++ * total number of elements in @iov. ++ * ++ * It is not required for all @iov data to be fully ++ * sent. If the channel is in blocking mode, at least ++ * one byte of data will be sent, but no more is ++ * guaranteed. If the channel is non-blocking and no ++ * data can be sent, it will return QIO_CHANNEL_ERR_BLOCK ++ * ++ * If there are file descriptors to send, the @fds ++ * array should be non-NULL and provide the handles. ++ * All file descriptors will be sent if at least one ++ * byte of data was sent. ++ * ++ * It is an error to pass a non-NULL @fds parameter ++ * unless qio_channel_has_feature() returns a true ++ * value for the QIO_CHANNEL_FEATURE_FD_PASS constant. ++ * ++ * Returns: the number of bytes sent, or -1 on error, ++ * or QIO_CHANNEL_ERR_BLOCK if no data is can be sent ++ * and the channel is non-blocking ++ */ ++ssize_t qio_channel_writev_full(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp); ++ ++/** ++ * qio_channel_readv_all_eof: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Read data from the IO channel, storing it in the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully populated with data ++ * before the next one is used. The @niov parameter ++ * specifies the total number of elements in @iov. ++ * ++ * The function will wait for all requested data ++ * to be read, yielding from the current coroutine ++ * if required. ++ * ++ * If end-of-file occurs before any data is read, ++ * no error is reported; otherwise, if it occurs ++ * before all requested data has been read, an error ++ * will be reported. ++ * ++ * Returns: 1 if all bytes were read, 0 if end-of-file ++ * occurs without data, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++/** ++ * qio_channel_readv_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Read data from the IO channel, storing it in the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully populated with data ++ * before the next one is used. The @niov parameter ++ * specifies the total number of elements in @iov. ++ * ++ * The function will wait for all requested data ++ * to be read, yielding from the current coroutine ++ * if required. ++ * ++ * If end-of-file occurs before all requested data ++ * has been read, an error will be reported. ++ * ++ * Returns: 0 if all bytes were read, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_writev_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Write data to the IO channel, reading it from the ++ * memory regions referenced by @iov. Each element ++ * in the @iov will be fully sent, before the next ++ * one is used. The @niov parameter specifies the ++ * total number of elements in @iov. ++ * ++ * The function will wait for all requested data ++ * to be written, yielding from the current coroutine ++ * if required. ++ * ++ * Returns: 0 if all bytes were written, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_writev_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++/** ++ * qio_channel_readv: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Behaves as qio_channel_readv_full() but does not support ++ * receiving of file handles. ++ */ ++ssize_t qio_channel_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++/** ++ * qio_channel_writev: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Behaves as qio_channel_writev_full() but does not support ++ * sending of file handles. ++ */ ++ssize_t qio_channel_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp); ++ ++/** ++ * qio_channel_read: ++ * @ioc: the channel object ++ * @buf: the memory region to read data into ++ * @buflen: the length of @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Behaves as qio_channel_readv_full() but does not support ++ * receiving of file handles, and only supports reading into ++ * a single memory region. ++ */ ++ssize_t qio_channel_read(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_write: ++ * @ioc: the channel object ++ * @buf: the memory regions to send data from ++ * @buflen: the length of @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Behaves as qio_channel_writev_full() but does not support ++ * sending of file handles, and only supports writing from a ++ * single memory region. ++ */ ++ssize_t qio_channel_write(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_read_all_eof: ++ * @ioc: the channel object ++ * @buf: the memory region to read data into ++ * @buflen: the number of bytes to @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Reads @buflen bytes into @buf, possibly blocking or (if the ++ * channel is non-blocking) yielding from the current coroutine ++ * multiple times until the entire content is read. If end-of-file ++ * occurs immediately it is not an error, but if it occurs after ++ * data has been read it will return an error rather than a ++ * short-read. Otherwise behaves as qio_channel_read(). ++ * ++ * Returns: 1 if all bytes were read, 0 if end-of-file occurs ++ * without data, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_read_all: ++ * @ioc: the channel object ++ * @buf: the memory region to read data into ++ * @buflen: the number of bytes to @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Reads @buflen bytes into @buf, possibly blocking or (if the ++ * channel is non-blocking) yielding from the current coroutine ++ * multiple times until the entire content is read. If end-of-file ++ * occurs it will return an error rather than a short-read. Otherwise ++ * behaves as qio_channel_read(). ++ * ++ * Returns: 0 if all bytes were read, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_write_all: ++ * @ioc: the channel object ++ * @buf: the memory region to write data into ++ * @buflen: the number of bytes to @buf ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Writes @buflen bytes from @buf, possibly blocking or (if the ++ * channel is non-blocking) yielding from the current coroutine ++ * multiple times until the entire content is written. Otherwise ++ * behaves as qio_channel_write(). ++ * ++ * Returns: 0 if all bytes were written, or -1 on error ++ */ ++int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp); ++ ++/** ++ * qio_channel_set_blocking: ++ * @ioc: the channel object ++ * @enabled: the blocking flag state ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * If @enabled is true, then the channel is put into ++ * blocking mode, otherwise it will be non-blocking. ++ * ++ * In non-blocking mode, read/write operations may ++ * return QIO_CHANNEL_ERR_BLOCK if they would otherwise ++ * block on I/O ++ */ ++int qio_channel_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp); ++ ++/** ++ * qio_channel_set_follow_coroutine_ctx: ++ * @ioc: the channel object ++ * @enabled: whether or not to follow the coroutine's AioContext ++ * ++ * If @enabled is true, calls to qio_channel_yield() use the current ++ * coroutine's AioContext. Usually this is desirable. ++ * ++ * If @enabled is false, calls to qio_channel_yield() use the global iohandler ++ * AioContext. This is may be used by coroutines that run in the main loop and ++ * do not wish to respond to I/O during nested event loops. This is the ++ * default for compatibility with code that is not aware of AioContexts. ++ */ ++void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled); ++ ++/** ++ * qio_channel_close: ++ * @ioc: the channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Close the channel, flushing any pending I/O ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qio_channel_close(QIOChannel *ioc, ++ Error **errp); ++ ++/** ++ * qio_channel_pwritev ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @offset: offset in the channel where writes should begin ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Not all implementations will support this facility, so may report ++ * an error. To avoid errors, the caller may check for the feature ++ * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. ++ * ++ * Behaves as qio_channel_writev_full, apart from not supporting ++ * sending of file handles as well as beginning the write at the ++ * passed @offset ++ * ++ */ ++ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov, ++ size_t niov, off_t offset, Error **errp); ++ ++/** ++ * qio_channel_pwrite ++ * @ioc: the channel object ++ * @buf: the memory region to write data into ++ * @buflen: the number of bytes to @buf ++ * @offset: offset in the channel where writes should begin ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Not all implementations will support this facility, so may report ++ * an error. To avoid errors, the caller may check for the feature ++ * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. ++ * ++ */ ++ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen, ++ off_t offset, Error **errp); ++ ++/** ++ * qio_channel_preadv ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data into ++ * @niov: the length of the @iov array ++ * @offset: offset in the channel where writes should begin ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Not all implementations will support this facility, so may report ++ * an error. To avoid errors, the caller may check for the feature ++ * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. ++ * ++ * Behaves as qio_channel_readv_full, apart from not supporting ++ * receiving of file handles as well as beginning the read at the ++ * passed @offset ++ * ++ */ ++ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov, ++ size_t niov, off_t offset, Error **errp); ++ ++/** ++ * qio_channel_pread ++ * @ioc: the channel object ++ * @buf: the memory region to write data into ++ * @buflen: the number of bytes to @buf ++ * @offset: offset in the channel where writes should begin ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Not all implementations will support this facility, so may report ++ * an error. To avoid errors, the caller may check for the feature ++ * flag QIO_CHANNEL_FEATURE_SEEKABLE prior to calling this method. ++ * ++ */ ++ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen, ++ off_t offset, Error **errp); ++ ++/** ++ * qio_channel_shutdown: ++ * @ioc: the channel object ++ * @how: the direction to shutdown ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Shutdowns transmission and/or receiving of data ++ * without closing the underlying transport. ++ * ++ * Not all implementations will support this facility, ++ * so may report an error. To avoid errors, the ++ * caller may check for the feature flag ++ * QIO_CHANNEL_FEATURE_SHUTDOWN prior to calling ++ * this method. ++ * ++ * This function is thread-safe, terminates quickly and does not block. ++ * ++ * Returns: 0 on success, -1 on error ++ */ ++int qio_channel_shutdown(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp); ++ ++/** ++ * qio_channel_set_delay: ++ * @ioc: the channel object ++ * @enabled: the new flag state ++ * ++ * Controls whether the underlying transport is ++ * permitted to delay writes in order to merge ++ * small packets. If @enabled is true, then the ++ * writes may be delayed in order to opportunistically ++ * merge small packets into larger ones. If @enabled ++ * is false, writes are dispatched immediately with ++ * no delay. ++ * ++ * When @enabled is false, applications may wish to ++ * use the qio_channel_set_cork() method to explicitly ++ * control write merging. ++ * ++ * On channels which are backed by a socket, this ++ * API corresponds to the inverse of TCP_NODELAY flag, ++ * controlling whether the Nagle algorithm is active. ++ * ++ * This setting is merely a hint, so implementations are ++ * free to ignore this without it being considered an ++ * error. ++ */ ++void qio_channel_set_delay(QIOChannel *ioc, ++ bool enabled); ++ ++/** ++ * qio_channel_set_cork: ++ * @ioc: the channel object ++ * @enabled: the new flag state ++ * ++ * Controls whether the underlying transport is ++ * permitted to dispatch data that is written. ++ * If @enabled is true, then any data written will ++ * be queued in local buffers until @enabled is ++ * set to false once again. ++ * ++ * This feature is typically used when the automatic ++ * write coalescing facility is disabled via the ++ * qio_channel_set_delay() method. ++ * ++ * On channels which are backed by a socket, this ++ * API corresponds to the TCP_CORK flag. ++ * ++ * This setting is merely a hint, so implementations are ++ * free to ignore this without it being considered an ++ * error. ++ */ ++void qio_channel_set_cork(QIOChannel *ioc, ++ bool enabled); ++ ++ ++/** ++ * qio_channel_seek: ++ * @ioc: the channel object ++ * @offset: the position to seek to, relative to @whence ++ * @whence: one of the (POSIX) SEEK_* constants listed below ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Moves the current I/O position within the channel ++ * @ioc, to be @offset. The value of @offset is ++ * interpreted relative to @whence: ++ * ++ * SEEK_SET - the position is set to @offset bytes ++ * SEEK_CUR - the position is moved by @offset bytes ++ * SEEK_END - the position is set to end of the file plus @offset bytes ++ * ++ * Not all implementations will support this facility, ++ * so may report an error. ++ * ++ * Returns: the new position on success, (off_t)-1 on failure ++ */ ++off_t qio_channel_io_seek(QIOChannel *ioc, ++ off_t offset, ++ int whence, ++ Error **errp); ++ ++ ++/** ++ * qio_channel_create_watch: ++ * @ioc: the channel object ++ * @condition: the I/O condition to monitor ++ * ++ * Create a new main loop source that is used to watch ++ * for the I/O condition @condition. Typically the ++ * qio_channel_add_watch() method would be used instead ++ * of this, since it directly attaches a callback to ++ * the source ++ * ++ * Returns: the new main loop source. ++ */ ++GSource *qio_channel_create_watch(QIOChannel *ioc, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_add_watch: ++ * @ioc: the channel object ++ * @condition: the I/O condition to monitor ++ * @func: callback to invoke when the source becomes ready ++ * @user_data: opaque data to pass to @func ++ * @notify: callback to free @user_data ++ * ++ * Create a new main loop source that is used to watch ++ * for the I/O condition @condition. The callback @func ++ * will be registered against the source, to be invoked ++ * when the source becomes ready. The optional @user_data ++ * will be passed to @func when it is invoked. The @notify ++ * callback will be used to free @user_data when the ++ * watch is deleted ++ * ++ * The returned source ID can be used with g_source_remove() ++ * to remove and free the source when no longer required. ++ * Alternatively the @func callback can return a FALSE ++ * value. ++ * ++ * Returns: the source ID ++ */ ++guint qio_channel_add_watch(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify); ++ ++/** ++ * qio_channel_add_watch_full: ++ * @ioc: the channel object ++ * @condition: the I/O condition to monitor ++ * @func: callback to invoke when the source becomes ready ++ * @user_data: opaque data to pass to @func ++ * @notify: callback to free @user_data ++ * @context: the context to run the watch source ++ * ++ * Similar as qio_channel_add_watch(), but allows to specify context ++ * to run the watch source. ++ * ++ * Returns: the source ID ++ */ ++guint qio_channel_add_watch_full(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify, ++ GMainContext *context); ++ ++/** ++ * qio_channel_add_watch_source: ++ * @ioc: the channel object ++ * @condition: the I/O condition to monitor ++ * @func: callback to invoke when the source becomes ready ++ * @user_data: opaque data to pass to @func ++ * @notify: callback to free @user_data ++ * @context: gcontext to bind the source to ++ * ++ * Similar as qio_channel_add_watch(), but allows to specify context ++ * to run the watch source, meanwhile return the GSource object ++ * instead of tag ID, with the GSource referenced already. ++ * ++ * Note: callers is responsible to unref the source when not needed. ++ * ++ * Returns: the source pointer ++ */ ++GSource *qio_channel_add_watch_source(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify, ++ GMainContext *context); ++ ++/** ++ * qio_channel_yield: ++ * @ioc: the channel object ++ * @condition: the I/O condition to wait for ++ * ++ * Yields execution from the current coroutine until the condition ++ * indicated by @condition becomes available. @condition must ++ * be either %G_IO_IN or %G_IO_OUT; it cannot contain both. In ++ * addition, no two coroutine can be waiting on the same condition ++ * and channel at the same time. ++ * ++ * This must only be called from coroutine context. It is safe to ++ * reenter the coroutine externally while it is waiting; in this ++ * case the function will return even if @condition is not yet ++ * available. ++ */ ++void coroutine_fn qio_channel_yield(QIOChannel *ioc, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_wake_read: ++ * @ioc: the channel object ++ * ++ * If qio_channel_yield() is currently waiting for the channel to become ++ * readable, interrupt it and reenter immediately. This function is safe to call ++ * from any thread. ++ */ ++void qio_channel_wake_read(QIOChannel *ioc); ++ ++/** ++ * qio_channel_wait: ++ * @ioc: the channel object ++ * @condition: the I/O condition to wait for ++ * ++ * Block execution from the current thread until ++ * the condition indicated by @condition becomes ++ * available. ++ * ++ * This will enter a nested event loop to perform ++ * the wait. ++ */ ++void qio_channel_wait(QIOChannel *ioc, ++ GIOCondition condition); ++ ++/** ++ * qio_channel_set_aio_fd_handler: ++ * @ioc: the channel object ++ * @read_ctx: the AioContext to set the read handler on or NULL ++ * @io_read: the read handler ++ * @write_ctx: the AioContext to set the write handler on or NULL ++ * @io_write: the write handler ++ * @opaque: the opaque value passed to the handler ++ * ++ * This is used internally by qio_channel_yield(). It can ++ * be used by channel implementations to forward the handlers ++ * to another channel (e.g. from #QIOChannelTLS to the ++ * underlying socket). ++ * ++ * When @read_ctx is NULL, don't touch the read handler. When @write_ctx is ++ * NULL, don't touch the write handler. Note that setting the read handler ++ * clears the write handler, and vice versa, if they share the same AioContext. ++ * Therefore the caller must pass both handlers together when sharing the same ++ * AioContext. ++ */ ++void qio_channel_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque); ++ ++/** ++ * qio_channel_readv_full_all_eof: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data to ++ * @niov: the length of the @iov array ++ * @fds: an array of file handles to read ++ * @nfds: number of file handles in @fds ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * ++ * Performs same function as qio_channel_readv_all_eof. ++ * Additionally, attempts to read file descriptors shared ++ * over the channel. The function will wait for all ++ * requested data to be read, yielding from the current ++ * coroutine if required. data refers to both file ++ * descriptors and the iovs. ++ * ++ * Returns: 1 if all bytes were read, 0 if end-of-file ++ * occurs without data, or -1 on error ++ */ ++ ++int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, size_t *nfds, ++ Error **errp); ++ ++/** ++ * qio_channel_readv_full_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to read data to ++ * @niov: the length of the @iov array ++ * @fds: an array of file handles to read ++ * @nfds: number of file handles in @fds ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * ++ * Performs same function as qio_channel_readv_all_eof. ++ * Additionally, attempts to read file descriptors shared ++ * over the channel. The function will wait for all ++ * requested data to be read, yielding from the current ++ * coroutine if required. data refers to both file ++ * descriptors and the iovs. ++ * ++ * Returns: 0 if all bytes were read, or -1 on error ++ */ ++ ++int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, size_t *nfds, ++ Error **errp); ++ ++/** ++ * qio_channel_writev_full_all: ++ * @ioc: the channel object ++ * @iov: the array of memory regions to write data from ++ * @niov: the length of the @iov array ++ * @fds: an array of file handles to send ++ * @nfds: number of file handles in @fds ++ * @flags: write flags (QIO_CHANNEL_WRITE_FLAG_*) ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * ++ * Behaves like qio_channel_writev_full but will attempt ++ * to send all data passed (file handles and memory regions). ++ * The function will wait for all requested data ++ * to be written, yielding from the current coroutine ++ * if required. ++ * ++ * If QIO_CHANNEL_WRITE_FLAG_ZERO_COPY is passed in flags, ++ * instead of waiting for all requested data to be written, ++ * this function will wait until it's all queued for writing. ++ * In this case, if the buffer gets changed between queueing and ++ * sending, the updated buffer will be sent. If this is not a ++ * desired behavior, it's suggested to call qio_channel_flush() ++ * before reusing the buffer. ++ * ++ * Returns: 0 if all bytes were written, or -1 on error ++ */ ++ ++int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, size_t nfds, ++ int flags, Error **errp); ++ ++/** ++ * qio_channel_flush: ++ * @ioc: the channel object ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Will block until every packet queued with ++ * qio_channel_writev_full() + QIO_CHANNEL_WRITE_FLAG_ZERO_COPY ++ * is sent, or return in case of any error. ++ * ++ * If not implemented, acts as a no-op, and returns 0. ++ * ++ * Returns -1 if any error is found, ++ * 1 if every send failed to use zero copy. ++ * 0 otherwise. ++ */ ++ ++int qio_channel_flush(QIOChannel *ioc, ++ Error **errp); ++ ++/** ++ * qio_channel_get_peercred: ++ * @ioc: the channel object ++ * @pid: pointer to pid ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Returns the pid of the peer process connected to this socket. ++ * ++ * The use of this function is possible only for connected ++ * AF_UNIX stream sockets and for AF_UNIX stream and datagram ++ * socket pairs on Linux. ++ * Return -1 on error with pid -1 for the non-Linux OS. ++ * ++ */ ++int qio_channel_get_peerpid(QIOChannel *ioc, ++ unsigned int *pid, ++ Error **errp); ++ ++#endif /* QIO_CHANNEL_H */ +diff --git a/include/io/task.h b/include/io/task.h +new file mode 100644 +index 00000000..0b5342ee +--- /dev/null ++++ b/include/io/task.h +@@ -0,0 +1,350 @@ ++/* ++ * QEMU I/O task ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QIO_TASK_H ++#define QIO_TASK_H ++ ++typedef struct QIOTask QIOTask; ++ ++typedef void (*QIOTaskFunc)(QIOTask *task, ++ gpointer opaque); ++ ++typedef void (*QIOTaskWorker)(QIOTask *task, ++ gpointer opaque); ++ ++/** ++ * QIOTask: ++ * ++ * The QIOTask object provides a simple mechanism for reporting ++ * success / failure of long running background operations. ++ * ++ * A object on which the operation is to be performed could have ++ * a public API which accepts a task callback: ++ * ++ * ++ * Task function signature ++ * ++ * void myobject_operation(QMyObject *obj, ++ * QIOTaskFunc *func, ++ * gpointer opaque, ++ * GDestroyNotify notify); ++ * ++ * ++ * ++ * The 'func' parameter is the callback to be invoked, and 'opaque' ++ * is data to pass to it. The optional 'notify' function is used ++ * to free 'opaque' when no longer needed. ++ * ++ * When the operation completes, the 'func' callback will be ++ * invoked, allowing the calling code to determine the result ++ * of the operation. An example QIOTaskFunc implementation may ++ * look like ++ * ++ * ++ * Task callback implementation ++ * ++ * static void myobject_operation_notify(QIOTask *task, ++ * gpointer opaque) ++ * { ++ * Error *err = NULL; ++ * if (qio_task_propagate_error(task, &err)) { ++ * ...deal with the failure... ++ * error_free(err); ++ * } else { ++ * QMyObject *src = QMY_OBJECT(qio_task_get_source(task)); ++ * ...deal with the completion... ++ * } ++ * } ++ * ++ * ++ * ++ * Now, lets say the implementation of the method using the ++ * task wants to set a timer to run once a second checking ++ * for completion of some activity. It would do something ++ * like ++ * ++ * ++ * Task function implementation ++ * ++ * void myobject_operation(QMyObject *obj, ++ * QIOTaskFunc *func, ++ * gpointer opaque, ++ * GDestroyNotify notify) ++ * { ++ * QIOTask *task; ++ * ++ * task = qio_task_new(OBJECT(obj), func, opaque, notify); ++ * ++ * g_timeout_add_full(G_PRIORITY_DEFAULT, ++ * 1000, ++ * myobject_operation_timer, ++ * task, ++ * NULL); ++ * } ++ * ++ * ++ * ++ * It could equally have setup a watch on a file descriptor or ++ * created a background thread, or something else entirely. ++ * Notice that the source object is passed to the task, and ++ * QIOTask will hold a reference on that. This ensure that ++ * the QMyObject instance cannot be garbage collected while ++ * the async task is still in progress. ++ * ++ * In this case, myobject_operation_timer will fire after ++ * 3 secs and do ++ * ++ * ++ * Task timer function ++ * ++ * gboolean myobject_operation_timer(gpointer opaque) ++ * { ++ * QIOTask *task = QIO_TASK(opaque); ++ * Error *err = NULL; ++ * ++ * ...check something important... ++ * if (err) { ++ * qio_task_set_error(task, err); ++ * qio_task_complete(task); ++ * return FALSE; ++ * } else if (...work is completed ...) { ++ * qio_task_complete(task); ++ * return FALSE; ++ * } ++ * ...carry on polling ... ++ * return TRUE; ++ * } ++ * ++ * ++ * ++ * The 'qio_task_complete' call in this method will trigger ++ * the callback func 'myobject_operation_notify' shown ++ * earlier to deal with the results. ++ * ++ * Once this function returns false, object_unref will be called ++ * automatically on the task causing it to be released and the ++ * ref on QMyObject dropped too. ++ * ++ * The QIOTask module can also be used to perform operations ++ * in a background thread context, while still reporting the ++ * results in the main event thread. This allows code which ++ * cannot easily be rewritten to be asynchronous (such as DNS ++ * lookups) to be easily run non-blocking. Reporting the ++ * results in the main thread context means that the caller ++ * typically does not need to be concerned about thread ++ * safety wrt the BQL. ++ * ++ * For example, the socket_listen() method will block the caller ++ * while DNS lookups take place if given a name, instead of IP ++ * address. The C library often do not provide a practical async ++ * DNS API, so the to get non-blocking DNS lookups in a portable ++ * manner requires use of a thread. So achieve a non-blocking ++ * socket listen using QIOTask would require: ++ * ++ * ++ * static void myobject_listen_worker(QIOTask *task, ++ * gpointer opaque) ++ * { ++ * QMyObject obj = QMY_OBJECT(qio_task_get_source(task)); ++ * SocketAddress *addr = opaque; ++ * Error *err = NULL; ++ * ++ * obj->fd = socket_listen(addr, &err); ++ * ++ qio_task_set_error(task, err); ++ * } ++ * ++ * void myobject_listen_async(QMyObject *obj, ++ * SocketAddress *addr, ++ * QIOTaskFunc *func, ++ * gpointer opaque, ++ * GDestroyNotify notify) ++ * { ++ * QIOTask *task; ++ * SocketAddress *addrCopy; ++ * ++ * addrCopy = QAPI_CLONE(SocketAddress, addr); ++ * task = qio_task_new(OBJECT(obj), func, opaque, notify); ++ * ++ * qio_task_run_in_thread(task, myobject_listen_worker, ++ * addrCopy, ++ * qapi_free_SocketAddress); ++ * } ++ * ++ * ++ * NB, The 'func' callback passed into myobject_listen_async ++ * will be invoked from the main event thread, despite the ++ * actual operation being performed in a different thread. ++ */ ++ ++/** ++ * qio_task_new: ++ * @source: the object on which the operation is invoked ++ * @func: the callback to invoke when the task completes ++ * @opaque: opaque data to pass to @func when invoked ++ * @destroy: optional callback to free @opaque ++ * ++ * Creates a new task struct to track completion of a ++ * background operation running on the object @source. ++ * When the operation completes or fails, the callback ++ * @func will be invoked. The callback can access the ++ * 'err' attribute in the task object to determine if ++ * the operation was successful or not. ++ * ++ * The returned task will be released when qio_task_complete() ++ * is invoked. ++ * ++ * Returns: the task struct ++ */ ++QIOTask *qio_task_new(Object *source, ++ QIOTaskFunc func, ++ gpointer opaque, ++ GDestroyNotify destroy); ++ ++/** ++ * qio_task_run_in_thread: ++ * @task: the task struct ++ * @worker: the function to invoke in a thread ++ * @opaque: opaque data to pass to @worker ++ * @destroy: function to free @opaque ++ * @context: the context to run the complete hook. If %NULL, the ++ * default context will be used. ++ * ++ * Run a task in a background thread. When @worker ++ * returns it will call qio_task_complete() in ++ * the thread that is running the main loop associated ++ * with @context. ++ */ ++void qio_task_run_in_thread(QIOTask *task, ++ QIOTaskWorker worker, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context); ++ ++ ++/** ++ * qio_task_wait_thread: ++ * @task: the task struct ++ * ++ * Wait for completion of a task that was previously ++ * invoked using qio_task_run_in_thread. This MUST ++ * ONLY be invoked if the task has not already ++ * completed, since after the completion callback ++ * is invoked, @task will have been freed. ++ * ++ * To avoid racing with execution of the completion ++ * callback provided with qio_task_new, this method ++ * MUST ONLY be invoked from the thread that is ++ * running the main loop associated with @context ++ * parameter to qio_task_run_in_thread. ++ * ++ * When the thread has completed, the completion ++ * callback provided to qio_task_new will be invoked. ++ * When that callback returns @task will be freed, ++ * so @task must not be referenced after this ++ * method completes. ++ */ ++void qio_task_wait_thread(QIOTask *task); ++ ++ ++/** ++ * qio_task_complete: ++ * @task: the task struct ++ * ++ * Invoke the completion callback for @task and ++ * then free its memory. ++ */ ++void qio_task_complete(QIOTask *task); ++ ++ ++/** ++ * qio_task_set_error: ++ * @task: the task struct ++ * @err: pointer to the error, or NULL ++ * ++ * Associate an error with the task, which can later ++ * be retrieved with the qio_task_propagate_error() ++ * method. This method takes ownership of @err, so ++ * it is not valid to access it after this call ++ * completes. If @err is NULL this is a no-op. If ++ * this is call multiple times, only the first ++ * provided @err will be recorded, later ones will ++ * be discarded and freed. ++ */ ++void qio_task_set_error(QIOTask *task, ++ Error *err); ++ ++ ++/** ++ * qio_task_propagate_error: ++ * @task: the task struct ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Propagate the error associated with @task ++ * into @errp. ++ * ++ * Returns: true if an error was propagated, false otherwise ++ */ ++bool qio_task_propagate_error(QIOTask *task, ++ Error **errp); ++ ++ ++/** ++ * qio_task_set_result_pointer: ++ * @task: the task struct ++ * @result: pointer to the result data ++ * ++ * Associate an opaque result with the task, ++ * which can later be retrieved with the ++ * qio_task_get_result_pointer() method ++ * ++ */ ++void qio_task_set_result_pointer(QIOTask *task, ++ gpointer result, ++ GDestroyNotify notify); ++ ++ ++/** ++ * qio_task_get_result_pointer: ++ * @task: the task struct ++ * ++ * Retrieve the opaque result data associated ++ * with the task, if any. ++ * ++ * Returns: the task result, or NULL ++ */ ++gpointer qio_task_get_result_pointer(QIOTask *task); ++ ++ ++/** ++ * qio_task_get_source: ++ * @task: the task struct ++ * ++ * Get the source object associated with the background ++ * task. The caller does not own a reference on the ++ * returned Object, and so should call object_ref() ++ * if it wants to keep the object pointer outside the ++ * lifetime of the QIOTask object. ++ * ++ * Returns: the source object ++ */ ++Object *qio_task_get_source(QIOTask *task); ++ ++#endif /* QIO_TASK_H */ +diff --git a/include/qapi/clone-visitor.h b/include/qapi/clone-visitor.h +new file mode 100644 +index 00000000..ebc182b0 +--- /dev/null ++++ b/include/qapi/clone-visitor.h +@@ -0,0 +1,62 @@ ++/* ++ * Clone Visitor ++ * ++ * Copyright (C) 2016 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_CLONE_VISITOR_H ++#define QAPI_CLONE_VISITOR_H ++ ++#include "qapi/error.h" ++#include "qapi/visitor.h" ++ ++/* ++ * The clone visitor is for direct use only by the QAPI_CLONE() macro; ++ * it requires that the root visit occur on an object, list, or ++ * alternate, and is not usable directly on built-in QAPI types. ++ */ ++typedef struct QapiCloneVisitor QapiCloneVisitor; ++ ++Visitor *qapi_clone_visitor_new(void); ++Visitor *qapi_clone_members_visitor_new(void); ++ ++/* ++ * Deep-clone QAPI object @src of the given @type, and return the result. ++ * ++ * Not usable on QAPI scalars (integers, strings, enums), nor on a ++ * QAPI object that references the 'any' type. Safe when @src is NULL. ++ */ ++#define QAPI_CLONE(type, src) \ ++ ({ \ ++ Visitor *v_; \ ++ type *dst_ = (type *) (src); /* Cast away const */ \ ++ \ ++ if (dst_) { \ ++ v_ = qapi_clone_visitor_new(); \ ++ visit_type_ ## type(v_, NULL, &dst_, &error_abort); \ ++ visit_free(v_); \ ++ } \ ++ dst_; \ ++ }) ++ ++/* ++ * Copy deep clones of @type members from @src to @dst. ++ * ++ * Not usable on QAPI scalars (integers, strings, enums), nor on a ++ * QAPI object that references the 'any' type. ++ */ ++#define QAPI_CLONE_MEMBERS(type, dst, src) \ ++ ({ \ ++ Visitor *v_; \ ++ \ ++ v_ = qapi_clone_members_visitor_new(); \ ++ *(type *)(dst) = *(src); \ ++ visit_type_ ## type ## _members(v_, (type *)(dst), &error_abort); \ ++ visit_free(v_); \ ++ }) ++ ++#endif +diff --git a/include/qapi/compat-policy.h b/include/qapi/compat-policy.h +new file mode 100644 +index 00000000..8b7b25c0 +--- /dev/null ++++ b/include/qapi/compat-policy.h +@@ -0,0 +1,45 @@ ++/* ++ * Policy for handling "funny" management interfaces ++ * ++ * Copyright (C) 2020 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMPAT_POLICY_H ++#define QAPI_COMPAT_POLICY_H ++ ++#include "qapi/error.h" ++#include "qapi/qapi-types-compat.h" ++ ++extern CompatPolicy compat_policy; ++ ++bool compat_policy_input_ok(unsigned special_features, ++ const CompatPolicy *policy, ++ ErrorClass error_class, ++ const char *kind, const char *name, ++ Error **errp); ++ ++/* ++ * Create a QObject input visitor for @obj for use with QMP ++ * ++ * This is like qobject_input_visitor_new(), except it obeys the ++ * policy for handling deprecated management interfaces set with ++ * -compat. ++ */ ++Visitor *qobject_input_visitor_new_qmp(QObject *obj); ++ ++/* ++ * Create a QObject output visitor for @obj for use with QMP ++ * ++ * This is like qobject_output_visitor_new(), except it obeys the ++ * policy for handling deprecated management interfaces set with ++ * -compat. ++ */ ++Visitor *qobject_output_visitor_new_qmp(QObject **result); ++ ++#endif +diff --git a/include/qapi/dealloc-visitor.h b/include/qapi/dealloc-visitor.h +new file mode 100644 +index 00000000..c36715fd +--- /dev/null ++++ b/include/qapi/dealloc-visitor.h +@@ -0,0 +1,28 @@ ++/* ++ * Dealloc Visitor ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Michael Roth ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_DEALLOC_VISITOR_H ++#define QAPI_DEALLOC_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct QapiDeallocVisitor QapiDeallocVisitor; ++ ++/* ++ * The dealloc visitor is primarily used only by generated ++ * qapi_free_FOO() functions, and is the only visitor designed to work ++ * correctly in the face of a partially-constructed QAPI tree. ++ */ ++Visitor *qapi_dealloc_visitor_new(void); ++ ++#endif +diff --git a/include/qapi/error.h b/include/qapi/error.h +new file mode 100644 +index 00000000..71f8fb2c +--- /dev/null ++++ b/include/qapi/error.h +@@ -0,0 +1,540 @@ ++/* ++ * QEMU Error Objects ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2011-2015 Red Hat, Inc. ++ * ++ * Authors: ++ * Anthony Liguori ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2. See ++ * the COPYING.LIB file in the top-level directory. ++ */ ++ ++/* ++ * Error reporting system loosely patterned after Glib's GError. ++ * ++ * = Rules = ++ * ++ * - Functions that use Error to report errors have an Error **errp ++ * parameter. It should be the last parameter, except for functions ++ * taking variable arguments. ++ * ++ * - You may pass NULL to not receive the error, &error_abort to abort ++ * on error, &error_fatal to exit(1) on error, or a pointer to a ++ * variable containing NULL to receive the error. ++ * ++ * - Separation of concerns: the function is responsible for detecting ++ * errors and failing cleanly; handling the error is its caller's ++ * job. Since the value of @errp is about handling the error, the ++ * function should not examine it. ++ * ++ * - The function may pass @errp to functions it calls to pass on ++ * their errors to its caller. If it dereferences @errp to check ++ * for errors, it must use ERRP_GUARD(). ++ * ++ * - On success, the function should not touch *errp. On failure, it ++ * should set a new error, e.g. with error_setg(errp, ...), or ++ * propagate an existing one, e.g. with error_propagate(errp, ...). ++ * ++ * - Whenever practical, also return a value that indicates success / ++ * failure. This can make the error checking more concise, and can ++ * avoid useless error object creation and destruction. Note that ++ * we still have many functions returning void. We recommend ++ * • bool-valued functions return true on success / false on failure, ++ * • pointer-valued functions return non-null / null pointer, and ++ * • integer-valued functions return non-negative / negative. ++ * ++ * = Creating errors = ++ * ++ * Create an error: ++ * error_setg(errp, "situation normal, all fouled up"); ++ * where @errp points to the location to receive the error. ++ * ++ * Create an error and add additional explanation: ++ * error_setg(errp, "invalid quark"); ++ * error_append_hint(errp, "Valid quarks are up, down, strange, " ++ * "charm, top, bottom.\n"); ++ * This may require use of ERRP_GUARD(); more on that below. ++ * ++ * Do *not* contract this to ++ * error_setg(errp, "invalid quark\n" // WRONG! ++ * "Valid quarks are up, down, strange, charm, top, bottom."); ++ * ++ * = Reporting and destroying errors = ++ * ++ * Report an error to the current monitor if we have one, else stderr: ++ * error_report_err(err); ++ * This frees the error object. ++ * ++ * Likewise, but with additional text prepended: ++ * error_reportf_err(err, "Could not frobnicate '%s': ", name); ++ * ++ * Report an error somewhere else: ++ * const char *msg = error_get_pretty(err); ++ * do with msg what needs to be done... ++ * error_free(err); ++ * Note that this loses hints added with error_append_hint(). ++ * ++ * Call a function ignoring errors: ++ * foo(arg, NULL); ++ * This is more concise than ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * error_free(err); // don't do this ++ * ++ * Call a function aborting on errors: ++ * foo(arg, &error_abort); ++ * This is more concise and fails more nicely than ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * assert(!err); // don't do this ++ * ++ * Call a function treating errors as fatal: ++ * foo(arg, &error_fatal); ++ * This is more concise than ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * if (err) { // don't do this ++ * error_report_err(err); ++ * exit(1); ++ * } ++ * ++ * Handle an error without reporting it (just for completeness): ++ * error_free(err); ++ * ++ * Assert that an expected error occurred, but clean it up without ++ * reporting it (primarily useful in testsuites): ++ * error_free_or_abort(&err); ++ * ++ * = Passing errors around = ++ * ++ * Errors get passed to the caller through the conventional @errp ++ * parameter. ++ * ++ * Create a new error and pass it to the caller: ++ * error_setg(errp, "situation normal, all fouled up"); ++ * ++ * Call a function, receive an error from it, and pass it to the caller ++ * - when the function returns a value that indicates failure, say ++ * false: ++ * if (!foo(arg, errp)) { ++ * handle the error... ++ * } ++ * - when it does not, say because it is a void function: ++ * ERRP_GUARD(); ++ * foo(arg, errp); ++ * if (*errp) { ++ * handle the error... ++ * } ++ * More on ERRP_GUARD() below. ++ * ++ * Code predating ERRP_GUARD() still exists, and looks like this: ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * if (err) { ++ * handle the error... ++ * error_propagate(errp, err); // deprecated ++ * } ++ * Avoid in new code. Do *not* "optimize" it to ++ * foo(arg, errp); ++ * if (*errp) { // WRONG! ++ * handle the error... ++ * } ++ * because errp may be NULL without the ERRP_GUARD() guard. ++ * ++ * But when all you do with the error is pass it on, please use ++ * foo(arg, errp); ++ * for readability. ++ * ++ * Receive an error, and handle it locally ++ * - when the function returns a value that indicates failure, say ++ * false: ++ * Error *err = NULL; ++ * if (!foo(arg, &err)) { ++ * handle the error... ++ * } ++ * - when it does not, say because it is a void function: ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * if (err) { ++ * handle the error... ++ * } ++ * ++ * Pass an existing error to the caller: ++ * error_propagate(errp, err); ++ * This is rarely needed. When @err is a local variable, use of ++ * ERRP_GUARD() commonly results in more readable code. ++ * ++ * Pass an existing error to the caller with the message modified: ++ * error_propagate_prepend(errp, err, ++ * "Could not frobnicate '%s': ", name); ++ * This is more concise than ++ * error_propagate(errp, err); // don't do this ++ * error_prepend(errp, "Could not frobnicate '%s': ", name); ++ * and works even when @errp is &error_fatal. ++ * ++ * Receive and accumulate multiple errors (first one wins): ++ * Error *err = NULL, *local_err = NULL; ++ * foo(arg, &err); ++ * bar(arg, &local_err); ++ * error_propagate(&err, local_err); ++ * if (err) { ++ * handle the error... ++ * } ++ * ++ * Do *not* "optimize" this to ++ * Error *err = NULL; ++ * foo(arg, &err); ++ * bar(arg, &err); // WRONG! ++ * if (err) { ++ * handle the error... ++ * } ++ * because this may pass a non-null err to bar(). ++ * ++ * Likewise, do *not* ++ * Error *err = NULL; ++ * if (cond1) { ++ * error_setg(&err, ...); ++ * } ++ * if (cond2) { ++ * error_setg(&err, ...); // WRONG! ++ * } ++ * because this may pass a non-null err to error_setg(). ++ * ++ * = Why, when and how to use ERRP_GUARD() = ++ * ++ * Without ERRP_GUARD(), use of the @errp parameter is restricted: ++ * - It must not be dereferenced, because it may be null. ++ * - It should not be passed to error_prepend(), error_vprepend(), or ++ * error_append_hint(), because that doesn't work with &error_fatal. ++ * ERRP_GUARD() lifts these restrictions. ++ * ++ * To use ERRP_GUARD(), add it right at the beginning of the function. ++ * @errp can then be used without worrying about the argument being ++ * NULL or &error_fatal. ++ * ++ * Using it when it's not needed is safe, but please avoid cluttering ++ * the source with useless code. ++ * ++ * = Converting to ERRP_GUARD() = ++ * ++ * To convert a function to use ERRP_GUARD(): ++ * ++ * 0. If the Error ** parameter is not named @errp, rename it to ++ * @errp. ++ * ++ * 1. Add an ERRP_GUARD() invocation, by convention right at the ++ * beginning of the function. This makes @errp safe to use. ++ * ++ * 2. Replace &err by errp, and err by *errp. Delete local variable ++ * @err. ++ * ++ * 3. Delete error_propagate(errp, *errp), replace ++ * error_propagate_prepend(errp, *errp, ...) by error_prepend(errp, ...) ++ * ++ * 4. Ensure @errp is valid at return: when you destroy *errp, set ++ * *errp = NULL. ++ * ++ * Example: ++ * ++ * bool fn(..., Error **errp) ++ * { ++ * Error *err = NULL; ++ * ++ * foo(arg, &err); ++ * if (err) { ++ * handle the error... ++ * error_propagate(errp, err); ++ * return false; ++ * } ++ * ... ++ * } ++ * ++ * becomes ++ * ++ * bool fn(..., Error **errp) ++ * { ++ * ERRP_GUARD(); ++ * ++ * foo(arg, errp); ++ * if (*errp) { ++ * handle the error... ++ * return false; ++ * } ++ * ... ++ * } ++ * ++ * For mass-conversion, use scripts/coccinelle/errp-guard.cocci. ++ */ ++ ++#ifndef ERROR_H ++#define ERROR_H ++ ++#include "qapi/qapi-types-error.h" ++ ++/* ++ * Overall category of an error. ++ * Based on the qapi type QapiErrorClass, but reproduced here for nicer ++ * enum names. ++ */ ++typedef enum ErrorClass { ++ ERROR_CLASS_GENERIC_ERROR = QAPI_ERROR_CLASS_GENERICERROR, ++ ERROR_CLASS_COMMAND_NOT_FOUND = QAPI_ERROR_CLASS_COMMANDNOTFOUND, ++ ERROR_CLASS_DEVICE_NOT_ACTIVE = QAPI_ERROR_CLASS_DEVICENOTACTIVE, ++ ERROR_CLASS_DEVICE_NOT_FOUND = QAPI_ERROR_CLASS_DEVICENOTFOUND, ++ ERROR_CLASS_KVM_MISSING_CAP = QAPI_ERROR_CLASS_KVMMISSINGCAP, ++} ErrorClass; ++ ++/* ++ * Get @err's human-readable error message. ++ */ ++const char *error_get_pretty(const Error *err); ++ ++/* ++ * Get @err's error class. ++ * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is ++ * strongly discouraged. ++ */ ++ErrorClass error_get_class(const Error *err); ++ ++/* ++ * Create a new error object and assign it to *@errp. ++ * If @errp is NULL, the error is ignored. Don't bother creating one ++ * then. ++ * If @errp is &error_abort, print a suitable message and abort(). ++ * If @errp is &error_fatal, print a suitable message and exit(1). ++ * If @errp is anything else, *@errp must be NULL. ++ * The new error's class is ERROR_CLASS_GENERIC_ERROR, and its ++ * human-readable error message is made from printf-style @fmt, ... ++ * The resulting message should be a single phrase, with no newline or ++ * trailing punctuation. ++ * Please don't error_setg(&error_fatal, ...), use error_report() and ++ * exit(), because that's more obvious. ++ * Likewise, don't error_setg(&error_abort, ...), use assert(). ++ */ ++#define error_setg(errp, fmt, ...) \ ++ error_setg_internal((errp), __FILE__, __LINE__, __func__, \ ++ (fmt), ## __VA_ARGS__) ++void error_setg_internal(Error **errp, ++ const char *src, int line, const char *func, ++ const char *fmt, ...) ++ G_GNUC_PRINTF(5, 6); ++ ++/* ++ * Just like error_setg(), with @os_error info added to the message. ++ * If @os_error is non-zero, ": " + strerror(os_error) is appended to ++ * the human-readable error message. ++ * ++ * The value of errno (which usually can get clobbered by almost any ++ * function call) will be preserved. ++ */ ++#define error_setg_errno(errp, os_error, fmt, ...) \ ++ error_setg_errno_internal((errp), __FILE__, __LINE__, __func__, \ ++ (os_error), (fmt), ## __VA_ARGS__) ++void error_setg_errno_internal(Error **errp, ++ const char *fname, int line, const char *func, ++ int os_error, const char *fmt, ...) ++ G_GNUC_PRINTF(6, 7); ++ ++#ifdef _WIN32 ++/* ++ * Just like error_setg(), with @win32_error info added to the message. ++ * If @win32_error is non-zero, ": " + g_win32_error_message(win32_err) ++ * is appended to the human-readable error message. ++ */ ++#define error_setg_win32(errp, win32_err, fmt, ...) \ ++ error_setg_win32_internal((errp), __FILE__, __LINE__, __func__, \ ++ (win32_err), (fmt), ## __VA_ARGS__) ++void error_setg_win32_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int win32_err, const char *fmt, ...) ++ G_GNUC_PRINTF(6, 7); ++#endif ++ ++/* ++ * Propagate error object (if any) from @local_err to @dst_errp. ++ * If @local_err is NULL, do nothing (because there's nothing to ++ * propagate). ++ * Else, if @dst_errp is NULL, errors are being ignored. Free the ++ * error object. ++ * Else, if @dst_errp is &error_abort, print a suitable message and ++ * abort(). ++ * Else, if @dst_errp is &error_fatal, print a suitable message and ++ * exit(1). ++ * Else, if @dst_errp already contains an error, ignore this one: free ++ * the error object. ++ * Else, move the error object from @local_err to *@dst_errp. ++ * On return, @local_err is invalid. ++ * Please use ERRP_GUARD() instead when possible. ++ * Please don't error_propagate(&error_fatal, ...), use ++ * error_report_err() and exit(), because that's more obvious. ++ */ ++void error_propagate(Error **dst_errp, Error *local_err); ++ ++ ++/* ++ * Propagate error object (if any) with some text prepended. ++ * Behaves like ++ * error_prepend(&local_err, fmt, ...); ++ * error_propagate(dst_errp, local_err); ++ * Please use ERRP_GUARD() and error_prepend() instead when possible. ++ */ ++void error_propagate_prepend(Error **dst_errp, Error *local_err, ++ const char *fmt, ...) ++ G_GNUC_PRINTF(3, 4); ++ ++/* ++ * Prepend some text to @errp's human-readable error message. ++ * The text is made by formatting @fmt, @ap like vprintf(). ++ */ ++void error_vprepend(Error *const *errp, const char *fmt, va_list ap) ++ G_GNUC_PRINTF(2, 0); ++ ++/* ++ * Prepend some text to @errp's human-readable error message. ++ * The text is made by formatting @fmt, ... like printf(). ++ */ ++void error_prepend(Error *const *errp, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++/* ++ * Append a printf-style human-readable explanation to an existing error. ++ * If the error is later reported to a human user with ++ * error_report_err() or warn_report_err(), the hints will be shown, ++ * too. If it's reported via QMP, the hints will be ignored. ++ * Intended use is adding helpful hints on the human user interface, ++ * e.g. a list of valid values. It's not for clarifying a confusing ++ * error message. ++ * @errp may be NULL, but not &error_fatal or &error_abort. ++ * Trivially the case if you call it only after error_setg() or ++ * error_propagate(). ++ * May be called multiple times. The resulting hint should end with a ++ * newline. ++ */ ++void error_append_hint(Error *const *errp, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++/* ++ * Convenience function to report open() failure. ++ */ ++#define error_setg_file_open(errp, os_errno, filename) \ ++ error_setg_file_open_internal((errp), __FILE__, __LINE__, __func__, \ ++ (os_errno), (filename)) ++void error_setg_file_open_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int os_errno, const char *filename); ++ ++/* ++ * Return an exact copy of @err. ++ */ ++Error *error_copy(const Error *err); ++ ++/* ++ * Free @err. ++ * @err may be NULL. ++ */ ++void error_free(Error *err); ++ ++/* ++ * Convenience function to assert that *@errp is set, then silently free it. ++ */ ++void error_free_or_abort(Error **errp); ++ ++/* ++ * Convenience function to warn_report() and free @err. ++ * The report includes hints added with error_append_hint(). ++ */ ++void warn_report_err(Error *err); ++ ++/* ++ * Convenience function to error_report() and free @err. ++ * The report includes hints added with error_append_hint(). ++ */ ++void error_report_err(Error *err); ++ ++/* ++ * Convenience function to error_prepend(), warn_report() and free @err. ++ */ ++void warn_reportf_err(Error *err, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++/* ++ * Convenience function to error_prepend(), error_report() and free @err. ++ */ ++void error_reportf_err(Error *err, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++/* ++ * Just like error_setg(), except you get to specify the error class. ++ * Note: use of error classes other than ERROR_CLASS_GENERIC_ERROR is ++ * strongly discouraged. ++ */ ++#define error_set(errp, err_class, fmt, ...) \ ++ error_set_internal((errp), __FILE__, __LINE__, __func__, \ ++ (err_class), (fmt), ## __VA_ARGS__) ++void error_set_internal(Error **errp, ++ const char *src, int line, const char *func, ++ ErrorClass err_class, const char *fmt, ...) ++ G_GNUC_PRINTF(6, 7); ++ ++/* ++ * Make @errp parameter easier to use regardless of argument value ++ * ++ * This macro is for use right at the beginning of a function that ++ * takes an Error **errp parameter to pass errors to its caller. The ++ * parameter must be named @errp. ++ * ++ * It must be used when the function dereferences @errp or passes ++ * @errp to error_prepend(), error_vprepend(), or error_append_hint(). ++ * It is safe to use even when it's not needed, but please avoid ++ * cluttering the source with useless code. ++ * ++ * If @errp is NULL or &error_fatal, rewrite it to point to a local ++ * Error variable, which will be automatically propagated to the ++ * original @errp on function exit. ++ * ++ * Note: &error_abort is not rewritten, because that would move the ++ * abort from the place where the error is created to the place where ++ * it's propagated. ++ */ ++#define ERRP_GUARD() \ ++ g_auto(ErrorPropagator) _auto_errp_prop = {.errp = errp}; \ ++ do { \ ++ if (!errp || errp == &error_fatal) { \ ++ errp = &_auto_errp_prop.local_err; \ ++ } \ ++ } while (0) ++ ++typedef struct ErrorPropagator { ++ Error *local_err; ++ Error **errp; ++} ErrorPropagator; ++ ++static inline void error_propagator_cleanup(ErrorPropagator *prop) ++{ ++ error_propagate(prop->errp, prop->local_err); ++} ++ ++G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(ErrorPropagator, error_propagator_cleanup); ++ ++/* ++ * Special error destination to warn on error. ++ * See error_setg() and error_propagate() for details. ++ */ ++extern Error *error_warn; ++ ++/* ++ * Special error destination to abort on error. ++ * See error_setg() and error_propagate() for details. ++ */ ++extern Error *error_abort; ++ ++/* ++ * Special error destination to exit(1) on error. ++ * See error_setg() and error_propagate() for details. ++ */ ++extern Error *error_fatal; ++ ++#endif +diff --git a/include/qapi/qapi-builtin-types.h b/include/qapi/qapi-builtin-types.h +new file mode 100644 +index 00000000..b217d4a3 +--- /dev/null ++++ b/include/qapi/qapi-builtin-types.h +@@ -0,0 +1,184 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Built-in QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_BUILTIN_TYPES_H ++#define QAPI_BUILTIN_TYPES_H ++ ++#include "qapi/util.h" ++ ++typedef struct strList strList; ++ ++typedef struct numberList numberList; ++ ++typedef struct intList intList; ++ ++typedef struct int8List int8List; ++ ++typedef struct int16List int16List; ++ ++typedef struct int32List int32List; ++ ++typedef struct int64List int64List; ++ ++typedef struct uint8List uint8List; ++ ++typedef struct uint16List uint16List; ++ ++typedef struct uint32List uint32List; ++ ++typedef struct uint64List uint64List; ++ ++typedef struct sizeList sizeList; ++ ++typedef struct boolList boolList; ++ ++typedef struct anyList anyList; ++ ++typedef struct nullList nullList; ++ ++typedef enum QType { ++ QTYPE_NONE, ++ QTYPE_QNULL, ++ QTYPE_QNUM, ++ QTYPE_QSTRING, ++ QTYPE_QDICT, ++ QTYPE_QLIST, ++ QTYPE_QBOOL, ++ QTYPE__MAX, ++} QType; ++ ++#define QType_str(val) \ ++ qapi_enum_lookup(&QType_lookup, (val)) ++ ++extern const QEnumLookup QType_lookup; ++ ++struct strList { ++ strList *next; ++ char *value; ++}; ++ ++void qapi_free_strList(strList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(strList, qapi_free_strList) ++ ++struct numberList { ++ numberList *next; ++ double value; ++}; ++ ++void qapi_free_numberList(numberList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(numberList, qapi_free_numberList) ++ ++struct intList { ++ intList *next; ++ int64_t value; ++}; ++ ++void qapi_free_intList(intList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(intList, qapi_free_intList) ++ ++struct int8List { ++ int8List *next; ++ int8_t value; ++}; ++ ++void qapi_free_int8List(int8List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(int8List, qapi_free_int8List) ++ ++struct int16List { ++ int16List *next; ++ int16_t value; ++}; ++ ++void qapi_free_int16List(int16List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(int16List, qapi_free_int16List) ++ ++struct int32List { ++ int32List *next; ++ int32_t value; ++}; ++ ++void qapi_free_int32List(int32List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(int32List, qapi_free_int32List) ++ ++struct int64List { ++ int64List *next; ++ int64_t value; ++}; ++ ++void qapi_free_int64List(int64List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(int64List, qapi_free_int64List) ++ ++struct uint8List { ++ uint8List *next; ++ uint8_t value; ++}; ++ ++void qapi_free_uint8List(uint8List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(uint8List, qapi_free_uint8List) ++ ++struct uint16List { ++ uint16List *next; ++ uint16_t value; ++}; ++ ++void qapi_free_uint16List(uint16List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(uint16List, qapi_free_uint16List) ++ ++struct uint32List { ++ uint32List *next; ++ uint32_t value; ++}; ++ ++void qapi_free_uint32List(uint32List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(uint32List, qapi_free_uint32List) ++ ++struct uint64List { ++ uint64List *next; ++ uint64_t value; ++}; ++ ++void qapi_free_uint64List(uint64List *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(uint64List, qapi_free_uint64List) ++ ++struct sizeList { ++ sizeList *next; ++ uint64_t value; ++}; ++ ++void qapi_free_sizeList(sizeList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(sizeList, qapi_free_sizeList) ++ ++struct boolList { ++ boolList *next; ++ bool value; ++}; ++ ++void qapi_free_boolList(boolList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(boolList, qapi_free_boolList) ++ ++struct anyList { ++ anyList *next; ++ QObject *value; ++}; ++ ++void qapi_free_anyList(anyList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(anyList, qapi_free_anyList) ++ ++struct nullList { ++ nullList *next; ++ QNull *value; ++}; ++ ++void qapi_free_nullList(nullList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(nullList, qapi_free_nullList) ++ ++#endif /* QAPI_BUILTIN_TYPES_H */ +diff --git a/include/qapi/qapi-builtin-visit.h b/include/qapi/qapi-builtin-visit.h +new file mode 100644 +index 00000000..14b44234 +--- /dev/null ++++ b/include/qapi/qapi-builtin-visit.h +@@ -0,0 +1,68 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Built-in QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_BUILTIN_VISIT_H ++#define QAPI_BUILTIN_VISIT_H ++ ++#include "qapi/visitor.h" ++#include "qapi/qapi-builtin-types.h" ++ ++ ++bool visit_type_strList(Visitor *v, const char *name, ++ strList **obj, Error **errp); ++ ++bool visit_type_numberList(Visitor *v, const char *name, ++ numberList **obj, Error **errp); ++ ++bool visit_type_intList(Visitor *v, const char *name, ++ intList **obj, Error **errp); ++ ++bool visit_type_int8List(Visitor *v, const char *name, ++ int8List **obj, Error **errp); ++ ++bool visit_type_int16List(Visitor *v, const char *name, ++ int16List **obj, Error **errp); ++ ++bool visit_type_int32List(Visitor *v, const char *name, ++ int32List **obj, Error **errp); ++ ++bool visit_type_int64List(Visitor *v, const char *name, ++ int64List **obj, Error **errp); ++ ++bool visit_type_uint8List(Visitor *v, const char *name, ++ uint8List **obj, Error **errp); ++ ++bool visit_type_uint16List(Visitor *v, const char *name, ++ uint16List **obj, Error **errp); ++ ++bool visit_type_uint32List(Visitor *v, const char *name, ++ uint32List **obj, Error **errp); ++ ++bool visit_type_uint64List(Visitor *v, const char *name, ++ uint64List **obj, Error **errp); ++ ++bool visit_type_sizeList(Visitor *v, const char *name, ++ sizeList **obj, Error **errp); ++ ++bool visit_type_boolList(Visitor *v, const char *name, ++ boolList **obj, Error **errp); ++ ++bool visit_type_anyList(Visitor *v, const char *name, ++ anyList **obj, Error **errp); ++ ++bool visit_type_nullList(Visitor *v, const char *name, ++ nullList **obj, Error **errp); ++ ++bool visit_type_QType(Visitor *v, const char *name, ++ QType *obj, Error **errp); ++ ++#endif /* QAPI_BUILTIN_VISIT_H */ +diff --git a/include/qapi/qapi-commands-block-core.h b/include/qapi/qapi-commands-block-core.h +new file mode 100644 +index 00000000..e698e23c +--- /dev/null ++++ b/include/qapi/qapi-commands-block-core.h +@@ -0,0 +1,103 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP commands ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMMANDS_BLOCK_CORE_H ++#define QAPI_COMMANDS_BLOCK_CORE_H ++ ++#include "qapi-commands-common.h" ++#include "qapi-commands-crypto.h" ++#include "qapi-commands-job.h" ++#include "qapi-commands-sockets.h" ++#include "qapi-types-block-core.h" ++ ++BlockInfoList *qmp_query_block(Error **errp); ++void qmp_marshal_query_block(QDict *args, QObject **ret, Error **errp); ++BlockStatsList *qmp_query_blockstats(bool has_query_nodes, bool query_nodes, Error **errp); ++void qmp_marshal_query_blockstats(QDict *args, QObject **ret, Error **errp); ++BlockJobInfoList *qmp_query_block_jobs(Error **errp); ++void qmp_marshal_query_block_jobs(QDict *args, QObject **ret, Error **errp); ++void coroutine_fn qmp_block_resize(const char *device, const char *node_name, int64_t size, Error **errp); ++void coroutine_fn qmp_marshal_block_resize(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_snapshot_sync(const char *device, const char *node_name, const char *snapshot_file, const char *snapshot_node_name, const char *format, bool has_mode, NewImageMode mode, Error **errp); ++void qmp_marshal_blockdev_snapshot_sync(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_snapshot(const char *node, const char *overlay, Error **errp); ++void qmp_marshal_blockdev_snapshot(QDict *args, QObject **ret, Error **errp); ++void qmp_change_backing_file(const char *device, const char *image_node_name, const char *backing_file, Error **errp); ++void qmp_marshal_change_backing_file(QDict *args, QObject **ret, Error **errp); ++void qmp_block_commit(const char *job_id, const char *device, const char *base_node, const char *base, const char *top_node, const char *top, const char *backing_file, bool has_backing_mask_protocol, bool backing_mask_protocol, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, const char *filter_node_name, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp); ++void qmp_marshal_block_commit(QDict *args, QObject **ret, Error **errp); ++void qmp_drive_backup(DriveBackup *arg, Error **errp); ++void qmp_marshal_drive_backup(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp); ++void qmp_marshal_blockdev_backup(QDict *args, QObject **ret, Error **errp); ++BlockDeviceInfoList *qmp_query_named_block_nodes(bool has_flat, bool flat, Error **errp); ++void qmp_marshal_query_named_block_nodes(QDict *args, QObject **ret, Error **errp); ++XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp); ++void qmp_marshal_x_debug_query_block_graph(QDict *args, QObject **ret, Error **errp); ++void qmp_drive_mirror(DriveMirror *arg, Error **errp); ++void qmp_marshal_drive_mirror(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_add(const char *node, const char *name, bool has_granularity, uint32_t granularity, bool has_persistent, bool persistent, bool has_disabled, bool disabled, Error **errp); ++void qmp_marshal_block_dirty_bitmap_add(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_remove(const char *node, const char *name, Error **errp); ++void qmp_marshal_block_dirty_bitmap_remove(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_clear(const char *node, const char *name, Error **errp); ++void qmp_marshal_block_dirty_bitmap_clear(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_enable(const char *node, const char *name, Error **errp); ++void qmp_marshal_block_dirty_bitmap_enable(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_disable(const char *node, const char *name, Error **errp); ++void qmp_marshal_block_dirty_bitmap_disable(QDict *args, QObject **ret, Error **errp); ++void qmp_block_dirty_bitmap_merge(const char *node, const char *target, BlockDirtyBitmapOrStrList *bitmaps, Error **errp); ++void qmp_marshal_block_dirty_bitmap_merge(QDict *args, QObject **ret, Error **errp); ++BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, const char *name, Error **errp); ++void qmp_marshal_x_debug_block_dirty_bitmap_sha256(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_mirror(const char *job_id, const char *device, const char *target, const char *replaces, MirrorSyncMode sync, bool has_speed, int64_t speed, bool has_granularity, uint32_t granularity, bool has_buf_size, int64_t buf_size, bool has_on_source_error, BlockdevOnError on_source_error, bool has_on_target_error, BlockdevOnError on_target_error, const char *filter_node_name, bool has_copy_mode, MirrorCopyMode copy_mode, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp); ++void qmp_marshal_blockdev_mirror(QDict *args, QObject **ret, Error **errp); ++void qmp_block_stream(const char *job_id, const char *device, const char *base, const char *base_node, const char *backing_file, bool has_backing_mask_protocol, bool backing_mask_protocol, const char *bottom, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, const char *filter_node_name, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, Error **errp); ++void qmp_marshal_block_stream(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp); ++void qmp_marshal_block_job_set_speed(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_cancel(const char *device, bool has_force, bool force, Error **errp); ++void qmp_marshal_block_job_cancel(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_pause(const char *device, Error **errp); ++void qmp_marshal_block_job_pause(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_resume(const char *device, Error **errp); ++void qmp_marshal_block_job_resume(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_complete(const char *device, Error **errp); ++void qmp_marshal_block_job_complete(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_dismiss(const char *id, Error **errp); ++void qmp_marshal_block_job_dismiss(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_finalize(const char *id, Error **errp); ++void qmp_marshal_block_job_finalize(QDict *args, QObject **ret, Error **errp); ++void qmp_block_job_change(BlockJobChangeOptions *arg, Error **errp); ++void qmp_marshal_block_job_change(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_add(BlockdevOptions *arg, Error **errp); ++void qmp_marshal_blockdev_add(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_reopen(BlockdevOptionsList *options, Error **errp); ++void qmp_marshal_blockdev_reopen(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_del(const char *node_name, Error **errp); ++void qmp_marshal_blockdev_del(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, Error **errp); ++void qmp_marshal_blockdev_create(QDict *args, QObject **ret, Error **errp); ++void qmp_x_blockdev_amend(const char *job_id, const char *node_name, BlockdevAmendOptions *options, bool has_force, bool force, Error **errp); ++void qmp_marshal_x_blockdev_amend(QDict *args, QObject **ret, Error **errp); ++void qmp_block_set_write_threshold(const char *node_name, uint64_t write_threshold, Error **errp); ++void qmp_marshal_block_set_write_threshold(QDict *args, QObject **ret, Error **errp); ++void qmp_x_blockdev_change(const char *parent, const char *child, const char *node, Error **errp); ++void qmp_marshal_x_blockdev_change(QDict *args, QObject **ret, Error **errp); ++void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, bool has_force, bool force, Error **errp); ++void qmp_marshal_x_blockdev_set_iothread(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_snapshot_internal_sync(const char *device, const char *name, Error **errp); ++void qmp_marshal_blockdev_snapshot_internal_sync(QDict *args, QObject **ret, Error **errp); ++SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, const char *id, const char *name, Error **errp); ++void qmp_marshal_blockdev_snapshot_delete_internal_sync(QDict *args, QObject **ret, Error **errp); ++ ++#endif /* QAPI_COMMANDS_BLOCK_CORE_H */ +diff --git a/include/qapi/qapi-commands-block.h b/include/qapi/qapi-commands-block.h +new file mode 100644 +index 00000000..2c1adc60 +--- /dev/null ++++ b/include/qapi/qapi-commands-block.h +@@ -0,0 +1,38 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP commands ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMMANDS_BLOCK_H ++#define QAPI_COMMANDS_BLOCK_H ++ ++#include "qapi-commands-block-core.h" ++#include "qapi-types-block.h" ++ ++PRManagerInfoList *qmp_query_pr_managers(Error **errp); ++void qmp_marshal_query_pr_managers(QDict *args, QObject **ret, Error **errp); ++void qmp_eject(const char *device, const char *id, bool has_force, bool force, Error **errp); ++void qmp_marshal_eject(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_open_tray(const char *device, const char *id, bool has_force, bool force, Error **errp); ++void qmp_marshal_blockdev_open_tray(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_close_tray(const char *device, const char *id, Error **errp); ++void qmp_marshal_blockdev_close_tray(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_remove_medium(const char *id, Error **errp); ++void qmp_marshal_blockdev_remove_medium(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_insert_medium(const char *id, const char *node_name, Error **errp); ++void qmp_marshal_blockdev_insert_medium(QDict *args, QObject **ret, Error **errp); ++void qmp_blockdev_change_medium(const char *device, const char *id, const char *filename, const char *format, bool has_force, bool force, bool has_read_only_mode, BlockdevChangeReadOnlyMode read_only_mode, Error **errp); ++void qmp_marshal_blockdev_change_medium(QDict *args, QObject **ret, Error **errp); ++void qmp_block_set_io_throttle(BlockIOThrottle *arg, Error **errp); ++void qmp_marshal_block_set_io_throttle(QDict *args, QObject **ret, Error **errp); ++void qmp_block_latency_histogram_set(const char *id, bool has_boundaries, uint64List *boundaries, bool has_boundaries_read, uint64List *boundaries_read, bool has_boundaries_write, uint64List *boundaries_write, bool has_boundaries_zap, uint64List *boundaries_zap, bool has_boundaries_flush, uint64List *boundaries_flush, Error **errp); ++void qmp_marshal_block_latency_histogram_set(QDict *args, QObject **ret, Error **errp); ++ ++#endif /* QAPI_COMMANDS_BLOCK_H */ +diff --git a/include/qapi/qapi-commands-common.h b/include/qapi/qapi-commands-common.h +new file mode 100644 +index 00000000..23b5cb75 +--- /dev/null ++++ b/include/qapi/qapi-commands-common.h +@@ -0,0 +1,19 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP commands ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMMANDS_COMMON_H ++#define QAPI_COMMANDS_COMMON_H ++ ++#include "qapi-types-common.h" ++ ++ ++#endif /* QAPI_COMMANDS_COMMON_H */ +diff --git a/include/qapi/qapi-commands-job.h b/include/qapi/qapi-commands-job.h +new file mode 100644 +index 00000000..9783650f +--- /dev/null ++++ b/include/qapi/qapi-commands-job.h +@@ -0,0 +1,33 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP commands ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_COMMANDS_JOB_H ++#define QAPI_COMMANDS_JOB_H ++ ++#include "qapi-types-job.h" ++ ++void qmp_job_pause(const char *id, Error **errp); ++void qmp_marshal_job_pause(QDict *args, QObject **ret, Error **errp); ++void qmp_job_resume(const char *id, Error **errp); ++void qmp_marshal_job_resume(QDict *args, QObject **ret, Error **errp); ++void qmp_job_cancel(const char *id, Error **errp); ++void qmp_marshal_job_cancel(QDict *args, QObject **ret, Error **errp); ++void qmp_job_complete(const char *id, Error **errp); ++void qmp_marshal_job_complete(QDict *args, QObject **ret, Error **errp); ++void qmp_job_dismiss(const char *id, Error **errp); ++void qmp_marshal_job_dismiss(QDict *args, QObject **ret, Error **errp); ++void qmp_job_finalize(const char *id, Error **errp); ++void qmp_marshal_job_finalize(QDict *args, QObject **ret, Error **errp); ++JobInfoList *qmp_query_jobs(Error **errp); ++void qmp_marshal_query_jobs(QDict *args, QObject **ret, Error **errp); ++ ++#endif /* QAPI_COMMANDS_JOB_H */ +diff --git a/include/qapi/qapi-events-block-core.h b/include/qapi/qapi-events-block-core.h +new file mode 100644 +index 00000000..7f12beff +--- /dev/null ++++ b/include/qapi/qapi-events-block-core.h +@@ -0,0 +1,43 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_EVENTS_BLOCK_CORE_H ++#define QAPI_EVENTS_BLOCK_CORE_H ++ ++#include "qapi-events-common.h" ++#include "qapi-events-crypto.h" ++#include "qapi-events-job.h" ++#include "qapi-events-sockets.h" ++#include "qapi/util.h" ++#include "qapi-types-block-core.h" ++ ++void qapi_event_send_block_image_corrupted(const char *device, const char *node_name, const char *msg, bool has_offset, int64_t offset, bool has_size, int64_t size, bool fatal); ++ ++void qapi_event_send_block_io_error(const char *device, const char *node_name, IoOperationType operation, BlockErrorAction action, bool has_nospace, bool nospace, const char *reason); ++ ++void qapi_event_send_block_job_completed(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed, const char *error); ++ ++void qapi_event_send_block_job_cancelled(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed); ++ ++void qapi_event_send_block_job_error(const char *device, IoOperationType operation, BlockErrorAction action); ++ ++void qapi_event_send_block_job_ready(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed); ++ ++void qapi_event_send_block_job_pending(JobType type, const char *id); ++ ++void qapi_event_send_block_write_threshold(const char *node_name, uint64_t amount_exceeded, uint64_t write_threshold); ++ ++void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num, int64_t sectors_count); ++ ++void qapi_event_send_quorum_report_bad(QuorumOpType type, const char *error, const char *node_name, int64_t sector_num, int64_t sectors_count); ++ ++#endif /* QAPI_EVENTS_BLOCK_CORE_H */ +diff --git a/include/qapi/qapi-events-common.h b/include/qapi/qapi-events-common.h +new file mode 100644 +index 00000000..4c3e4790 +--- /dev/null ++++ b/include/qapi/qapi-events-common.h +@@ -0,0 +1,19 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_EVENTS_COMMON_H ++#define QAPI_EVENTS_COMMON_H ++ ++#include "qapi/util.h" ++#include "qapi-types-common.h" ++ ++#endif /* QAPI_EVENTS_COMMON_H */ +diff --git a/include/qapi/qapi-events-job.h b/include/qapi/qapi-events-job.h +new file mode 100644 +index 00000000..09a6dc06 +--- /dev/null ++++ b/include/qapi/qapi-events-job.h +@@ -0,0 +1,21 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_EVENTS_JOB_H ++#define QAPI_EVENTS_JOB_H ++ ++#include "qapi/util.h" ++#include "qapi-types-job.h" ++ ++void qapi_event_send_job_status_change(const char *id, JobStatus status); ++ ++#endif /* QAPI_EVENTS_JOB_H */ +diff --git a/include/qapi/qapi-types-block-core.h b/include/qapi/qapi-types-block-core.h +new file mode 100644 +index 00000000..26585893 +--- /dev/null ++++ b/include/qapi/qapi-types-block-core.h +@@ -0,0 +1,3626 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_BLOCK_CORE_H ++#define QAPI_TYPES_BLOCK_CORE_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qapi-types-common.h" ++#include "qapi-types-crypto.h" ++#include "qapi-types-job.h" ++#include "qapi-types-sockets.h" ++ ++typedef struct SnapshotInfo SnapshotInfo; ++ ++typedef struct ImageInfoSpecificQCow2EncryptionBase ImageInfoSpecificQCow2EncryptionBase; ++ ++typedef struct ImageInfoSpecificQCow2Encryption ImageInfoSpecificQCow2Encryption; ++ ++typedef struct Qcow2BitmapInfoList Qcow2BitmapInfoList; ++ ++typedef struct ImageInfoSpecificQCow2 ImageInfoSpecificQCow2; ++ ++typedef struct VmdkExtentInfoList VmdkExtentInfoList; ++ ++typedef struct ImageInfoSpecificVmdk ImageInfoSpecificVmdk; ++ ++typedef struct VmdkExtentInfo VmdkExtentInfo; ++ ++typedef struct ImageInfoSpecificRbd ImageInfoSpecificRbd; ++ ++typedef struct ImageInfoSpecificFile ImageInfoSpecificFile; ++ ++typedef enum ImageInfoSpecificKind { ++ IMAGE_INFO_SPECIFIC_KIND_QCOW2, ++ IMAGE_INFO_SPECIFIC_KIND_VMDK, ++ IMAGE_INFO_SPECIFIC_KIND_LUKS, ++ IMAGE_INFO_SPECIFIC_KIND_RBD, ++ IMAGE_INFO_SPECIFIC_KIND_FILE, ++ IMAGE_INFO_SPECIFIC_KIND__MAX, ++} ImageInfoSpecificKind; ++ ++#define ImageInfoSpecificKind_str(val) \ ++ qapi_enum_lookup(&ImageInfoSpecificKind_lookup, (val)) ++ ++extern const QEnumLookup ImageInfoSpecificKind_lookup; ++ ++typedef struct ImageInfoSpecificQCow2Wrapper ImageInfoSpecificQCow2Wrapper; ++ ++typedef struct ImageInfoSpecificVmdkWrapper ImageInfoSpecificVmdkWrapper; ++ ++typedef struct ImageInfoSpecificLUKSWrapper ImageInfoSpecificLUKSWrapper; ++ ++typedef struct ImageInfoSpecificRbdWrapper ImageInfoSpecificRbdWrapper; ++ ++typedef struct ImageInfoSpecificFileWrapper ImageInfoSpecificFileWrapper; ++ ++typedef struct q_obj_ImageInfoSpecific_base q_obj_ImageInfoSpecific_base; ++ ++typedef struct ImageInfoSpecific ImageInfoSpecific; ++ ++typedef struct SnapshotInfoList SnapshotInfoList; ++ ++typedef struct BlockNodeInfo BlockNodeInfo; ++ ++typedef struct ImageInfo ImageInfo; ++ ++typedef struct BlockChildInfo BlockChildInfo; ++ ++typedef struct BlockChildInfoList BlockChildInfoList; ++ ++typedef struct BlockGraphInfo BlockGraphInfo; ++ ++typedef struct ImageCheck ImageCheck; ++ ++typedef struct MapEntry MapEntry; ++ ++typedef struct BlockdevCacheInfo BlockdevCacheInfo; ++ ++typedef struct BlockDirtyInfoList BlockDirtyInfoList; ++ ++typedef struct BlockDeviceInfo BlockDeviceInfo; ++ ++typedef enum BlockDeviceIoStatus { ++ BLOCK_DEVICE_IO_STATUS_OK, ++ BLOCK_DEVICE_IO_STATUS_FAILED, ++ BLOCK_DEVICE_IO_STATUS_NOSPACE, ++ BLOCK_DEVICE_IO_STATUS__MAX, ++} BlockDeviceIoStatus; ++ ++#define BlockDeviceIoStatus_str(val) \ ++ qapi_enum_lookup(&BlockDeviceIoStatus_lookup, (val)) ++ ++extern const QEnumLookup BlockDeviceIoStatus_lookup; ++ ++typedef struct BlockDirtyInfo BlockDirtyInfo; ++ ++typedef enum Qcow2BitmapInfoFlags { ++ QCOW2_BITMAP_INFO_FLAGS_IN_USE, ++ QCOW2_BITMAP_INFO_FLAGS_AUTO, ++ QCOW2_BITMAP_INFO_FLAGS__MAX, ++} Qcow2BitmapInfoFlags; ++ ++#define Qcow2BitmapInfoFlags_str(val) \ ++ qapi_enum_lookup(&Qcow2BitmapInfoFlags_lookup, (val)) ++ ++extern const QEnumLookup Qcow2BitmapInfoFlags_lookup; ++ ++typedef struct Qcow2BitmapInfoFlagsList Qcow2BitmapInfoFlagsList; ++ ++typedef struct Qcow2BitmapInfo Qcow2BitmapInfo; ++ ++typedef struct BlockLatencyHistogramInfo BlockLatencyHistogramInfo; ++ ++typedef struct BlockInfo BlockInfo; ++ ++typedef struct BlockMeasureInfo BlockMeasureInfo; ++ ++typedef struct BlockInfoList BlockInfoList; ++ ++typedef struct BlockDeviceTimedStats BlockDeviceTimedStats; ++ ++typedef struct BlockDeviceTimedStatsList BlockDeviceTimedStatsList; ++ ++typedef struct BlockDeviceStats BlockDeviceStats; ++ ++typedef struct BlockStatsSpecificFile BlockStatsSpecificFile; ++ ++typedef struct BlockStatsSpecificNvme BlockStatsSpecificNvme; ++ ++typedef struct q_obj_BlockStatsSpecific_base q_obj_BlockStatsSpecific_base; ++ ++typedef struct BlockStatsSpecific BlockStatsSpecific; ++ ++typedef struct BlockStats BlockStats; ++ ++typedef struct q_obj_query_blockstats_arg q_obj_query_blockstats_arg; ++ ++typedef struct BlockStatsList BlockStatsList; ++ ++typedef enum BlockdevOnError { ++ BLOCKDEV_ON_ERROR_REPORT, ++ BLOCKDEV_ON_ERROR_IGNORE, ++ BLOCKDEV_ON_ERROR_ENOSPC, ++ BLOCKDEV_ON_ERROR_STOP, ++ BLOCKDEV_ON_ERROR_AUTO, ++ BLOCKDEV_ON_ERROR__MAX, ++} BlockdevOnError; ++ ++#define BlockdevOnError_str(val) \ ++ qapi_enum_lookup(&BlockdevOnError_lookup, (val)) ++ ++extern const QEnumLookup BlockdevOnError_lookup; ++ ++typedef enum MirrorSyncMode { ++ MIRROR_SYNC_MODE_TOP, ++ MIRROR_SYNC_MODE_FULL, ++ MIRROR_SYNC_MODE_NONE, ++ MIRROR_SYNC_MODE_INCREMENTAL, ++ MIRROR_SYNC_MODE_BITMAP, ++ MIRROR_SYNC_MODE__MAX, ++} MirrorSyncMode; ++ ++#define MirrorSyncMode_str(val) \ ++ qapi_enum_lookup(&MirrorSyncMode_lookup, (val)) ++ ++extern const QEnumLookup MirrorSyncMode_lookup; ++ ++typedef enum BitmapSyncMode { ++ BITMAP_SYNC_MODE_ON_SUCCESS, ++ BITMAP_SYNC_MODE_NEVER, ++ BITMAP_SYNC_MODE_ALWAYS, ++ BITMAP_SYNC_MODE__MAX, ++} BitmapSyncMode; ++ ++#define BitmapSyncMode_str(val) \ ++ qapi_enum_lookup(&BitmapSyncMode_lookup, (val)) ++ ++extern const QEnumLookup BitmapSyncMode_lookup; ++ ++typedef enum MirrorCopyMode { ++ MIRROR_COPY_MODE_BACKGROUND, ++ MIRROR_COPY_MODE_WRITE_BLOCKING, ++ MIRROR_COPY_MODE__MAX, ++} MirrorCopyMode; ++ ++#define MirrorCopyMode_str(val) \ ++ qapi_enum_lookup(&MirrorCopyMode_lookup, (val)) ++ ++extern const QEnumLookup MirrorCopyMode_lookup; ++ ++typedef struct BlockJobInfoMirror BlockJobInfoMirror; ++ ++typedef struct q_obj_BlockJobInfo_base q_obj_BlockJobInfo_base; ++ ++typedef struct BlockJobInfo BlockJobInfo; ++ ++typedef struct BlockJobInfoList BlockJobInfoList; ++ ++typedef struct q_obj_block_resize_arg q_obj_block_resize_arg; ++ ++typedef enum NewImageMode { ++ NEW_IMAGE_MODE_EXISTING, ++ NEW_IMAGE_MODE_ABSOLUTE_PATHS, ++ NEW_IMAGE_MODE__MAX, ++} NewImageMode; ++ ++#define NewImageMode_str(val) \ ++ qapi_enum_lookup(&NewImageMode_lookup, (val)) ++ ++extern const QEnumLookup NewImageMode_lookup; ++ ++typedef struct BlockdevSnapshotSync BlockdevSnapshotSync; ++ ++typedef struct BlockdevSnapshot BlockdevSnapshot; ++ ++typedef struct BackupPerf BackupPerf; ++ ++typedef struct BackupCommon BackupCommon; ++ ++typedef struct DriveBackup DriveBackup; ++ ++typedef struct BlockdevBackup BlockdevBackup; ++ ++typedef struct q_obj_change_backing_file_arg q_obj_change_backing_file_arg; ++ ++typedef struct q_obj_block_commit_arg q_obj_block_commit_arg; ++ ++typedef struct q_obj_query_named_block_nodes_arg q_obj_query_named_block_nodes_arg; ++ ++typedef struct BlockDeviceInfoList BlockDeviceInfoList; ++ ++typedef enum XDbgBlockGraphNodeType { ++ X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, ++ X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, ++ X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, ++ X_DBG_BLOCK_GRAPH_NODE_TYPE__MAX, ++} XDbgBlockGraphNodeType; ++ ++#define XDbgBlockGraphNodeType_str(val) \ ++ qapi_enum_lookup(&XDbgBlockGraphNodeType_lookup, (val)) ++ ++extern const QEnumLookup XDbgBlockGraphNodeType_lookup; ++ ++typedef struct XDbgBlockGraphNode XDbgBlockGraphNode; ++ ++typedef enum BlockPermission { ++ BLOCK_PERMISSION_CONSISTENT_READ, ++ BLOCK_PERMISSION_WRITE, ++ BLOCK_PERMISSION_WRITE_UNCHANGED, ++ BLOCK_PERMISSION_RESIZE, ++ BLOCK_PERMISSION__MAX, ++} BlockPermission; ++ ++#define BlockPermission_str(val) \ ++ qapi_enum_lookup(&BlockPermission_lookup, (val)) ++ ++extern const QEnumLookup BlockPermission_lookup; ++ ++typedef struct BlockPermissionList BlockPermissionList; ++ ++typedef struct XDbgBlockGraphEdge XDbgBlockGraphEdge; ++ ++typedef struct XDbgBlockGraphNodeList XDbgBlockGraphNodeList; ++ ++typedef struct XDbgBlockGraphEdgeList XDbgBlockGraphEdgeList; ++ ++typedef struct XDbgBlockGraph XDbgBlockGraph; ++ ++typedef struct DriveMirror DriveMirror; ++ ++typedef struct BlockDirtyBitmap BlockDirtyBitmap; ++ ++typedef struct BlockDirtyBitmapAdd BlockDirtyBitmapAdd; ++ ++typedef struct BlockDirtyBitmapOrStr BlockDirtyBitmapOrStr; ++ ++typedef struct BlockDirtyBitmapOrStrList BlockDirtyBitmapOrStrList; ++ ++typedef struct BlockDirtyBitmapMerge BlockDirtyBitmapMerge; ++ ++typedef struct BlockDirtyBitmapSha256 BlockDirtyBitmapSha256; ++ ++typedef struct q_obj_blockdev_mirror_arg q_obj_blockdev_mirror_arg; ++ ++typedef struct BlockIOThrottle BlockIOThrottle; ++ ++typedef struct ThrottleLimits ThrottleLimits; ++ ++typedef struct ThrottleGroupProperties ThrottleGroupProperties; ++ ++typedef struct q_obj_block_stream_arg q_obj_block_stream_arg; ++ ++typedef struct q_obj_block_job_set_speed_arg q_obj_block_job_set_speed_arg; ++ ++typedef struct q_obj_block_job_cancel_arg q_obj_block_job_cancel_arg; ++ ++typedef struct q_obj_block_job_pause_arg q_obj_block_job_pause_arg; ++ ++typedef struct q_obj_block_job_resume_arg q_obj_block_job_resume_arg; ++ ++typedef struct q_obj_block_job_complete_arg q_obj_block_job_complete_arg; ++ ++typedef struct q_obj_block_job_dismiss_arg q_obj_block_job_dismiss_arg; ++ ++typedef struct q_obj_block_job_finalize_arg q_obj_block_job_finalize_arg; ++ ++typedef struct BlockJobChangeOptionsMirror BlockJobChangeOptionsMirror; ++ ++typedef struct q_obj_BlockJobChangeOptions_base q_obj_BlockJobChangeOptions_base; ++ ++typedef struct BlockJobChangeOptions BlockJobChangeOptions; ++ ++typedef enum BlockdevDiscardOptions { ++ BLOCKDEV_DISCARD_OPTIONS_IGNORE, ++ BLOCKDEV_DISCARD_OPTIONS_UNMAP, ++ BLOCKDEV_DISCARD_OPTIONS__MAX, ++} BlockdevDiscardOptions; ++ ++#define BlockdevDiscardOptions_str(val) \ ++ qapi_enum_lookup(&BlockdevDiscardOptions_lookup, (val)) ++ ++extern const QEnumLookup BlockdevDiscardOptions_lookup; ++ ++typedef enum BlockdevDetectZeroesOptions { ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_ON, ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP, ++ BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX, ++} BlockdevDetectZeroesOptions; ++ ++#define BlockdevDetectZeroesOptions_str(val) \ ++ qapi_enum_lookup(&BlockdevDetectZeroesOptions_lookup, (val)) ++ ++extern const QEnumLookup BlockdevDetectZeroesOptions_lookup; ++ ++typedef enum BlockdevAioOptions { ++ BLOCKDEV_AIO_OPTIONS_THREADS, ++ BLOCKDEV_AIO_OPTIONS_NATIVE, ++#if defined(CONFIG_LINUX_IO_URING) ++ BLOCKDEV_AIO_OPTIONS_IO_URING, ++#endif /* defined(CONFIG_LINUX_IO_URING) */ ++ BLOCKDEV_AIO_OPTIONS__MAX, ++} BlockdevAioOptions; ++ ++#define BlockdevAioOptions_str(val) \ ++ qapi_enum_lookup(&BlockdevAioOptions_lookup, (val)) ++ ++extern const QEnumLookup BlockdevAioOptions_lookup; ++ ++typedef struct BlockdevCacheOptions BlockdevCacheOptions; ++ ++typedef enum BlockdevDriver { ++ BLOCKDEV_DRIVER_BLKDEBUG, ++ BLOCKDEV_DRIVER_BLKLOGWRITES, ++ BLOCKDEV_DRIVER_BLKREPLAY, ++ BLOCKDEV_DRIVER_BLKVERIFY, ++ BLOCKDEV_DRIVER_BOCHS, ++ BLOCKDEV_DRIVER_CLOOP, ++ BLOCKDEV_DRIVER_COMPRESS, ++ BLOCKDEV_DRIVER_COPY_BEFORE_WRITE, ++ BLOCKDEV_DRIVER_COPY_ON_READ, ++ BLOCKDEV_DRIVER_DMG, ++ BLOCKDEV_DRIVER_FILE, ++ BLOCKDEV_DRIVER_SNAPSHOT_ACCESS, ++ BLOCKDEV_DRIVER_FTP, ++ BLOCKDEV_DRIVER_FTPS, ++ BLOCKDEV_DRIVER_GLUSTER, ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BLOCKDEV_DRIVER_HOST_CDROM, ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BLOCKDEV_DRIVER_HOST_DEVICE, ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ BLOCKDEV_DRIVER_HTTP, ++ BLOCKDEV_DRIVER_HTTPS, ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_IO_URING, ++#endif /* defined(CONFIG_BLKIO) */ ++ BLOCKDEV_DRIVER_ISCSI, ++ BLOCKDEV_DRIVER_LUKS, ++ BLOCKDEV_DRIVER_NBD, ++ BLOCKDEV_DRIVER_NFS, ++ BLOCKDEV_DRIVER_NULL_AIO, ++ BLOCKDEV_DRIVER_NULL_CO, ++ BLOCKDEV_DRIVER_NVME, ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_NVME_IO_URING, ++#endif /* defined(CONFIG_BLKIO) */ ++ BLOCKDEV_DRIVER_PARALLELS, ++ BLOCKDEV_DRIVER_PREALLOCATE, ++ BLOCKDEV_DRIVER_QCOW, ++ BLOCKDEV_DRIVER_QCOW2, ++ BLOCKDEV_DRIVER_QED, ++ BLOCKDEV_DRIVER_QUORUM, ++ BLOCKDEV_DRIVER_RAW, ++ BLOCKDEV_DRIVER_RBD, ++#if defined(CONFIG_REPLICATION) ++ BLOCKDEV_DRIVER_REPLICATION, ++#endif /* defined(CONFIG_REPLICATION) */ ++ BLOCKDEV_DRIVER_SSH, ++ BLOCKDEV_DRIVER_THROTTLE, ++ BLOCKDEV_DRIVER_VDI, ++ BLOCKDEV_DRIVER_VHDX, ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI, ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER, ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA, ++#endif /* defined(CONFIG_BLKIO) */ ++ BLOCKDEV_DRIVER_VMDK, ++ BLOCKDEV_DRIVER_VPC, ++ BLOCKDEV_DRIVER_VVFAT, ++ BLOCKDEV_DRIVER__MAX, ++} BlockdevDriver; ++ ++#define BlockdevDriver_str(val) \ ++ qapi_enum_lookup(&BlockdevDriver_lookup, (val)) ++ ++extern const QEnumLookup BlockdevDriver_lookup; ++ ++typedef struct BlockdevOptionsFile BlockdevOptionsFile; ++ ++typedef struct BlockdevOptionsNull BlockdevOptionsNull; ++ ++typedef struct BlockdevOptionsNVMe BlockdevOptionsNVMe; ++ ++typedef struct BlockdevOptionsVVFAT BlockdevOptionsVVFAT; ++ ++typedef struct BlockdevOptionsGenericFormat BlockdevOptionsGenericFormat; ++ ++typedef struct BlockdevOptionsLUKS BlockdevOptionsLUKS; ++ ++typedef struct BlockdevOptionsGenericCOWFormat BlockdevOptionsGenericCOWFormat; ++ ++typedef enum Qcow2OverlapCheckMode { ++ QCOW2_OVERLAP_CHECK_MODE_NONE, ++ QCOW2_OVERLAP_CHECK_MODE_CONSTANT, ++ QCOW2_OVERLAP_CHECK_MODE_CACHED, ++ QCOW2_OVERLAP_CHECK_MODE_ALL, ++ QCOW2_OVERLAP_CHECK_MODE__MAX, ++} Qcow2OverlapCheckMode; ++ ++#define Qcow2OverlapCheckMode_str(val) \ ++ qapi_enum_lookup(&Qcow2OverlapCheckMode_lookup, (val)) ++ ++extern const QEnumLookup Qcow2OverlapCheckMode_lookup; ++ ++typedef struct Qcow2OverlapCheckFlags Qcow2OverlapCheckFlags; ++ ++typedef struct Qcow2OverlapChecks Qcow2OverlapChecks; ++ ++typedef enum BlockdevQcowEncryptionFormat { ++ BLOCKDEV_QCOW_ENCRYPTION_FORMAT_AES, ++ BLOCKDEV_QCOW_ENCRYPTION_FORMAT__MAX, ++} BlockdevQcowEncryptionFormat; ++ ++#define BlockdevQcowEncryptionFormat_str(val) \ ++ qapi_enum_lookup(&BlockdevQcowEncryptionFormat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevQcowEncryptionFormat_lookup; ++ ++typedef struct q_obj_BlockdevQcowEncryption_base q_obj_BlockdevQcowEncryption_base; ++ ++typedef struct BlockdevQcowEncryption BlockdevQcowEncryption; ++ ++typedef struct BlockdevOptionsQcow BlockdevOptionsQcow; ++ ++typedef enum BlockdevQcow2EncryptionFormat { ++ BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES, ++ BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS, ++ BLOCKDEV_QCOW2_ENCRYPTION_FORMAT__MAX, ++} BlockdevQcow2EncryptionFormat; ++ ++#define BlockdevQcow2EncryptionFormat_str(val) \ ++ qapi_enum_lookup(&BlockdevQcow2EncryptionFormat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevQcow2EncryptionFormat_lookup; ++ ++typedef struct q_obj_BlockdevQcow2Encryption_base q_obj_BlockdevQcow2Encryption_base; ++ ++typedef struct BlockdevQcow2Encryption BlockdevQcow2Encryption; ++ ++typedef struct BlockdevOptionsPreallocate BlockdevOptionsPreallocate; ++ ++typedef struct BlockdevOptionsQcow2 BlockdevOptionsQcow2; ++ ++typedef enum SshHostKeyCheckMode { ++ SSH_HOST_KEY_CHECK_MODE_NONE, ++ SSH_HOST_KEY_CHECK_MODE_HASH, ++ SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS, ++ SSH_HOST_KEY_CHECK_MODE__MAX, ++} SshHostKeyCheckMode; ++ ++#define SshHostKeyCheckMode_str(val) \ ++ qapi_enum_lookup(&SshHostKeyCheckMode_lookup, (val)) ++ ++extern const QEnumLookup SshHostKeyCheckMode_lookup; ++ ++typedef enum SshHostKeyCheckHashType { ++ SSH_HOST_KEY_CHECK_HASH_TYPE_MD5, ++ SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1, ++ SSH_HOST_KEY_CHECK_HASH_TYPE_SHA256, ++ SSH_HOST_KEY_CHECK_HASH_TYPE__MAX, ++} SshHostKeyCheckHashType; ++ ++#define SshHostKeyCheckHashType_str(val) \ ++ qapi_enum_lookup(&SshHostKeyCheckHashType_lookup, (val)) ++ ++extern const QEnumLookup SshHostKeyCheckHashType_lookup; ++ ++typedef struct SshHostKeyHash SshHostKeyHash; ++ ++typedef struct q_obj_SshHostKeyCheck_base q_obj_SshHostKeyCheck_base; ++ ++typedef struct SshHostKeyCheck SshHostKeyCheck; ++ ++typedef struct BlockdevOptionsSsh BlockdevOptionsSsh; ++ ++typedef enum BlkdebugEvent { ++ BLKDBG_L1_UPDATE, ++ BLKDBG_L1_GROW_ALLOC_TABLE, ++ BLKDBG_L1_GROW_WRITE_TABLE, ++ BLKDBG_L1_GROW_ACTIVATE_TABLE, ++ BLKDBG_L2_LOAD, ++ BLKDBG_L2_UPDATE, ++ BLKDBG_L2_UPDATE_COMPRESSED, ++ BLKDBG_L2_ALLOC_COW_READ, ++ BLKDBG_L2_ALLOC_WRITE, ++ BLKDBG_READ_AIO, ++ BLKDBG_READ_BACKING_AIO, ++ BLKDBG_READ_COMPRESSED, ++ BLKDBG_WRITE_AIO, ++ BLKDBG_WRITE_COMPRESSED, ++ BLKDBG_VMSTATE_LOAD, ++ BLKDBG_VMSTATE_SAVE, ++ BLKDBG_COW_READ, ++ BLKDBG_COW_WRITE, ++ BLKDBG_REFTABLE_LOAD, ++ BLKDBG_REFTABLE_GROW, ++ BLKDBG_REFTABLE_UPDATE, ++ BLKDBG_REFBLOCK_LOAD, ++ BLKDBG_REFBLOCK_UPDATE, ++ BLKDBG_REFBLOCK_UPDATE_PART, ++ BLKDBG_REFBLOCK_ALLOC, ++ BLKDBG_REFBLOCK_ALLOC_HOOKUP, ++ BLKDBG_REFBLOCK_ALLOC_WRITE, ++ BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS, ++ BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE, ++ BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE, ++ BLKDBG_CLUSTER_ALLOC, ++ BLKDBG_CLUSTER_ALLOC_BYTES, ++ BLKDBG_CLUSTER_FREE, ++ BLKDBG_FLUSH_TO_OS, ++ BLKDBG_FLUSH_TO_DISK, ++ BLKDBG_PWRITEV_RMW_HEAD, ++ BLKDBG_PWRITEV_RMW_AFTER_HEAD, ++ BLKDBG_PWRITEV_RMW_TAIL, ++ BLKDBG_PWRITEV_RMW_AFTER_TAIL, ++ BLKDBG_PWRITEV, ++ BLKDBG_PWRITEV_ZERO, ++ BLKDBG_PWRITEV_DONE, ++ BLKDBG_EMPTY_IMAGE_PREPARE, ++ BLKDBG_L1_SHRINK_WRITE_TABLE, ++ BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS, ++ BLKDBG_COR_WRITE, ++ BLKDBG_CLUSTER_ALLOC_SPACE, ++ BLKDBG_NONE, ++ BLKDBG__MAX, ++} BlkdebugEvent; ++ ++#define BlkdebugEvent_str(val) \ ++ qapi_enum_lookup(&BlkdebugEvent_lookup, (val)) ++ ++extern const QEnumLookup BlkdebugEvent_lookup; ++ ++typedef enum BlkdebugIOType { ++ BLKDEBUG_IO_TYPE_READ, ++ BLKDEBUG_IO_TYPE_WRITE, ++ BLKDEBUG_IO_TYPE_WRITE_ZEROES, ++ BLKDEBUG_IO_TYPE_DISCARD, ++ BLKDEBUG_IO_TYPE_FLUSH, ++ BLKDEBUG_IO_TYPE_BLOCK_STATUS, ++ BLKDEBUG_IO_TYPE__MAX, ++} BlkdebugIOType; ++ ++#define BlkdebugIOType_str(val) \ ++ qapi_enum_lookup(&BlkdebugIOType_lookup, (val)) ++ ++extern const QEnumLookup BlkdebugIOType_lookup; ++ ++typedef struct BlkdebugInjectErrorOptions BlkdebugInjectErrorOptions; ++ ++typedef struct BlkdebugSetStateOptions BlkdebugSetStateOptions; ++ ++typedef struct BlkdebugInjectErrorOptionsList BlkdebugInjectErrorOptionsList; ++ ++typedef struct BlkdebugSetStateOptionsList BlkdebugSetStateOptionsList; ++ ++typedef struct BlockdevOptionsBlkdebug BlockdevOptionsBlkdebug; ++ ++typedef struct BlockdevOptionsBlklogwrites BlockdevOptionsBlklogwrites; ++ ++typedef struct BlockdevOptionsBlkverify BlockdevOptionsBlkverify; ++ ++typedef struct BlockdevOptionsBlkreplay BlockdevOptionsBlkreplay; ++ ++typedef enum QuorumReadPattern { ++ QUORUM_READ_PATTERN_QUORUM, ++ QUORUM_READ_PATTERN_FIFO, ++ QUORUM_READ_PATTERN__MAX, ++} QuorumReadPattern; ++ ++#define QuorumReadPattern_str(val) \ ++ qapi_enum_lookup(&QuorumReadPattern_lookup, (val)) ++ ++extern const QEnumLookup QuorumReadPattern_lookup; ++ ++typedef struct BlockdevRefList BlockdevRefList; ++ ++typedef struct BlockdevOptionsQuorum BlockdevOptionsQuorum; ++ ++typedef struct BlockdevOptionsGluster BlockdevOptionsGluster; ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsIoUring BlockdevOptionsIoUring; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsNvmeIoUring BlockdevOptionsNvmeIoUring; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsVirtioBlkVfioPci BlockdevOptionsVirtioBlkVfioPci; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsVirtioBlkVhostUser BlockdevOptionsVirtioBlkVhostUser; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++typedef struct BlockdevOptionsVirtioBlkVhostVdpa BlockdevOptionsVirtioBlkVhostVdpa; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++typedef enum IscsiTransport { ++ ISCSI_TRANSPORT_TCP, ++ ISCSI_TRANSPORT_ISER, ++ ISCSI_TRANSPORT__MAX, ++} IscsiTransport; ++ ++#define IscsiTransport_str(val) \ ++ qapi_enum_lookup(&IscsiTransport_lookup, (val)) ++ ++extern const QEnumLookup IscsiTransport_lookup; ++ ++typedef enum IscsiHeaderDigest { ++ QAPI_ISCSI_HEADER_DIGEST_CRC32C, ++ QAPI_ISCSI_HEADER_DIGEST_NONE, ++ QAPI_ISCSI_HEADER_DIGEST_CRC32C_NONE, ++ QAPI_ISCSI_HEADER_DIGEST_NONE_CRC32C, ++ QAPI_ISCSI_HEADER_DIGEST__MAX, ++} IscsiHeaderDigest; ++ ++#define IscsiHeaderDigest_str(val) \ ++ qapi_enum_lookup(&IscsiHeaderDigest_lookup, (val)) ++ ++extern const QEnumLookup IscsiHeaderDigest_lookup; ++ ++typedef struct BlockdevOptionsIscsi BlockdevOptionsIscsi; ++ ++typedef enum RbdAuthMode { ++ RBD_AUTH_MODE_CEPHX, ++ RBD_AUTH_MODE_NONE, ++ RBD_AUTH_MODE__MAX, ++} RbdAuthMode; ++ ++#define RbdAuthMode_str(val) \ ++ qapi_enum_lookup(&RbdAuthMode_lookup, (val)) ++ ++extern const QEnumLookup RbdAuthMode_lookup; ++ ++typedef enum RbdImageEncryptionFormat { ++ RBD_IMAGE_ENCRYPTION_FORMAT_LUKS, ++ RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2, ++ RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY, ++ RBD_IMAGE_ENCRYPTION_FORMAT__MAX, ++} RbdImageEncryptionFormat; ++ ++#define RbdImageEncryptionFormat_str(val) \ ++ qapi_enum_lookup(&RbdImageEncryptionFormat_lookup, (val)) ++ ++extern const QEnumLookup RbdImageEncryptionFormat_lookup; ++ ++typedef struct RbdEncryptionOptionsLUKSBase RbdEncryptionOptionsLUKSBase; ++ ++typedef struct RbdEncryptionCreateOptionsLUKSBase RbdEncryptionCreateOptionsLUKSBase; ++ ++typedef struct RbdEncryptionOptionsLUKS RbdEncryptionOptionsLUKS; ++ ++typedef struct RbdEncryptionOptionsLUKS2 RbdEncryptionOptionsLUKS2; ++ ++typedef struct RbdEncryptionOptionsLUKSAny RbdEncryptionOptionsLUKSAny; ++ ++typedef struct RbdEncryptionCreateOptionsLUKS RbdEncryptionCreateOptionsLUKS; ++ ++typedef struct RbdEncryptionCreateOptionsLUKS2 RbdEncryptionCreateOptionsLUKS2; ++ ++typedef struct q_obj_RbdEncryptionOptions_base q_obj_RbdEncryptionOptions_base; ++ ++typedef struct RbdEncryptionOptions RbdEncryptionOptions; ++ ++typedef struct q_obj_RbdEncryptionCreateOptions_base q_obj_RbdEncryptionCreateOptions_base; ++ ++typedef struct RbdEncryptionCreateOptions RbdEncryptionCreateOptions; ++ ++typedef struct RbdAuthModeList RbdAuthModeList; ++ ++typedef struct BlockdevOptionsRbd BlockdevOptionsRbd; ++ ++#if defined(CONFIG_REPLICATION) ++typedef enum ReplicationMode { ++ REPLICATION_MODE_PRIMARY, ++ REPLICATION_MODE_SECONDARY, ++ REPLICATION_MODE__MAX, ++} ReplicationMode; ++ ++#define ReplicationMode_str(val) \ ++ qapi_enum_lookup(&ReplicationMode_lookup, (val)) ++ ++extern const QEnumLookup ReplicationMode_lookup; ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++typedef struct BlockdevOptionsReplication BlockdevOptionsReplication; ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++typedef enum NFSTransport { ++ NFS_TRANSPORT_INET, ++ NFS_TRANSPORT__MAX, ++} NFSTransport; ++ ++#define NFSTransport_str(val) \ ++ qapi_enum_lookup(&NFSTransport_lookup, (val)) ++ ++extern const QEnumLookup NFSTransport_lookup; ++ ++typedef struct NFSServer NFSServer; ++ ++typedef struct BlockdevOptionsNfs BlockdevOptionsNfs; ++ ++typedef struct BlockdevOptionsCurlBase BlockdevOptionsCurlBase; ++ ++typedef struct BlockdevOptionsCurlHttp BlockdevOptionsCurlHttp; ++ ++typedef struct BlockdevOptionsCurlHttps BlockdevOptionsCurlHttps; ++ ++typedef struct BlockdevOptionsCurlFtp BlockdevOptionsCurlFtp; ++ ++typedef struct BlockdevOptionsCurlFtps BlockdevOptionsCurlFtps; ++ ++typedef struct BlockdevOptionsNbd BlockdevOptionsNbd; ++ ++typedef struct BlockdevOptionsRaw BlockdevOptionsRaw; ++ ++typedef struct BlockdevOptionsThrottle BlockdevOptionsThrottle; ++ ++typedef struct BlockdevOptionsCor BlockdevOptionsCor; ++ ++typedef enum OnCbwError { ++ ON_CBW_ERROR_BREAK_GUEST_WRITE, ++ ON_CBW_ERROR_BREAK_SNAPSHOT, ++ ON_CBW_ERROR__MAX, ++} OnCbwError; ++ ++#define OnCbwError_str(val) \ ++ qapi_enum_lookup(&OnCbwError_lookup, (val)) ++ ++extern const QEnumLookup OnCbwError_lookup; ++ ++typedef struct BlockdevOptionsCbw BlockdevOptionsCbw; ++ ++typedef struct q_obj_BlockdevOptions_base q_obj_BlockdevOptions_base; ++ ++typedef struct BlockdevOptions BlockdevOptions; ++ ++typedef struct BlockdevRef BlockdevRef; ++ ++typedef struct BlockdevRefOrNull BlockdevRefOrNull; ++ ++typedef struct BlockdevOptionsList BlockdevOptionsList; ++ ++typedef struct q_obj_blockdev_reopen_arg q_obj_blockdev_reopen_arg; ++ ++typedef struct q_obj_blockdev_del_arg q_obj_blockdev_del_arg; ++ ++typedef struct BlockdevCreateOptionsFile BlockdevCreateOptionsFile; ++ ++typedef struct BlockdevCreateOptionsGluster BlockdevCreateOptionsGluster; ++ ++typedef struct BlockdevCreateOptionsLUKS BlockdevCreateOptionsLUKS; ++ ++typedef struct BlockdevCreateOptionsNfs BlockdevCreateOptionsNfs; ++ ++typedef struct BlockdevCreateOptionsParallels BlockdevCreateOptionsParallels; ++ ++typedef struct BlockdevCreateOptionsQcow BlockdevCreateOptionsQcow; ++ ++typedef enum BlockdevQcow2Version { ++ BLOCKDEV_QCOW2_VERSION_V2, ++ BLOCKDEV_QCOW2_VERSION_V3, ++ BLOCKDEV_QCOW2_VERSION__MAX, ++} BlockdevQcow2Version; ++ ++#define BlockdevQcow2Version_str(val) \ ++ qapi_enum_lookup(&BlockdevQcow2Version_lookup, (val)) ++ ++extern const QEnumLookup BlockdevQcow2Version_lookup; ++ ++typedef enum Qcow2CompressionType { ++ QCOW2_COMPRESSION_TYPE_ZLIB, ++#if defined(CONFIG_ZSTD) ++ QCOW2_COMPRESSION_TYPE_ZSTD, ++#endif /* defined(CONFIG_ZSTD) */ ++ QCOW2_COMPRESSION_TYPE__MAX, ++} Qcow2CompressionType; ++ ++#define Qcow2CompressionType_str(val) \ ++ qapi_enum_lookup(&Qcow2CompressionType_lookup, (val)) ++ ++extern const QEnumLookup Qcow2CompressionType_lookup; ++ ++typedef struct BlockdevCreateOptionsQcow2 BlockdevCreateOptionsQcow2; ++ ++typedef struct BlockdevCreateOptionsQed BlockdevCreateOptionsQed; ++ ++typedef struct BlockdevCreateOptionsRbd BlockdevCreateOptionsRbd; ++ ++typedef enum BlockdevVmdkSubformat { ++ BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE, ++ BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT, ++ BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE, ++ BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT, ++ BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED, ++ BLOCKDEV_VMDK_SUBFORMAT__MAX, ++} BlockdevVmdkSubformat; ++ ++#define BlockdevVmdkSubformat_str(val) \ ++ qapi_enum_lookup(&BlockdevVmdkSubformat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevVmdkSubformat_lookup; ++ ++typedef enum BlockdevVmdkAdapterType { ++ BLOCKDEV_VMDK_ADAPTER_TYPE_IDE, ++ BLOCKDEV_VMDK_ADAPTER_TYPE_BUSLOGIC, ++ BLOCKDEV_VMDK_ADAPTER_TYPE_LSILOGIC, ++ BLOCKDEV_VMDK_ADAPTER_TYPE_LEGACYESX, ++ BLOCKDEV_VMDK_ADAPTER_TYPE__MAX, ++} BlockdevVmdkAdapterType; ++ ++#define BlockdevVmdkAdapterType_str(val) \ ++ qapi_enum_lookup(&BlockdevVmdkAdapterType_lookup, (val)) ++ ++extern const QEnumLookup BlockdevVmdkAdapterType_lookup; ++ ++typedef struct BlockdevCreateOptionsVmdk BlockdevCreateOptionsVmdk; ++ ++typedef struct BlockdevCreateOptionsSsh BlockdevCreateOptionsSsh; ++ ++typedef struct BlockdevCreateOptionsVdi BlockdevCreateOptionsVdi; ++ ++typedef enum BlockdevVhdxSubformat { ++ BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC, ++ BLOCKDEV_VHDX_SUBFORMAT_FIXED, ++ BLOCKDEV_VHDX_SUBFORMAT__MAX, ++} BlockdevVhdxSubformat; ++ ++#define BlockdevVhdxSubformat_str(val) \ ++ qapi_enum_lookup(&BlockdevVhdxSubformat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevVhdxSubformat_lookup; ++ ++typedef struct BlockdevCreateOptionsVhdx BlockdevCreateOptionsVhdx; ++ ++typedef enum BlockdevVpcSubformat { ++ BLOCKDEV_VPC_SUBFORMAT_DYNAMIC, ++ BLOCKDEV_VPC_SUBFORMAT_FIXED, ++ BLOCKDEV_VPC_SUBFORMAT__MAX, ++} BlockdevVpcSubformat; ++ ++#define BlockdevVpcSubformat_str(val) \ ++ qapi_enum_lookup(&BlockdevVpcSubformat_lookup, (val)) ++ ++extern const QEnumLookup BlockdevVpcSubformat_lookup; ++ ++typedef struct BlockdevCreateOptionsVpc BlockdevCreateOptionsVpc; ++ ++typedef struct q_obj_BlockdevCreateOptions_base q_obj_BlockdevCreateOptions_base; ++ ++typedef struct BlockdevCreateOptions BlockdevCreateOptions; ++ ++typedef struct q_obj_blockdev_create_arg q_obj_blockdev_create_arg; ++ ++typedef struct BlockdevAmendOptionsLUKS BlockdevAmendOptionsLUKS; ++ ++typedef struct BlockdevAmendOptionsQcow2 BlockdevAmendOptionsQcow2; ++ ++typedef struct q_obj_BlockdevAmendOptions_base q_obj_BlockdevAmendOptions_base; ++ ++typedef struct BlockdevAmendOptions BlockdevAmendOptions; ++ ++typedef struct q_obj_x_blockdev_amend_arg q_obj_x_blockdev_amend_arg; ++ ++typedef enum BlockErrorAction { ++ BLOCK_ERROR_ACTION_IGNORE, ++ BLOCK_ERROR_ACTION_REPORT, ++ BLOCK_ERROR_ACTION_STOP, ++ BLOCK_ERROR_ACTION__MAX, ++} BlockErrorAction; ++ ++#define BlockErrorAction_str(val) \ ++ qapi_enum_lookup(&BlockErrorAction_lookup, (val)) ++ ++extern const QEnumLookup BlockErrorAction_lookup; ++ ++typedef struct q_obj_BLOCK_IMAGE_CORRUPTED_arg q_obj_BLOCK_IMAGE_CORRUPTED_arg; ++ ++typedef struct q_obj_BLOCK_IO_ERROR_arg q_obj_BLOCK_IO_ERROR_arg; ++ ++typedef struct q_obj_BLOCK_JOB_COMPLETED_arg q_obj_BLOCK_JOB_COMPLETED_arg; ++ ++typedef struct q_obj_BLOCK_JOB_CANCELLED_arg q_obj_BLOCK_JOB_CANCELLED_arg; ++ ++typedef struct q_obj_BLOCK_JOB_ERROR_arg q_obj_BLOCK_JOB_ERROR_arg; ++ ++typedef struct q_obj_BLOCK_JOB_READY_arg q_obj_BLOCK_JOB_READY_arg; ++ ++typedef struct q_obj_BLOCK_JOB_PENDING_arg q_obj_BLOCK_JOB_PENDING_arg; ++ ++typedef enum PreallocMode { ++ PREALLOC_MODE_OFF, ++ PREALLOC_MODE_METADATA, ++ PREALLOC_MODE_FALLOC, ++ PREALLOC_MODE_FULL, ++ PREALLOC_MODE__MAX, ++} PreallocMode; ++ ++#define PreallocMode_str(val) \ ++ qapi_enum_lookup(&PreallocMode_lookup, (val)) ++ ++extern const QEnumLookup PreallocMode_lookup; ++ ++typedef struct q_obj_BLOCK_WRITE_THRESHOLD_arg q_obj_BLOCK_WRITE_THRESHOLD_arg; ++ ++typedef struct q_obj_block_set_write_threshold_arg q_obj_block_set_write_threshold_arg; ++ ++typedef struct q_obj_x_blockdev_change_arg q_obj_x_blockdev_change_arg; ++ ++typedef struct q_obj_x_blockdev_set_iothread_arg q_obj_x_blockdev_set_iothread_arg; ++ ++typedef enum QuorumOpType { ++ QUORUM_OP_TYPE_READ, ++ QUORUM_OP_TYPE_WRITE, ++ QUORUM_OP_TYPE_FLUSH, ++ QUORUM_OP_TYPE__MAX, ++} QuorumOpType; ++ ++#define QuorumOpType_str(val) \ ++ qapi_enum_lookup(&QuorumOpType_lookup, (val)) ++ ++extern const QEnumLookup QuorumOpType_lookup; ++ ++typedef struct q_obj_QUORUM_FAILURE_arg q_obj_QUORUM_FAILURE_arg; ++ ++typedef struct q_obj_QUORUM_REPORT_BAD_arg q_obj_QUORUM_REPORT_BAD_arg; ++ ++typedef struct BlockdevSnapshotInternal BlockdevSnapshotInternal; ++ ++typedef struct q_obj_blockdev_snapshot_delete_internal_sync_arg q_obj_blockdev_snapshot_delete_internal_sync_arg; ++ ++typedef struct BlockGraphInfoList BlockGraphInfoList; ++ ++typedef struct DummyBlockCoreForceArrays DummyBlockCoreForceArrays; ++ ++struct SnapshotInfo { ++ char *id; ++ char *name; ++ int64_t vm_state_size; ++ int64_t date_sec; ++ int64_t date_nsec; ++ int64_t vm_clock_sec; ++ int64_t vm_clock_nsec; ++ bool has_icount; ++ int64_t icount; ++}; ++ ++void qapi_free_SnapshotInfo(SnapshotInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SnapshotInfo, qapi_free_SnapshotInfo) ++ ++struct ImageInfoSpecificQCow2EncryptionBase { ++ BlockdevQcow2EncryptionFormat format; ++}; ++ ++void qapi_free_ImageInfoSpecificQCow2EncryptionBase(ImageInfoSpecificQCow2EncryptionBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2EncryptionBase, qapi_free_ImageInfoSpecificQCow2EncryptionBase) ++ ++struct ImageInfoSpecificQCow2Encryption { ++ /* Members inherited from ImageInfoSpecificQCow2EncryptionBase: */ ++ BlockdevQcow2EncryptionFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockInfoLUKS luks; ++ } u; ++}; ++ ++static inline ImageInfoSpecificQCow2EncryptionBase *qapi_ImageInfoSpecificQCow2Encryption_base(const ImageInfoSpecificQCow2Encryption *obj) ++{ ++ return (ImageInfoSpecificQCow2EncryptionBase *)obj; ++} ++ ++void qapi_free_ImageInfoSpecificQCow2Encryption(ImageInfoSpecificQCow2Encryption *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Encryption, qapi_free_ImageInfoSpecificQCow2Encryption) ++ ++struct Qcow2BitmapInfoList { ++ Qcow2BitmapInfoList *next; ++ Qcow2BitmapInfo *value; ++}; ++ ++void qapi_free_Qcow2BitmapInfoList(Qcow2BitmapInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2BitmapInfoList, qapi_free_Qcow2BitmapInfoList) ++ ++struct ImageInfoSpecificQCow2 { ++ char *compat; ++ char *data_file; ++ bool has_data_file_raw; ++ bool data_file_raw; ++ bool has_extended_l2; ++ bool extended_l2; ++ bool has_lazy_refcounts; ++ bool lazy_refcounts; ++ bool has_corrupt; ++ bool corrupt; ++ int64_t refcount_bits; ++ ImageInfoSpecificQCow2Encryption *encrypt; ++ bool has_bitmaps; ++ Qcow2BitmapInfoList *bitmaps; ++ Qcow2CompressionType compression_type; ++}; ++ ++void qapi_free_ImageInfoSpecificQCow2(ImageInfoSpecificQCow2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2, qapi_free_ImageInfoSpecificQCow2) ++ ++struct VmdkExtentInfoList { ++ VmdkExtentInfoList *next; ++ VmdkExtentInfo *value; ++}; ++ ++void qapi_free_VmdkExtentInfoList(VmdkExtentInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(VmdkExtentInfoList, qapi_free_VmdkExtentInfoList) ++ ++struct ImageInfoSpecificVmdk { ++ char *create_type; ++ int64_t cid; ++ int64_t parent_cid; ++ VmdkExtentInfoList *extents; ++}; ++ ++void qapi_free_ImageInfoSpecificVmdk(ImageInfoSpecificVmdk *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificVmdk, qapi_free_ImageInfoSpecificVmdk) ++ ++struct VmdkExtentInfo { ++ char *filename; ++ char *format; ++ int64_t virtual_size; ++ bool has_cluster_size; ++ int64_t cluster_size; ++ bool has_compressed; ++ bool compressed; ++}; ++ ++void qapi_free_VmdkExtentInfo(VmdkExtentInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(VmdkExtentInfo, qapi_free_VmdkExtentInfo) ++ ++struct ImageInfoSpecificRbd { ++ bool has_encryption_format; ++ RbdImageEncryptionFormat encryption_format; ++}; ++ ++void qapi_free_ImageInfoSpecificRbd(ImageInfoSpecificRbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificRbd, qapi_free_ImageInfoSpecificRbd) ++ ++struct ImageInfoSpecificFile { ++ bool has_extent_size_hint; ++ uint64_t extent_size_hint; ++}; ++ ++void qapi_free_ImageInfoSpecificFile(ImageInfoSpecificFile *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificFile, qapi_free_ImageInfoSpecificFile) ++ ++struct ImageInfoSpecificQCow2Wrapper { ++ ImageInfoSpecificQCow2 *data; ++}; ++ ++void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Wrapper, qapi_free_ImageInfoSpecificQCow2Wrapper) ++ ++struct ImageInfoSpecificVmdkWrapper { ++ ImageInfoSpecificVmdk *data; ++}; ++ ++void qapi_free_ImageInfoSpecificVmdkWrapper(ImageInfoSpecificVmdkWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificVmdkWrapper, qapi_free_ImageInfoSpecificVmdkWrapper) ++ ++struct ImageInfoSpecificLUKSWrapper { ++ QCryptoBlockInfoLUKS *data; ++}; ++ ++void qapi_free_ImageInfoSpecificLUKSWrapper(ImageInfoSpecificLUKSWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificLUKSWrapper, qapi_free_ImageInfoSpecificLUKSWrapper) ++ ++struct ImageInfoSpecificRbdWrapper { ++ ImageInfoSpecificRbd *data; ++}; ++ ++void qapi_free_ImageInfoSpecificRbdWrapper(ImageInfoSpecificRbdWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificRbdWrapper, qapi_free_ImageInfoSpecificRbdWrapper) ++ ++struct ImageInfoSpecificFileWrapper { ++ ImageInfoSpecificFile *data; ++}; ++ ++void qapi_free_ImageInfoSpecificFileWrapper(ImageInfoSpecificFileWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificFileWrapper, qapi_free_ImageInfoSpecificFileWrapper) ++ ++struct q_obj_ImageInfoSpecific_base { ++ ImageInfoSpecificKind type; ++}; ++ ++struct ImageInfoSpecific { ++ ImageInfoSpecificKind type; ++ union { /* union tag is @type */ ++ ImageInfoSpecificQCow2Wrapper qcow2; ++ ImageInfoSpecificVmdkWrapper vmdk; ++ ImageInfoSpecificLUKSWrapper luks; ++ ImageInfoSpecificRbdWrapper rbd; ++ ImageInfoSpecificFileWrapper file; ++ } u; ++}; ++ ++void qapi_free_ImageInfoSpecific(ImageInfoSpecific *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecific, qapi_free_ImageInfoSpecific) ++ ++struct SnapshotInfoList { ++ SnapshotInfoList *next; ++ SnapshotInfo *value; ++}; ++ ++void qapi_free_SnapshotInfoList(SnapshotInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SnapshotInfoList, qapi_free_SnapshotInfoList) ++ ++struct BlockNodeInfo { ++ char *filename; ++ char *format; ++ bool has_dirty_flag; ++ bool dirty_flag; ++ bool has_actual_size; ++ int64_t actual_size; ++ int64_t virtual_size; ++ bool has_cluster_size; ++ int64_t cluster_size; ++ bool has_encrypted; ++ bool encrypted; ++ bool has_compressed; ++ bool compressed; ++ char *backing_filename; ++ char *full_backing_filename; ++ char *backing_filename_format; ++ bool has_snapshots; ++ SnapshotInfoList *snapshots; ++ ImageInfoSpecific *format_specific; ++}; ++ ++void qapi_free_BlockNodeInfo(BlockNodeInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockNodeInfo, qapi_free_BlockNodeInfo) ++ ++struct ImageInfo { ++ /* Members inherited from BlockNodeInfo: */ ++ char *filename; ++ char *format; ++ bool has_dirty_flag; ++ bool dirty_flag; ++ bool has_actual_size; ++ int64_t actual_size; ++ int64_t virtual_size; ++ bool has_cluster_size; ++ int64_t cluster_size; ++ bool has_encrypted; ++ bool encrypted; ++ bool has_compressed; ++ bool compressed; ++ char *backing_filename; ++ char *full_backing_filename; ++ char *backing_filename_format; ++ bool has_snapshots; ++ SnapshotInfoList *snapshots; ++ ImageInfoSpecific *format_specific; ++ /* Own members: */ ++ ImageInfo *backing_image; ++}; ++ ++static inline BlockNodeInfo *qapi_ImageInfo_base(const ImageInfo *obj) ++{ ++ return (BlockNodeInfo *)obj; ++} ++ ++void qapi_free_ImageInfo(ImageInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfo, qapi_free_ImageInfo) ++ ++struct BlockChildInfo { ++ char *name; ++ BlockGraphInfo *info; ++}; ++ ++void qapi_free_BlockChildInfo(BlockChildInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockChildInfo, qapi_free_BlockChildInfo) ++ ++struct BlockChildInfoList { ++ BlockChildInfoList *next; ++ BlockChildInfo *value; ++}; ++ ++void qapi_free_BlockChildInfoList(BlockChildInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockChildInfoList, qapi_free_BlockChildInfoList) ++ ++struct BlockGraphInfo { ++ /* Members inherited from BlockNodeInfo: */ ++ char *filename; ++ char *format; ++ bool has_dirty_flag; ++ bool dirty_flag; ++ bool has_actual_size; ++ int64_t actual_size; ++ int64_t virtual_size; ++ bool has_cluster_size; ++ int64_t cluster_size; ++ bool has_encrypted; ++ bool encrypted; ++ bool has_compressed; ++ bool compressed; ++ char *backing_filename; ++ char *full_backing_filename; ++ char *backing_filename_format; ++ bool has_snapshots; ++ SnapshotInfoList *snapshots; ++ ImageInfoSpecific *format_specific; ++ /* Own members: */ ++ BlockChildInfoList *children; ++}; ++ ++static inline BlockNodeInfo *qapi_BlockGraphInfo_base(const BlockGraphInfo *obj) ++{ ++ return (BlockNodeInfo *)obj; ++} ++ ++void qapi_free_BlockGraphInfo(BlockGraphInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockGraphInfo, qapi_free_BlockGraphInfo) ++ ++struct ImageCheck { ++ char *filename; ++ char *format; ++ int64_t check_errors; ++ bool has_image_end_offset; ++ int64_t image_end_offset; ++ bool has_corruptions; ++ int64_t corruptions; ++ bool has_leaks; ++ int64_t leaks; ++ bool has_corruptions_fixed; ++ int64_t corruptions_fixed; ++ bool has_leaks_fixed; ++ int64_t leaks_fixed; ++ bool has_total_clusters; ++ int64_t total_clusters; ++ bool has_allocated_clusters; ++ int64_t allocated_clusters; ++ bool has_fragmented_clusters; ++ int64_t fragmented_clusters; ++ bool has_compressed_clusters; ++ int64_t compressed_clusters; ++}; ++ ++void qapi_free_ImageCheck(ImageCheck *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageCheck, qapi_free_ImageCheck) ++ ++struct MapEntry { ++ int64_t start; ++ int64_t length; ++ bool data; ++ bool zero; ++ bool compressed; ++ int64_t depth; ++ bool present; ++ bool has_offset; ++ int64_t offset; ++ char *filename; ++}; ++ ++void qapi_free_MapEntry(MapEntry *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(MapEntry, qapi_free_MapEntry) ++ ++struct BlockdevCacheInfo { ++ bool writeback; ++ bool direct; ++ bool no_flush; ++}; ++ ++void qapi_free_BlockdevCacheInfo(BlockdevCacheInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCacheInfo, qapi_free_BlockdevCacheInfo) ++ ++struct BlockDirtyInfoList { ++ BlockDirtyInfoList *next; ++ BlockDirtyInfo *value; ++}; ++ ++void qapi_free_BlockDirtyInfoList(BlockDirtyInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyInfoList, qapi_free_BlockDirtyInfoList) ++ ++struct BlockDeviceInfo { ++ char *file; ++ char *node_name; ++ bool ro; ++ char *drv; ++ char *backing_file; ++ int64_t backing_file_depth; ++ bool encrypted; ++ BlockdevDetectZeroesOptions detect_zeroes; ++ int64_t bps; ++ int64_t bps_rd; ++ int64_t bps_wr; ++ int64_t iops; ++ int64_t iops_rd; ++ int64_t iops_wr; ++ ImageInfo *image; ++ bool has_bps_max; ++ int64_t bps_max; ++ bool has_bps_rd_max; ++ int64_t bps_rd_max; ++ bool has_bps_wr_max; ++ int64_t bps_wr_max; ++ bool has_iops_max; ++ int64_t iops_max; ++ bool has_iops_rd_max; ++ int64_t iops_rd_max; ++ bool has_iops_wr_max; ++ int64_t iops_wr_max; ++ bool has_bps_max_length; ++ int64_t bps_max_length; ++ bool has_bps_rd_max_length; ++ int64_t bps_rd_max_length; ++ bool has_bps_wr_max_length; ++ int64_t bps_wr_max_length; ++ bool has_iops_max_length; ++ int64_t iops_max_length; ++ bool has_iops_rd_max_length; ++ int64_t iops_rd_max_length; ++ bool has_iops_wr_max_length; ++ int64_t iops_wr_max_length; ++ bool has_iops_size; ++ int64_t iops_size; ++ char *group; ++ BlockdevCacheInfo *cache; ++ int64_t write_threshold; ++ bool has_dirty_bitmaps; ++ BlockDirtyInfoList *dirty_bitmaps; ++}; ++ ++void qapi_free_BlockDeviceInfo(BlockDeviceInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceInfo, qapi_free_BlockDeviceInfo) ++ ++struct BlockDirtyInfo { ++ char *name; ++ int64_t count; ++ uint32_t granularity; ++ bool recording; ++ bool busy; ++ bool persistent; ++ bool has_inconsistent; ++ bool inconsistent; ++}; ++ ++void qapi_free_BlockDirtyInfo(BlockDirtyInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyInfo, qapi_free_BlockDirtyInfo) ++ ++struct Qcow2BitmapInfoFlagsList { ++ Qcow2BitmapInfoFlagsList *next; ++ Qcow2BitmapInfoFlags value; ++}; ++ ++void qapi_free_Qcow2BitmapInfoFlagsList(Qcow2BitmapInfoFlagsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2BitmapInfoFlagsList, qapi_free_Qcow2BitmapInfoFlagsList) ++ ++struct Qcow2BitmapInfo { ++ char *name; ++ uint32_t granularity; ++ Qcow2BitmapInfoFlagsList *flags; ++}; ++ ++void qapi_free_Qcow2BitmapInfo(Qcow2BitmapInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2BitmapInfo, qapi_free_Qcow2BitmapInfo) ++ ++struct BlockLatencyHistogramInfo { ++ uint64List *boundaries; ++ uint64List *bins; ++}; ++ ++void qapi_free_BlockLatencyHistogramInfo(BlockLatencyHistogramInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockLatencyHistogramInfo, qapi_free_BlockLatencyHistogramInfo) ++ ++struct BlockInfo { ++ char *device; ++ char *qdev; ++ char *type; ++ bool removable; ++ bool locked; ++ BlockDeviceInfo *inserted; ++ bool has_tray_open; ++ bool tray_open; ++ bool has_io_status; ++ BlockDeviceIoStatus io_status; ++}; ++ ++void qapi_free_BlockInfo(BlockInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockInfo, qapi_free_BlockInfo) ++ ++struct BlockMeasureInfo { ++ int64_t required; ++ int64_t fully_allocated; ++ bool has_bitmaps; ++ int64_t bitmaps; ++}; ++ ++void qapi_free_BlockMeasureInfo(BlockMeasureInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockMeasureInfo, qapi_free_BlockMeasureInfo) ++ ++struct BlockInfoList { ++ BlockInfoList *next; ++ BlockInfo *value; ++}; ++ ++void qapi_free_BlockInfoList(BlockInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockInfoList, qapi_free_BlockInfoList) ++ ++struct BlockDeviceTimedStats { ++ int64_t interval_length; ++ int64_t min_rd_latency_ns; ++ int64_t max_rd_latency_ns; ++ int64_t avg_rd_latency_ns; ++ int64_t min_wr_latency_ns; ++ int64_t max_wr_latency_ns; ++ int64_t avg_wr_latency_ns; ++ int64_t min_zone_append_latency_ns; ++ int64_t max_zone_append_latency_ns; ++ int64_t avg_zone_append_latency_ns; ++ int64_t min_flush_latency_ns; ++ int64_t max_flush_latency_ns; ++ int64_t avg_flush_latency_ns; ++ double avg_rd_queue_depth; ++ double avg_wr_queue_depth; ++ double avg_zone_append_queue_depth; ++}; ++ ++void qapi_free_BlockDeviceTimedStats(BlockDeviceTimedStats *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceTimedStats, qapi_free_BlockDeviceTimedStats) ++ ++struct BlockDeviceTimedStatsList { ++ BlockDeviceTimedStatsList *next; ++ BlockDeviceTimedStats *value; ++}; ++ ++void qapi_free_BlockDeviceTimedStatsList(BlockDeviceTimedStatsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceTimedStatsList, qapi_free_BlockDeviceTimedStatsList) ++ ++struct BlockDeviceStats { ++ int64_t rd_bytes; ++ int64_t wr_bytes; ++ int64_t zone_append_bytes; ++ int64_t unmap_bytes; ++ int64_t rd_operations; ++ int64_t wr_operations; ++ int64_t zone_append_operations; ++ int64_t flush_operations; ++ int64_t unmap_operations; ++ int64_t rd_total_time_ns; ++ int64_t wr_total_time_ns; ++ int64_t zone_append_total_time_ns; ++ int64_t flush_total_time_ns; ++ int64_t unmap_total_time_ns; ++ int64_t wr_highest_offset; ++ int64_t rd_merged; ++ int64_t wr_merged; ++ int64_t zone_append_merged; ++ int64_t unmap_merged; ++ bool has_idle_time_ns; ++ int64_t idle_time_ns; ++ int64_t failed_rd_operations; ++ int64_t failed_wr_operations; ++ int64_t failed_zone_append_operations; ++ int64_t failed_flush_operations; ++ int64_t failed_unmap_operations; ++ int64_t invalid_rd_operations; ++ int64_t invalid_wr_operations; ++ int64_t invalid_zone_append_operations; ++ int64_t invalid_flush_operations; ++ int64_t invalid_unmap_operations; ++ bool account_invalid; ++ bool account_failed; ++ BlockDeviceTimedStatsList *timed_stats; ++ BlockLatencyHistogramInfo *rd_latency_histogram; ++ BlockLatencyHistogramInfo *wr_latency_histogram; ++ BlockLatencyHistogramInfo *zone_append_latency_histogram; ++ BlockLatencyHistogramInfo *flush_latency_histogram; ++}; ++ ++void qapi_free_BlockDeviceStats(BlockDeviceStats *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceStats, qapi_free_BlockDeviceStats) ++ ++struct BlockStatsSpecificFile { ++ uint64_t discard_nb_ok; ++ uint64_t discard_nb_failed; ++ uint64_t discard_bytes_ok; ++}; ++ ++void qapi_free_BlockStatsSpecificFile(BlockStatsSpecificFile *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStatsSpecificFile, qapi_free_BlockStatsSpecificFile) ++ ++struct BlockStatsSpecificNvme { ++ uint64_t completion_errors; ++ uint64_t aligned_accesses; ++ uint64_t unaligned_accesses; ++}; ++ ++void qapi_free_BlockStatsSpecificNvme(BlockStatsSpecificNvme *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStatsSpecificNvme, qapi_free_BlockStatsSpecificNvme) ++ ++struct q_obj_BlockStatsSpecific_base { ++ BlockdevDriver driver; ++}; ++ ++struct BlockStatsSpecific { ++ BlockdevDriver driver; ++ union { /* union tag is @driver */ ++ BlockStatsSpecificFile file; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BlockStatsSpecificFile host_device; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ BlockStatsSpecificNvme nvme; ++ } u; ++}; ++ ++void qapi_free_BlockStatsSpecific(BlockStatsSpecific *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStatsSpecific, qapi_free_BlockStatsSpecific) ++ ++struct BlockStats { ++ char *device; ++ char *qdev; ++ char *node_name; ++ BlockDeviceStats *stats; ++ BlockStatsSpecific *driver_specific; ++ BlockStats *parent; ++ BlockStats *backing; ++}; ++ ++void qapi_free_BlockStats(BlockStats *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStats, qapi_free_BlockStats) ++ ++struct q_obj_query_blockstats_arg { ++ bool has_query_nodes; ++ bool query_nodes; ++}; ++ ++struct BlockStatsList { ++ BlockStatsList *next; ++ BlockStats *value; ++}; ++ ++void qapi_free_BlockStatsList(BlockStatsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockStatsList, qapi_free_BlockStatsList) ++ ++struct BlockJobInfoMirror { ++ bool actively_synced; ++}; ++ ++void qapi_free_BlockJobInfoMirror(BlockJobInfoMirror *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobInfoMirror, qapi_free_BlockJobInfoMirror) ++ ++struct q_obj_BlockJobInfo_base { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ bool busy; ++ bool paused; ++ int64_t speed; ++ BlockDeviceIoStatus io_status; ++ bool ready; ++ JobStatus status; ++ bool auto_finalize; ++ bool auto_dismiss; ++ char *error; ++}; ++ ++struct BlockJobInfo { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ bool busy; ++ bool paused; ++ int64_t speed; ++ BlockDeviceIoStatus io_status; ++ bool ready; ++ JobStatus status; ++ bool auto_finalize; ++ bool auto_dismiss; ++ char *error; ++ union { /* union tag is @type */ ++ BlockJobInfoMirror mirror; ++ } u; ++}; ++ ++void qapi_free_BlockJobInfo(BlockJobInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobInfo, qapi_free_BlockJobInfo) ++ ++struct BlockJobInfoList { ++ BlockJobInfoList *next; ++ BlockJobInfo *value; ++}; ++ ++void qapi_free_BlockJobInfoList(BlockJobInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobInfoList, qapi_free_BlockJobInfoList) ++ ++struct q_obj_block_resize_arg { ++ char *device; ++ char *node_name; ++ int64_t size; ++}; ++ ++struct BlockdevSnapshotSync { ++ char *device; ++ char *node_name; ++ char *snapshot_file; ++ char *snapshot_node_name; ++ char *format; ++ bool has_mode; ++ NewImageMode mode; ++}; ++ ++void qapi_free_BlockdevSnapshotSync(BlockdevSnapshotSync *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevSnapshotSync, qapi_free_BlockdevSnapshotSync) ++ ++struct BlockdevSnapshot { ++ char *node; ++ char *overlay; ++}; ++ ++void qapi_free_BlockdevSnapshot(BlockdevSnapshot *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevSnapshot, qapi_free_BlockdevSnapshot) ++ ++struct BackupPerf { ++ bool has_use_copy_range; ++ bool use_copy_range; ++ bool has_max_workers; ++ int64_t max_workers; ++ bool has_max_chunk; ++ int64_t max_chunk; ++}; ++ ++void qapi_free_BackupPerf(BackupPerf *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BackupPerf, qapi_free_BackupPerf) ++ ++struct BackupCommon { ++ char *job_id; ++ char *device; ++ MirrorSyncMode sync; ++ bool has_speed; ++ int64_t speed; ++ char *bitmap; ++ bool has_bitmap_mode; ++ BitmapSyncMode bitmap_mode; ++ bool has_compress; ++ bool compress; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++ char *filter_node_name; ++ bool has_discard_source; ++ bool discard_source; ++ BackupPerf *x_perf; ++}; ++ ++void qapi_free_BackupCommon(BackupCommon *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BackupCommon, qapi_free_BackupCommon) ++ ++struct DriveBackup { ++ /* Members inherited from BackupCommon: */ ++ char *job_id; ++ char *device; ++ MirrorSyncMode sync; ++ bool has_speed; ++ int64_t speed; ++ char *bitmap; ++ bool has_bitmap_mode; ++ BitmapSyncMode bitmap_mode; ++ bool has_compress; ++ bool compress; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++ char *filter_node_name; ++ bool has_discard_source; ++ bool discard_source; ++ BackupPerf *x_perf; ++ /* Own members: */ ++ char *target; ++ char *format; ++ bool has_mode; ++ NewImageMode mode; ++}; ++ ++static inline BackupCommon *qapi_DriveBackup_base(const DriveBackup *obj) ++{ ++ return (BackupCommon *)obj; ++} ++ ++void qapi_free_DriveBackup(DriveBackup *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(DriveBackup, qapi_free_DriveBackup) ++ ++struct BlockdevBackup { ++ /* Members inherited from BackupCommon: */ ++ char *job_id; ++ char *device; ++ MirrorSyncMode sync; ++ bool has_speed; ++ int64_t speed; ++ char *bitmap; ++ bool has_bitmap_mode; ++ BitmapSyncMode bitmap_mode; ++ bool has_compress; ++ bool compress; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++ char *filter_node_name; ++ bool has_discard_source; ++ bool discard_source; ++ BackupPerf *x_perf; ++ /* Own members: */ ++ char *target; ++}; ++ ++static inline BackupCommon *qapi_BlockdevBackup_base(const BlockdevBackup *obj) ++{ ++ return (BackupCommon *)obj; ++} ++ ++void qapi_free_BlockdevBackup(BlockdevBackup *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevBackup, qapi_free_BlockdevBackup) ++ ++struct q_obj_change_backing_file_arg { ++ char *device; ++ char *image_node_name; ++ char *backing_file; ++}; ++ ++struct q_obj_block_commit_arg { ++ char *job_id; ++ char *device; ++ char *base_node; ++ char *base; ++ char *top_node; ++ char *top; ++ char *backing_file; ++ bool has_backing_mask_protocol; ++ bool backing_mask_protocol; ++ bool has_speed; ++ int64_t speed; ++ bool has_on_error; ++ BlockdevOnError on_error; ++ char *filter_node_name; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++}; ++ ++struct q_obj_query_named_block_nodes_arg { ++ bool has_flat; ++ bool flat; ++}; ++ ++struct BlockDeviceInfoList { ++ BlockDeviceInfoList *next; ++ BlockDeviceInfo *value; ++}; ++ ++void qapi_free_BlockDeviceInfoList(BlockDeviceInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDeviceInfoList, qapi_free_BlockDeviceInfoList) ++ ++struct XDbgBlockGraphNode { ++ uint64_t id; ++ XDbgBlockGraphNodeType type; ++ char *name; ++}; ++ ++void qapi_free_XDbgBlockGraphNode(XDbgBlockGraphNode *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraphNode, qapi_free_XDbgBlockGraphNode) ++ ++struct BlockPermissionList { ++ BlockPermissionList *next; ++ BlockPermission value; ++}; ++ ++void qapi_free_BlockPermissionList(BlockPermissionList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockPermissionList, qapi_free_BlockPermissionList) ++ ++struct XDbgBlockGraphEdge { ++ uint64_t parent; ++ uint64_t child; ++ char *name; ++ BlockPermissionList *perm; ++ BlockPermissionList *shared_perm; ++}; ++ ++void qapi_free_XDbgBlockGraphEdge(XDbgBlockGraphEdge *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraphEdge, qapi_free_XDbgBlockGraphEdge) ++ ++struct XDbgBlockGraphNodeList { ++ XDbgBlockGraphNodeList *next; ++ XDbgBlockGraphNode *value; ++}; ++ ++void qapi_free_XDbgBlockGraphNodeList(XDbgBlockGraphNodeList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraphNodeList, qapi_free_XDbgBlockGraphNodeList) ++ ++struct XDbgBlockGraphEdgeList { ++ XDbgBlockGraphEdgeList *next; ++ XDbgBlockGraphEdge *value; ++}; ++ ++void qapi_free_XDbgBlockGraphEdgeList(XDbgBlockGraphEdgeList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraphEdgeList, qapi_free_XDbgBlockGraphEdgeList) ++ ++struct XDbgBlockGraph { ++ XDbgBlockGraphNodeList *nodes; ++ XDbgBlockGraphEdgeList *edges; ++}; ++ ++void qapi_free_XDbgBlockGraph(XDbgBlockGraph *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(XDbgBlockGraph, qapi_free_XDbgBlockGraph) ++ ++struct DriveMirror { ++ char *job_id; ++ char *device; ++ char *target; ++ char *format; ++ char *node_name; ++ char *replaces; ++ MirrorSyncMode sync; ++ bool has_mode; ++ NewImageMode mode; ++ bool has_speed; ++ int64_t speed; ++ bool has_granularity; ++ uint32_t granularity; ++ bool has_buf_size; ++ int64_t buf_size; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ bool has_unmap; ++ bool unmap; ++ bool has_copy_mode; ++ MirrorCopyMode copy_mode; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++}; ++ ++void qapi_free_DriveMirror(DriveMirror *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(DriveMirror, qapi_free_DriveMirror) ++ ++struct BlockDirtyBitmap { ++ char *node; ++ char *name; ++}; ++ ++void qapi_free_BlockDirtyBitmap(BlockDirtyBitmap *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmap, qapi_free_BlockDirtyBitmap) ++ ++struct BlockDirtyBitmapAdd { ++ char *node; ++ char *name; ++ bool has_granularity; ++ uint32_t granularity; ++ bool has_persistent; ++ bool persistent; ++ bool has_disabled; ++ bool disabled; ++}; ++ ++void qapi_free_BlockDirtyBitmapAdd(BlockDirtyBitmapAdd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapAdd, qapi_free_BlockDirtyBitmapAdd) ++ ++struct BlockDirtyBitmapOrStr { ++ QType type; ++ union { /* union tag is @type */ ++ char *local; ++ BlockDirtyBitmap external; ++ } u; ++}; ++ ++void qapi_free_BlockDirtyBitmapOrStr(BlockDirtyBitmapOrStr *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapOrStr, qapi_free_BlockDirtyBitmapOrStr) ++ ++struct BlockDirtyBitmapOrStrList { ++ BlockDirtyBitmapOrStrList *next; ++ BlockDirtyBitmapOrStr *value; ++}; ++ ++void qapi_free_BlockDirtyBitmapOrStrList(BlockDirtyBitmapOrStrList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapOrStrList, qapi_free_BlockDirtyBitmapOrStrList) ++ ++struct BlockDirtyBitmapMerge { ++ char *node; ++ char *target; ++ BlockDirtyBitmapOrStrList *bitmaps; ++}; ++ ++void qapi_free_BlockDirtyBitmapMerge(BlockDirtyBitmapMerge *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapMerge, qapi_free_BlockDirtyBitmapMerge) ++ ++struct BlockDirtyBitmapSha256 { ++ char *sha256; ++}; ++ ++void qapi_free_BlockDirtyBitmapSha256(BlockDirtyBitmapSha256 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockDirtyBitmapSha256, qapi_free_BlockDirtyBitmapSha256) ++ ++struct q_obj_blockdev_mirror_arg { ++ char *job_id; ++ char *device; ++ char *target; ++ char *replaces; ++ MirrorSyncMode sync; ++ bool has_speed; ++ int64_t speed; ++ bool has_granularity; ++ uint32_t granularity; ++ bool has_buf_size; ++ int64_t buf_size; ++ bool has_on_source_error; ++ BlockdevOnError on_source_error; ++ bool has_on_target_error; ++ BlockdevOnError on_target_error; ++ char *filter_node_name; ++ bool has_copy_mode; ++ MirrorCopyMode copy_mode; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++}; ++ ++struct BlockIOThrottle { ++ char *device; ++ char *id; ++ int64_t bps; ++ int64_t bps_rd; ++ int64_t bps_wr; ++ int64_t iops; ++ int64_t iops_rd; ++ int64_t iops_wr; ++ bool has_bps_max; ++ int64_t bps_max; ++ bool has_bps_rd_max; ++ int64_t bps_rd_max; ++ bool has_bps_wr_max; ++ int64_t bps_wr_max; ++ bool has_iops_max; ++ int64_t iops_max; ++ bool has_iops_rd_max; ++ int64_t iops_rd_max; ++ bool has_iops_wr_max; ++ int64_t iops_wr_max; ++ bool has_bps_max_length; ++ int64_t bps_max_length; ++ bool has_bps_rd_max_length; ++ int64_t bps_rd_max_length; ++ bool has_bps_wr_max_length; ++ int64_t bps_wr_max_length; ++ bool has_iops_max_length; ++ int64_t iops_max_length; ++ bool has_iops_rd_max_length; ++ int64_t iops_rd_max_length; ++ bool has_iops_wr_max_length; ++ int64_t iops_wr_max_length; ++ bool has_iops_size; ++ int64_t iops_size; ++ char *group; ++}; ++ ++void qapi_free_BlockIOThrottle(BlockIOThrottle *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockIOThrottle, qapi_free_BlockIOThrottle) ++ ++struct ThrottleLimits { ++ bool has_iops_total; ++ int64_t iops_total; ++ bool has_iops_total_max; ++ int64_t iops_total_max; ++ bool has_iops_total_max_length; ++ int64_t iops_total_max_length; ++ bool has_iops_read; ++ int64_t iops_read; ++ bool has_iops_read_max; ++ int64_t iops_read_max; ++ bool has_iops_read_max_length; ++ int64_t iops_read_max_length; ++ bool has_iops_write; ++ int64_t iops_write; ++ bool has_iops_write_max; ++ int64_t iops_write_max; ++ bool has_iops_write_max_length; ++ int64_t iops_write_max_length; ++ bool has_bps_total; ++ int64_t bps_total; ++ bool has_bps_total_max; ++ int64_t bps_total_max; ++ bool has_bps_total_max_length; ++ int64_t bps_total_max_length; ++ bool has_bps_read; ++ int64_t bps_read; ++ bool has_bps_read_max; ++ int64_t bps_read_max; ++ bool has_bps_read_max_length; ++ int64_t bps_read_max_length; ++ bool has_bps_write; ++ int64_t bps_write; ++ bool has_bps_write_max; ++ int64_t bps_write_max; ++ bool has_bps_write_max_length; ++ int64_t bps_write_max_length; ++ bool has_iops_size; ++ int64_t iops_size; ++}; ++ ++void qapi_free_ThrottleLimits(ThrottleLimits *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ThrottleLimits, qapi_free_ThrottleLimits) ++ ++struct ThrottleGroupProperties { ++ ThrottleLimits *limits; ++ bool has_x_iops_total; ++ int64_t x_iops_total; ++ bool has_x_iops_total_max; ++ int64_t x_iops_total_max; ++ bool has_x_iops_total_max_length; ++ int64_t x_iops_total_max_length; ++ bool has_x_iops_read; ++ int64_t x_iops_read; ++ bool has_x_iops_read_max; ++ int64_t x_iops_read_max; ++ bool has_x_iops_read_max_length; ++ int64_t x_iops_read_max_length; ++ bool has_x_iops_write; ++ int64_t x_iops_write; ++ bool has_x_iops_write_max; ++ int64_t x_iops_write_max; ++ bool has_x_iops_write_max_length; ++ int64_t x_iops_write_max_length; ++ bool has_x_bps_total; ++ int64_t x_bps_total; ++ bool has_x_bps_total_max; ++ int64_t x_bps_total_max; ++ bool has_x_bps_total_max_length; ++ int64_t x_bps_total_max_length; ++ bool has_x_bps_read; ++ int64_t x_bps_read; ++ bool has_x_bps_read_max; ++ int64_t x_bps_read_max; ++ bool has_x_bps_read_max_length; ++ int64_t x_bps_read_max_length; ++ bool has_x_bps_write; ++ int64_t x_bps_write; ++ bool has_x_bps_write_max; ++ int64_t x_bps_write_max; ++ bool has_x_bps_write_max_length; ++ int64_t x_bps_write_max_length; ++ bool has_x_iops_size; ++ int64_t x_iops_size; ++}; ++ ++void qapi_free_ThrottleGroupProperties(ThrottleGroupProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(ThrottleGroupProperties, qapi_free_ThrottleGroupProperties) ++ ++struct q_obj_block_stream_arg { ++ char *job_id; ++ char *device; ++ char *base; ++ char *base_node; ++ char *backing_file; ++ bool has_backing_mask_protocol; ++ bool backing_mask_protocol; ++ char *bottom; ++ bool has_speed; ++ int64_t speed; ++ bool has_on_error; ++ BlockdevOnError on_error; ++ char *filter_node_name; ++ bool has_auto_finalize; ++ bool auto_finalize; ++ bool has_auto_dismiss; ++ bool auto_dismiss; ++}; ++ ++struct q_obj_block_job_set_speed_arg { ++ char *device; ++ int64_t speed; ++}; ++ ++struct q_obj_block_job_cancel_arg { ++ char *device; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_block_job_pause_arg { ++ char *device; ++}; ++ ++struct q_obj_block_job_resume_arg { ++ char *device; ++}; ++ ++struct q_obj_block_job_complete_arg { ++ char *device; ++}; ++ ++struct q_obj_block_job_dismiss_arg { ++ char *id; ++}; ++ ++struct q_obj_block_job_finalize_arg { ++ char *id; ++}; ++ ++struct BlockJobChangeOptionsMirror { ++ MirrorCopyMode copy_mode; ++}; ++ ++void qapi_free_BlockJobChangeOptionsMirror(BlockJobChangeOptionsMirror *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobChangeOptionsMirror, qapi_free_BlockJobChangeOptionsMirror) ++ ++struct q_obj_BlockJobChangeOptions_base { ++ char *id; ++ JobType type; ++}; ++ ++struct BlockJobChangeOptions { ++ char *id; ++ JobType type; ++ union { /* union tag is @type */ ++ BlockJobChangeOptionsMirror mirror; ++ } u; ++}; ++ ++void qapi_free_BlockJobChangeOptions(BlockJobChangeOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobChangeOptions, qapi_free_BlockJobChangeOptions) ++ ++struct BlockdevCacheOptions { ++ bool has_direct; ++ bool direct; ++ bool has_no_flush; ++ bool no_flush; ++}; ++ ++void qapi_free_BlockdevCacheOptions(BlockdevCacheOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCacheOptions, qapi_free_BlockdevCacheOptions) ++ ++struct BlockdevOptionsFile { ++ char *filename; ++ char *pr_manager; ++ bool has_locking; ++ OnOffAuto locking; ++ bool has_aio; ++ BlockdevAioOptions aio; ++ bool has_aio_max_batch; ++ int64_t aio_max_batch; ++#if defined(CONFIG_LINUX) ++ bool has_drop_cache; ++ bool drop_cache; ++#endif /* defined(CONFIG_LINUX) */ ++ bool has_x_check_cache_dropped; ++ bool x_check_cache_dropped; ++}; ++ ++void qapi_free_BlockdevOptionsFile(BlockdevOptionsFile *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsFile, qapi_free_BlockdevOptionsFile) ++ ++struct BlockdevOptionsNull { ++ bool has_size; ++ int64_t size; ++ bool has_latency_ns; ++ uint64_t latency_ns; ++ bool has_read_zeroes; ++ bool read_zeroes; ++}; ++ ++void qapi_free_BlockdevOptionsNull(BlockdevOptionsNull *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNull, qapi_free_BlockdevOptionsNull) ++ ++struct BlockdevOptionsNVMe { ++ char *device; ++ int64_t q_namespace; ++}; ++ ++void qapi_free_BlockdevOptionsNVMe(BlockdevOptionsNVMe *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNVMe, qapi_free_BlockdevOptionsNVMe) ++ ++struct BlockdevOptionsVVFAT { ++ char *dir; ++ bool has_fat_type; ++ int64_t fat_type; ++ bool has_floppy; ++ bool floppy; ++ char *label; ++ bool has_rw; ++ bool rw; ++}; ++ ++void qapi_free_BlockdevOptionsVVFAT(BlockdevOptionsVVFAT *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsVVFAT, qapi_free_BlockdevOptionsVVFAT) ++ ++struct BlockdevOptionsGenericFormat { ++ BlockdevRef *file; ++}; ++ ++void qapi_free_BlockdevOptionsGenericFormat(BlockdevOptionsGenericFormat *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsGenericFormat, qapi_free_BlockdevOptionsGenericFormat) ++ ++struct BlockdevOptionsLUKS { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ char *key_secret; ++ BlockdevRef *header; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsLUKS_base(const BlockdevOptionsLUKS *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsLUKS(BlockdevOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsLUKS, qapi_free_BlockdevOptionsLUKS) ++ ++struct BlockdevOptionsGenericCOWFormat { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ BlockdevRefOrNull *backing; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsGenericCOWFormat_base(const BlockdevOptionsGenericCOWFormat *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsGenericCOWFormat(BlockdevOptionsGenericCOWFormat *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsGenericCOWFormat, qapi_free_BlockdevOptionsGenericCOWFormat) ++ ++struct Qcow2OverlapCheckFlags { ++ bool has_q_template; ++ Qcow2OverlapCheckMode q_template; ++ bool has_main_header; ++ bool main_header; ++ bool has_active_l1; ++ bool active_l1; ++ bool has_active_l2; ++ bool active_l2; ++ bool has_refcount_table; ++ bool refcount_table; ++ bool has_refcount_block; ++ bool refcount_block; ++ bool has_snapshot_table; ++ bool snapshot_table; ++ bool has_inactive_l1; ++ bool inactive_l1; ++ bool has_inactive_l2; ++ bool inactive_l2; ++ bool has_bitmap_directory; ++ bool bitmap_directory; ++}; ++ ++void qapi_free_Qcow2OverlapCheckFlags(Qcow2OverlapCheckFlags *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2OverlapCheckFlags, qapi_free_Qcow2OverlapCheckFlags) ++ ++struct Qcow2OverlapChecks { ++ QType type; ++ union { /* union tag is @type */ ++ Qcow2OverlapCheckFlags flags; ++ Qcow2OverlapCheckMode mode; ++ } u; ++}; ++ ++void qapi_free_Qcow2OverlapChecks(Qcow2OverlapChecks *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2OverlapChecks, qapi_free_Qcow2OverlapChecks) ++ ++struct q_obj_BlockdevQcowEncryption_base { ++ BlockdevQcowEncryptionFormat format; ++}; ++ ++struct BlockdevQcowEncryption { ++ BlockdevQcowEncryptionFormat format; ++ union { /* union tag is @format */ ++ QCryptoBlockOptionsQCow aes; ++ } u; ++}; ++ ++void qapi_free_BlockdevQcowEncryption(BlockdevQcowEncryption *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevQcowEncryption, qapi_free_BlockdevQcowEncryption) ++ ++struct BlockdevOptionsQcow { ++ /* Members inherited from BlockdevOptionsGenericCOWFormat: */ ++ BlockdevRef *file; ++ BlockdevRefOrNull *backing; ++ /* Own members: */ ++ BlockdevQcowEncryption *encrypt; ++}; ++ ++static inline BlockdevOptionsGenericCOWFormat *qapi_BlockdevOptionsQcow_base(const BlockdevOptionsQcow *obj) ++{ ++ return (BlockdevOptionsGenericCOWFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsQcow(BlockdevOptionsQcow *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQcow, qapi_free_BlockdevOptionsQcow) ++ ++struct q_obj_BlockdevQcow2Encryption_base { ++ BlockdevQcow2EncryptionFormat format; ++}; ++ ++struct BlockdevQcow2Encryption { ++ BlockdevQcow2EncryptionFormat format; ++ union { /* union tag is @format */ ++ QCryptoBlockOptionsQCow aes; ++ QCryptoBlockOptionsLUKS luks; ++ } u; ++}; ++ ++void qapi_free_BlockdevQcow2Encryption(BlockdevQcow2Encryption *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevQcow2Encryption, qapi_free_BlockdevQcow2Encryption) ++ ++struct BlockdevOptionsPreallocate { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ bool has_prealloc_align; ++ int64_t prealloc_align; ++ bool has_prealloc_size; ++ int64_t prealloc_size; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsPreallocate_base(const BlockdevOptionsPreallocate *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsPreallocate(BlockdevOptionsPreallocate *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsPreallocate, qapi_free_BlockdevOptionsPreallocate) ++ ++struct BlockdevOptionsQcow2 { ++ /* Members inherited from BlockdevOptionsGenericCOWFormat: */ ++ BlockdevRef *file; ++ BlockdevRefOrNull *backing; ++ /* Own members: */ ++ bool has_lazy_refcounts; ++ bool lazy_refcounts; ++ bool has_pass_discard_request; ++ bool pass_discard_request; ++ bool has_pass_discard_snapshot; ++ bool pass_discard_snapshot; ++ bool has_pass_discard_other; ++ bool pass_discard_other; ++ bool has_discard_no_unref; ++ bool discard_no_unref; ++ Qcow2OverlapChecks *overlap_check; ++ bool has_cache_size; ++ int64_t cache_size; ++ bool has_l2_cache_size; ++ int64_t l2_cache_size; ++ bool has_l2_cache_entry_size; ++ int64_t l2_cache_entry_size; ++ bool has_refcount_cache_size; ++ int64_t refcount_cache_size; ++ bool has_cache_clean_interval; ++ int64_t cache_clean_interval; ++ BlockdevQcow2Encryption *encrypt; ++ BlockdevRef *data_file; ++}; ++ ++static inline BlockdevOptionsGenericCOWFormat *qapi_BlockdevOptionsQcow2_base(const BlockdevOptionsQcow2 *obj) ++{ ++ return (BlockdevOptionsGenericCOWFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQcow2, qapi_free_BlockdevOptionsQcow2) ++ ++struct SshHostKeyHash { ++ SshHostKeyCheckHashType type; ++ char *hash; ++}; ++ ++void qapi_free_SshHostKeyHash(SshHostKeyHash *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SshHostKeyHash, qapi_free_SshHostKeyHash) ++ ++struct q_obj_SshHostKeyCheck_base { ++ SshHostKeyCheckMode mode; ++}; ++ ++struct SshHostKeyCheck { ++ SshHostKeyCheckMode mode; ++ union { /* union tag is @mode */ ++ SshHostKeyHash hash; ++ } u; ++}; ++ ++void qapi_free_SshHostKeyCheck(SshHostKeyCheck *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SshHostKeyCheck, qapi_free_SshHostKeyCheck) ++ ++struct BlockdevOptionsSsh { ++ InetSocketAddress *server; ++ char *path; ++ char *user; ++ SshHostKeyCheck *host_key_check; ++}; ++ ++void qapi_free_BlockdevOptionsSsh(BlockdevOptionsSsh *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsSsh, qapi_free_BlockdevOptionsSsh) ++ ++struct BlkdebugInjectErrorOptions { ++ BlkdebugEvent event; ++ bool has_state; ++ int64_t state; ++ bool has_iotype; ++ BlkdebugIOType iotype; ++ bool has_q_errno; ++ int64_t q_errno; ++ bool has_sector; ++ int64_t sector; ++ bool has_once; ++ bool once; ++ bool has_immediately; ++ bool immediately; ++}; ++ ++void qapi_free_BlkdebugInjectErrorOptions(BlkdebugInjectErrorOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlkdebugInjectErrorOptions, qapi_free_BlkdebugInjectErrorOptions) ++ ++struct BlkdebugSetStateOptions { ++ BlkdebugEvent event; ++ bool has_state; ++ int64_t state; ++ int64_t new_state; ++}; ++ ++void qapi_free_BlkdebugSetStateOptions(BlkdebugSetStateOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlkdebugSetStateOptions, qapi_free_BlkdebugSetStateOptions) ++ ++struct BlkdebugInjectErrorOptionsList { ++ BlkdebugInjectErrorOptionsList *next; ++ BlkdebugInjectErrorOptions *value; ++}; ++ ++void qapi_free_BlkdebugInjectErrorOptionsList(BlkdebugInjectErrorOptionsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlkdebugInjectErrorOptionsList, qapi_free_BlkdebugInjectErrorOptionsList) ++ ++struct BlkdebugSetStateOptionsList { ++ BlkdebugSetStateOptionsList *next; ++ BlkdebugSetStateOptions *value; ++}; ++ ++void qapi_free_BlkdebugSetStateOptionsList(BlkdebugSetStateOptionsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlkdebugSetStateOptionsList, qapi_free_BlkdebugSetStateOptionsList) ++ ++struct BlockdevOptionsBlkdebug { ++ BlockdevRef *image; ++ char *config; ++ bool has_align; ++ int64_t align; ++ bool has_max_transfer; ++ int32_t max_transfer; ++ bool has_opt_write_zero; ++ int32_t opt_write_zero; ++ bool has_max_write_zero; ++ int32_t max_write_zero; ++ bool has_opt_discard; ++ int32_t opt_discard; ++ bool has_max_discard; ++ int32_t max_discard; ++ bool has_inject_error; ++ BlkdebugInjectErrorOptionsList *inject_error; ++ bool has_set_state; ++ BlkdebugSetStateOptionsList *set_state; ++ bool has_take_child_perms; ++ BlockPermissionList *take_child_perms; ++ bool has_unshare_child_perms; ++ BlockPermissionList *unshare_child_perms; ++}; ++ ++void qapi_free_BlockdevOptionsBlkdebug(BlockdevOptionsBlkdebug *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlkdebug, qapi_free_BlockdevOptionsBlkdebug) ++ ++struct BlockdevOptionsBlklogwrites { ++ BlockdevRef *file; ++ BlockdevRef *log; ++ bool has_log_sector_size; ++ uint32_t log_sector_size; ++ bool has_log_append; ++ bool log_append; ++ bool has_log_super_update_interval; ++ uint64_t log_super_update_interval; ++}; ++ ++void qapi_free_BlockdevOptionsBlklogwrites(BlockdevOptionsBlklogwrites *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlklogwrites, qapi_free_BlockdevOptionsBlklogwrites) ++ ++struct BlockdevOptionsBlkverify { ++ BlockdevRef *test; ++ BlockdevRef *raw; ++}; ++ ++void qapi_free_BlockdevOptionsBlkverify(BlockdevOptionsBlkverify *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlkverify, qapi_free_BlockdevOptionsBlkverify) ++ ++struct BlockdevOptionsBlkreplay { ++ BlockdevRef *image; ++}; ++ ++void qapi_free_BlockdevOptionsBlkreplay(BlockdevOptionsBlkreplay *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlkreplay, qapi_free_BlockdevOptionsBlkreplay) ++ ++struct BlockdevRefList { ++ BlockdevRefList *next; ++ BlockdevRef *value; ++}; ++ ++void qapi_free_BlockdevRefList(BlockdevRefList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevRefList, qapi_free_BlockdevRefList) ++ ++struct BlockdevOptionsQuorum { ++ bool has_blkverify; ++ bool blkverify; ++ BlockdevRefList *children; ++ int64_t vote_threshold; ++ bool has_rewrite_corrupted; ++ bool rewrite_corrupted; ++ bool has_read_pattern; ++ QuorumReadPattern read_pattern; ++}; ++ ++void qapi_free_BlockdevOptionsQuorum(BlockdevOptionsQuorum *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQuorum, qapi_free_BlockdevOptionsQuorum) ++ ++struct BlockdevOptionsGluster { ++ char *volume; ++ char *path; ++ SocketAddressList *server; ++ bool has_debug; ++ int64_t debug; ++ char *logfile; ++}; ++ ++void qapi_free_BlockdevOptionsGluster(BlockdevOptionsGluster *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsGluster, qapi_free_BlockdevOptionsGluster) ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsIoUring { ++ char *filename; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsIoUring(BlockdevOptionsIoUring *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsIoUring, qapi_free_BlockdevOptionsIoUring) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsNvmeIoUring { ++ char *path; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsNvmeIoUring(BlockdevOptionsNvmeIoUring *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNvmeIoUring, qapi_free_BlockdevOptionsNvmeIoUring) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsVirtioBlkVfioPci { ++ char *path; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVfioPci(BlockdevOptionsVirtioBlkVfioPci *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsVirtioBlkVfioPci, qapi_free_BlockdevOptionsVirtioBlkVfioPci) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsVirtioBlkVhostUser { ++ char *path; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVhostUser(BlockdevOptionsVirtioBlkVhostUser *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsVirtioBlkVhostUser, qapi_free_BlockdevOptionsVirtioBlkVhostUser) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++struct BlockdevOptionsVirtioBlkVhostVdpa { ++ char *path; ++}; ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVhostVdpa(BlockdevOptionsVirtioBlkVhostVdpa *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsVirtioBlkVhostVdpa, qapi_free_BlockdevOptionsVirtioBlkVhostVdpa) ++#endif /* defined(CONFIG_BLKIO) */ ++ ++struct BlockdevOptionsIscsi { ++ IscsiTransport transport; ++ char *portal; ++ char *target; ++ bool has_lun; ++ int64_t lun; ++ char *user; ++ char *password_secret; ++ char *initiator_name; ++ bool has_header_digest; ++ IscsiHeaderDigest header_digest; ++ bool has_timeout; ++ int64_t timeout; ++}; ++ ++void qapi_free_BlockdevOptionsIscsi(BlockdevOptionsIscsi *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsIscsi, qapi_free_BlockdevOptionsIscsi) ++ ++struct RbdEncryptionOptionsLUKSBase { ++ char *key_secret; ++}; ++ ++void qapi_free_RbdEncryptionOptionsLUKSBase(RbdEncryptionOptionsLUKSBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptionsLUKSBase, qapi_free_RbdEncryptionOptionsLUKSBase) ++ ++struct RbdEncryptionCreateOptionsLUKSBase { ++ /* Members inherited from RbdEncryptionOptionsLUKSBase: */ ++ char *key_secret; ++ /* Own members: */ ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++}; ++ ++static inline RbdEncryptionOptionsLUKSBase *qapi_RbdEncryptionCreateOptionsLUKSBase_base(const RbdEncryptionCreateOptionsLUKSBase *obj) ++{ ++ return (RbdEncryptionOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKSBase(RbdEncryptionCreateOptionsLUKSBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionCreateOptionsLUKSBase, qapi_free_RbdEncryptionCreateOptionsLUKSBase) ++ ++struct RbdEncryptionOptionsLUKS { ++ /* Members inherited from RbdEncryptionOptionsLUKSBase: */ ++ char *key_secret; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionOptionsLUKSBase *qapi_RbdEncryptionOptionsLUKS_base(const RbdEncryptionOptionsLUKS *obj) ++{ ++ return (RbdEncryptionOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKS(RbdEncryptionOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptionsLUKS, qapi_free_RbdEncryptionOptionsLUKS) ++ ++struct RbdEncryptionOptionsLUKS2 { ++ /* Members inherited from RbdEncryptionOptionsLUKSBase: */ ++ char *key_secret; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionOptionsLUKSBase *qapi_RbdEncryptionOptionsLUKS2_base(const RbdEncryptionOptionsLUKS2 *obj) ++{ ++ return (RbdEncryptionOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKS2(RbdEncryptionOptionsLUKS2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptionsLUKS2, qapi_free_RbdEncryptionOptionsLUKS2) ++ ++struct RbdEncryptionOptionsLUKSAny { ++ /* Members inherited from RbdEncryptionOptionsLUKSBase: */ ++ char *key_secret; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionOptionsLUKSBase *qapi_RbdEncryptionOptionsLUKSAny_base(const RbdEncryptionOptionsLUKSAny *obj) ++{ ++ return (RbdEncryptionOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKSAny(RbdEncryptionOptionsLUKSAny *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptionsLUKSAny, qapi_free_RbdEncryptionOptionsLUKSAny) ++ ++struct RbdEncryptionCreateOptionsLUKS { ++ /* Members inherited from RbdEncryptionCreateOptionsLUKSBase: */ ++ char *key_secret; ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionCreateOptionsLUKSBase *qapi_RbdEncryptionCreateOptionsLUKS_base(const RbdEncryptionCreateOptionsLUKS *obj) ++{ ++ return (RbdEncryptionCreateOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKS(RbdEncryptionCreateOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionCreateOptionsLUKS, qapi_free_RbdEncryptionCreateOptionsLUKS) ++ ++struct RbdEncryptionCreateOptionsLUKS2 { ++ /* Members inherited from RbdEncryptionCreateOptionsLUKSBase: */ ++ char *key_secret; ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++ /* Own members: */ ++}; ++ ++static inline RbdEncryptionCreateOptionsLUKSBase *qapi_RbdEncryptionCreateOptionsLUKS2_base(const RbdEncryptionCreateOptionsLUKS2 *obj) ++{ ++ return (RbdEncryptionCreateOptionsLUKSBase *)obj; ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKS2(RbdEncryptionCreateOptionsLUKS2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionCreateOptionsLUKS2, qapi_free_RbdEncryptionCreateOptionsLUKS2) ++ ++struct q_obj_RbdEncryptionOptions_base { ++ RbdImageEncryptionFormat format; ++ RbdEncryptionOptions *parent; ++}; ++ ++struct RbdEncryptionOptions { ++ RbdImageEncryptionFormat format; ++ RbdEncryptionOptions *parent; ++ union { /* union tag is @format */ ++ RbdEncryptionOptionsLUKS luks; ++ RbdEncryptionOptionsLUKS2 luks2; ++ RbdEncryptionOptionsLUKSAny luks_any; ++ } u; ++}; ++ ++void qapi_free_RbdEncryptionOptions(RbdEncryptionOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionOptions, qapi_free_RbdEncryptionOptions) ++ ++struct q_obj_RbdEncryptionCreateOptions_base { ++ RbdImageEncryptionFormat format; ++}; ++ ++struct RbdEncryptionCreateOptions { ++ RbdImageEncryptionFormat format; ++ union { /* union tag is @format */ ++ RbdEncryptionCreateOptionsLUKS luks; ++ RbdEncryptionCreateOptionsLUKS2 luks2; ++ } u; ++}; ++ ++void qapi_free_RbdEncryptionCreateOptions(RbdEncryptionCreateOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdEncryptionCreateOptions, qapi_free_RbdEncryptionCreateOptions) ++ ++struct RbdAuthModeList { ++ RbdAuthModeList *next; ++ RbdAuthMode value; ++}; ++ ++void qapi_free_RbdAuthModeList(RbdAuthModeList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RbdAuthModeList, qapi_free_RbdAuthModeList) ++ ++struct BlockdevOptionsRbd { ++ char *pool; ++ char *q_namespace; ++ char *image; ++ char *conf; ++ char *snapshot; ++ RbdEncryptionOptions *encrypt; ++ char *user; ++ bool has_auth_client_required; ++ RbdAuthModeList *auth_client_required; ++ char *key_secret; ++ bool has_server; ++ InetSocketAddressBaseList *server; ++}; ++ ++void qapi_free_BlockdevOptionsRbd(BlockdevOptionsRbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsRbd, qapi_free_BlockdevOptionsRbd) ++ ++#if defined(CONFIG_REPLICATION) ++struct BlockdevOptionsReplication { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ ReplicationMode mode; ++ char *top_id; ++}; ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsReplication_base(const BlockdevOptionsReplication *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsReplication(BlockdevOptionsReplication *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsReplication, qapi_free_BlockdevOptionsReplication) ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++struct NFSServer { ++ NFSTransport type; ++ char *host; ++}; ++ ++void qapi_free_NFSServer(NFSServer *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(NFSServer, qapi_free_NFSServer) ++ ++struct BlockdevOptionsNfs { ++ NFSServer *server; ++ char *path; ++ bool has_user; ++ int64_t user; ++ bool has_group; ++ int64_t group; ++ bool has_tcp_syn_count; ++ int64_t tcp_syn_count; ++ bool has_readahead_size; ++ int64_t readahead_size; ++ bool has_page_cache_size; ++ int64_t page_cache_size; ++ bool has_debug; ++ int64_t debug; ++}; ++ ++void qapi_free_BlockdevOptionsNfs(BlockdevOptionsNfs *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNfs, qapi_free_BlockdevOptionsNfs) ++ ++struct BlockdevOptionsCurlBase { ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++}; ++ ++void qapi_free_BlockdevOptionsCurlBase(BlockdevOptionsCurlBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlBase, qapi_free_BlockdevOptionsCurlBase) ++ ++struct BlockdevOptionsCurlHttp { ++ /* Members inherited from BlockdevOptionsCurlBase: */ ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++ /* Own members: */ ++ char *cookie; ++ char *cookie_secret; ++}; ++ ++static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlHttp_base(const BlockdevOptionsCurlHttp *obj) ++{ ++ return (BlockdevOptionsCurlBase *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCurlHttp(BlockdevOptionsCurlHttp *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlHttp, qapi_free_BlockdevOptionsCurlHttp) ++ ++struct BlockdevOptionsCurlHttps { ++ /* Members inherited from BlockdevOptionsCurlBase: */ ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++ /* Own members: */ ++ char *cookie; ++ bool has_sslverify; ++ bool sslverify; ++ char *cookie_secret; ++}; ++ ++static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlHttps_base(const BlockdevOptionsCurlHttps *obj) ++{ ++ return (BlockdevOptionsCurlBase *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCurlHttps(BlockdevOptionsCurlHttps *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlHttps, qapi_free_BlockdevOptionsCurlHttps) ++ ++struct BlockdevOptionsCurlFtp { ++ /* Members inherited from BlockdevOptionsCurlBase: */ ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++ /* Own members: */ ++}; ++ ++static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlFtp_base(const BlockdevOptionsCurlFtp *obj) ++{ ++ return (BlockdevOptionsCurlBase *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCurlFtp(BlockdevOptionsCurlFtp *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlFtp, qapi_free_BlockdevOptionsCurlFtp) ++ ++struct BlockdevOptionsCurlFtps { ++ /* Members inherited from BlockdevOptionsCurlBase: */ ++ char *url; ++ bool has_readahead; ++ int64_t readahead; ++ bool has_timeout; ++ int64_t timeout; ++ char *username; ++ char *password_secret; ++ char *proxy_username; ++ char *proxy_password_secret; ++ /* Own members: */ ++ bool has_sslverify; ++ bool sslverify; ++}; ++ ++static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlFtps_base(const BlockdevOptionsCurlFtps *obj) ++{ ++ return (BlockdevOptionsCurlBase *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCurlFtps(BlockdevOptionsCurlFtps *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlFtps, qapi_free_BlockdevOptionsCurlFtps) ++ ++struct BlockdevOptionsNbd { ++ SocketAddress *server; ++ char *export; ++ char *tls_creds; ++ char *tls_hostname; ++ char *x_dirty_bitmap; ++ bool has_reconnect_delay; ++ uint32_t reconnect_delay; ++ bool has_open_timeout; ++ uint32_t open_timeout; ++}; ++ ++void qapi_free_BlockdevOptionsNbd(BlockdevOptionsNbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNbd, qapi_free_BlockdevOptionsNbd) ++ ++struct BlockdevOptionsRaw { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ bool has_offset; ++ int64_t offset; ++ bool has_size; ++ int64_t size; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsRaw_base(const BlockdevOptionsRaw *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsRaw(BlockdevOptionsRaw *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsRaw, qapi_free_BlockdevOptionsRaw) ++ ++struct BlockdevOptionsThrottle { ++ char *throttle_group; ++ BlockdevRef *file; ++}; ++ ++void qapi_free_BlockdevOptionsThrottle(BlockdevOptionsThrottle *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsThrottle, qapi_free_BlockdevOptionsThrottle) ++ ++struct BlockdevOptionsCor { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ char *bottom; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsCor_base(const BlockdevOptionsCor *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCor(BlockdevOptionsCor *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCor, qapi_free_BlockdevOptionsCor) ++ ++struct BlockdevOptionsCbw { ++ /* Members inherited from BlockdevOptionsGenericFormat: */ ++ BlockdevRef *file; ++ /* Own members: */ ++ BlockdevRef *target; ++ BlockDirtyBitmap *bitmap; ++ bool has_on_cbw_error; ++ OnCbwError on_cbw_error; ++ bool has_cbw_timeout; ++ uint32_t cbw_timeout; ++}; ++ ++static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsCbw_base(const BlockdevOptionsCbw *obj) ++{ ++ return (BlockdevOptionsGenericFormat *)obj; ++} ++ ++void qapi_free_BlockdevOptionsCbw(BlockdevOptionsCbw *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCbw, qapi_free_BlockdevOptionsCbw) ++ ++struct q_obj_BlockdevOptions_base { ++ BlockdevDriver driver; ++ char *node_name; ++ bool has_discard; ++ BlockdevDiscardOptions discard; ++ BlockdevCacheOptions *cache; ++ bool has_read_only; ++ bool read_only; ++ bool has_auto_read_only; ++ bool auto_read_only; ++ bool has_force_share; ++ bool force_share; ++ bool has_detect_zeroes; ++ BlockdevDetectZeroesOptions detect_zeroes; ++}; ++ ++struct BlockdevOptions { ++ BlockdevDriver driver; ++ char *node_name; ++ bool has_discard; ++ BlockdevDiscardOptions discard; ++ BlockdevCacheOptions *cache; ++ bool has_read_only; ++ bool read_only; ++ bool has_auto_read_only; ++ bool auto_read_only; ++ bool has_force_share; ++ bool force_share; ++ bool has_detect_zeroes; ++ BlockdevDetectZeroesOptions detect_zeroes; ++ union { /* union tag is @driver */ ++ BlockdevOptionsBlkdebug blkdebug; ++ BlockdevOptionsBlklogwrites blklogwrites; ++ BlockdevOptionsBlkverify blkverify; ++ BlockdevOptionsBlkreplay blkreplay; ++ BlockdevOptionsGenericFormat bochs; ++ BlockdevOptionsGenericFormat cloop; ++ BlockdevOptionsGenericFormat compress; ++ BlockdevOptionsCbw copy_before_write; ++ BlockdevOptionsCor copy_on_read; ++ BlockdevOptionsGenericFormat dmg; ++ BlockdevOptionsFile file; ++ BlockdevOptionsCurlFtp ftp; ++ BlockdevOptionsCurlFtps ftps; ++ BlockdevOptionsGluster gluster; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BlockdevOptionsFile host_cdrom; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ BlockdevOptionsFile host_device; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ BlockdevOptionsCurlHttp http; ++ BlockdevOptionsCurlHttps https; ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsIoUring io_uring; ++#endif /* defined(CONFIG_BLKIO) */ ++ BlockdevOptionsIscsi iscsi; ++ BlockdevOptionsLUKS luks; ++ BlockdevOptionsNbd nbd; ++ BlockdevOptionsNfs nfs; ++ BlockdevOptionsNull null_aio; ++ BlockdevOptionsNull null_co; ++ BlockdevOptionsNVMe nvme; ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsNvmeIoUring nvme_io_uring; ++#endif /* defined(CONFIG_BLKIO) */ ++ BlockdevOptionsGenericFormat parallels; ++ BlockdevOptionsPreallocate preallocate; ++ BlockdevOptionsQcow2 qcow2; ++ BlockdevOptionsQcow qcow; ++ BlockdevOptionsGenericCOWFormat qed; ++ BlockdevOptionsQuorum quorum; ++ BlockdevOptionsRaw raw; ++ BlockdevOptionsRbd rbd; ++#if defined(CONFIG_REPLICATION) ++ BlockdevOptionsReplication replication; ++#endif /* defined(CONFIG_REPLICATION) */ ++ BlockdevOptionsGenericFormat snapshot_access; ++ BlockdevOptionsSsh ssh; ++ BlockdevOptionsThrottle throttle; ++ BlockdevOptionsGenericFormat vdi; ++ BlockdevOptionsGenericFormat vhdx; ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsVirtioBlkVfioPci virtio_blk_vfio_pci; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsVirtioBlkVhostUser virtio_blk_vhost_user; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ BlockdevOptionsVirtioBlkVhostVdpa virtio_blk_vhost_vdpa; ++#endif /* defined(CONFIG_BLKIO) */ ++ BlockdevOptionsGenericCOWFormat vmdk; ++ BlockdevOptionsGenericFormat vpc; ++ BlockdevOptionsVVFAT vvfat; ++ } u; ++}; ++ ++void qapi_free_BlockdevOptions(BlockdevOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptions, qapi_free_BlockdevOptions) ++ ++struct BlockdevRef { ++ QType type; ++ union { /* union tag is @type */ ++ BlockdevOptions definition; ++ char *reference; ++ } u; ++}; ++ ++void qapi_free_BlockdevRef(BlockdevRef *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevRef, qapi_free_BlockdevRef) ++ ++struct BlockdevRefOrNull { ++ QType type; ++ union { /* union tag is @type */ ++ BlockdevOptions definition; ++ char *reference; ++ QNull *null; ++ } u; ++}; ++ ++void qapi_free_BlockdevRefOrNull(BlockdevRefOrNull *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevRefOrNull, qapi_free_BlockdevRefOrNull) ++ ++struct BlockdevOptionsList { ++ BlockdevOptionsList *next; ++ BlockdevOptions *value; ++}; ++ ++void qapi_free_BlockdevOptionsList(BlockdevOptionsList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsList, qapi_free_BlockdevOptionsList) ++ ++struct q_obj_blockdev_reopen_arg { ++ BlockdevOptionsList *options; ++}; ++ ++struct q_obj_blockdev_del_arg { ++ char *node_name; ++}; ++ ++struct BlockdevCreateOptionsFile { ++ char *filename; ++ uint64_t size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++ bool has_nocow; ++ bool nocow; ++ bool has_extent_size_hint; ++ uint64_t extent_size_hint; ++}; ++ ++void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsFile, qapi_free_BlockdevCreateOptionsFile) ++ ++struct BlockdevCreateOptionsGluster { ++ BlockdevOptionsGluster *location; ++ uint64_t size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++}; ++ ++void qapi_free_BlockdevCreateOptionsGluster(BlockdevCreateOptionsGluster *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsGluster, qapi_free_BlockdevCreateOptionsGluster) ++ ++struct BlockdevCreateOptionsLUKS { ++ /* Members inherited from QCryptoBlockCreateOptionsLUKS: */ ++ char *key_secret; ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++ bool has_cipher_mode; ++ QCryptoCipherMode cipher_mode; ++ bool has_ivgen_alg; ++ QCryptoIVGenAlgorithm ivgen_alg; ++ bool has_ivgen_hash_alg; ++ QCryptoHashAlgorithm ivgen_hash_alg; ++ bool has_hash_alg; ++ QCryptoHashAlgorithm hash_alg; ++ bool has_iter_time; ++ int64_t iter_time; ++ /* Own members: */ ++ BlockdevRef *file; ++ BlockdevRef *header; ++ uint64_t size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++}; ++ ++static inline QCryptoBlockCreateOptionsLUKS *qapi_BlockdevCreateOptionsLUKS_base(const BlockdevCreateOptionsLUKS *obj) ++{ ++ return (QCryptoBlockCreateOptionsLUKS *)obj; ++} ++ ++void qapi_free_BlockdevCreateOptionsLUKS(BlockdevCreateOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsLUKS, qapi_free_BlockdevCreateOptionsLUKS) ++ ++struct BlockdevCreateOptionsNfs { ++ BlockdevOptionsNfs *location; ++ uint64_t size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsNfs(BlockdevCreateOptionsNfs *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsNfs, qapi_free_BlockdevCreateOptionsNfs) ++ ++struct BlockdevCreateOptionsParallels { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_cluster_size; ++ uint64_t cluster_size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsParallels(BlockdevCreateOptionsParallels *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsParallels, qapi_free_BlockdevCreateOptionsParallels) ++ ++struct BlockdevCreateOptionsQcow { ++ BlockdevRef *file; ++ uint64_t size; ++ char *backing_file; ++ QCryptoBlockCreateOptions *encrypt; ++}; ++ ++void qapi_free_BlockdevCreateOptionsQcow(BlockdevCreateOptionsQcow *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsQcow, qapi_free_BlockdevCreateOptionsQcow) ++ ++struct BlockdevCreateOptionsQcow2 { ++ BlockdevRef *file; ++ BlockdevRef *data_file; ++ bool has_data_file_raw; ++ bool data_file_raw; ++ bool has_extended_l2; ++ bool extended_l2; ++ uint64_t size; ++ bool has_version; ++ BlockdevQcow2Version version; ++ char *backing_file; ++ bool has_backing_fmt; ++ BlockdevDriver backing_fmt; ++ QCryptoBlockCreateOptions *encrypt; ++ bool has_cluster_size; ++ uint64_t cluster_size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++ bool has_lazy_refcounts; ++ bool lazy_refcounts; ++ bool has_refcount_bits; ++ int64_t refcount_bits; ++ bool has_compression_type; ++ Qcow2CompressionType compression_type; ++}; ++ ++void qapi_free_BlockdevCreateOptionsQcow2(BlockdevCreateOptionsQcow2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsQcow2, qapi_free_BlockdevCreateOptionsQcow2) ++ ++struct BlockdevCreateOptionsQed { ++ BlockdevRef *file; ++ uint64_t size; ++ char *backing_file; ++ bool has_backing_fmt; ++ BlockdevDriver backing_fmt; ++ bool has_cluster_size; ++ uint64_t cluster_size; ++ bool has_table_size; ++ int64_t table_size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsQed(BlockdevCreateOptionsQed *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsQed, qapi_free_BlockdevCreateOptionsQed) ++ ++struct BlockdevCreateOptionsRbd { ++ BlockdevOptionsRbd *location; ++ uint64_t size; ++ bool has_cluster_size; ++ uint64_t cluster_size; ++ RbdEncryptionCreateOptions *encrypt; ++}; ++ ++void qapi_free_BlockdevCreateOptionsRbd(BlockdevCreateOptionsRbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsRbd, qapi_free_BlockdevCreateOptionsRbd) ++ ++struct BlockdevCreateOptionsVmdk { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_extents; ++ BlockdevRefList *extents; ++ bool has_subformat; ++ BlockdevVmdkSubformat subformat; ++ char *backing_file; ++ bool has_adapter_type; ++ BlockdevVmdkAdapterType adapter_type; ++ char *hwversion; ++ char *toolsversion; ++ bool has_zeroed_grain; ++ bool zeroed_grain; ++}; ++ ++void qapi_free_BlockdevCreateOptionsVmdk(BlockdevCreateOptionsVmdk *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsVmdk, qapi_free_BlockdevCreateOptionsVmdk) ++ ++struct BlockdevCreateOptionsSsh { ++ BlockdevOptionsSsh *location; ++ uint64_t size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsSsh(BlockdevCreateOptionsSsh *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsSsh, qapi_free_BlockdevCreateOptionsSsh) ++ ++struct BlockdevCreateOptionsVdi { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_preallocation; ++ PreallocMode preallocation; ++}; ++ ++void qapi_free_BlockdevCreateOptionsVdi(BlockdevCreateOptionsVdi *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsVdi, qapi_free_BlockdevCreateOptionsVdi) ++ ++struct BlockdevCreateOptionsVhdx { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_log_size; ++ uint64_t log_size; ++ bool has_block_size; ++ uint64_t block_size; ++ bool has_subformat; ++ BlockdevVhdxSubformat subformat; ++ bool has_block_state_zero; ++ bool block_state_zero; ++}; ++ ++void qapi_free_BlockdevCreateOptionsVhdx(BlockdevCreateOptionsVhdx *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsVhdx, qapi_free_BlockdevCreateOptionsVhdx) ++ ++struct BlockdevCreateOptionsVpc { ++ BlockdevRef *file; ++ uint64_t size; ++ bool has_subformat; ++ BlockdevVpcSubformat subformat; ++ bool has_force_size; ++ bool force_size; ++}; ++ ++void qapi_free_BlockdevCreateOptionsVpc(BlockdevCreateOptionsVpc *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsVpc, qapi_free_BlockdevCreateOptionsVpc) ++ ++struct q_obj_BlockdevCreateOptions_base { ++ BlockdevDriver driver; ++}; ++ ++struct BlockdevCreateOptions { ++ BlockdevDriver driver; ++ union { /* union tag is @driver */ ++ BlockdevCreateOptionsFile file; ++ BlockdevCreateOptionsGluster gluster; ++ BlockdevCreateOptionsLUKS luks; ++ BlockdevCreateOptionsNfs nfs; ++ BlockdevCreateOptionsParallels parallels; ++ BlockdevCreateOptionsQcow qcow; ++ BlockdevCreateOptionsQcow2 qcow2; ++ BlockdevCreateOptionsQed qed; ++ BlockdevCreateOptionsRbd rbd; ++ BlockdevCreateOptionsSsh ssh; ++ BlockdevCreateOptionsVdi vdi; ++ BlockdevCreateOptionsVhdx vhdx; ++ BlockdevCreateOptionsVmdk vmdk; ++ BlockdevCreateOptionsVpc vpc; ++ } u; ++}; ++ ++void qapi_free_BlockdevCreateOptions(BlockdevCreateOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptions, qapi_free_BlockdevCreateOptions) ++ ++struct q_obj_blockdev_create_arg { ++ char *job_id; ++ BlockdevCreateOptions *options; ++}; ++ ++struct BlockdevAmendOptionsLUKS { ++ /* Members inherited from QCryptoBlockAmendOptionsLUKS: */ ++ QCryptoBlockLUKSKeyslotState state; ++ char *new_secret; ++ char *old_secret; ++ bool has_keyslot; ++ int64_t keyslot; ++ bool has_iter_time; ++ int64_t iter_time; ++ char *secret; ++ /* Own members: */ ++}; ++ ++static inline QCryptoBlockAmendOptionsLUKS *qapi_BlockdevAmendOptionsLUKS_base(const BlockdevAmendOptionsLUKS *obj) ++{ ++ return (QCryptoBlockAmendOptionsLUKS *)obj; ++} ++ ++void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptionsLUKS, qapi_free_BlockdevAmendOptionsLUKS) ++ ++struct BlockdevAmendOptionsQcow2 { ++ QCryptoBlockAmendOptions *encrypt; ++}; ++ ++void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptionsQcow2, qapi_free_BlockdevAmendOptionsQcow2) ++ ++struct q_obj_BlockdevAmendOptions_base { ++ BlockdevDriver driver; ++}; ++ ++struct BlockdevAmendOptions { ++ BlockdevDriver driver; ++ union { /* union tag is @driver */ ++ BlockdevAmendOptionsLUKS luks; ++ BlockdevAmendOptionsQcow2 qcow2; ++ } u; ++}; ++ ++void qapi_free_BlockdevAmendOptions(BlockdevAmendOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptions, qapi_free_BlockdevAmendOptions) ++ ++struct q_obj_x_blockdev_amend_arg { ++ char *job_id; ++ char *node_name; ++ BlockdevAmendOptions *options; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_BLOCK_IMAGE_CORRUPTED_arg { ++ char *device; ++ char *node_name; ++ char *msg; ++ bool has_offset; ++ int64_t offset; ++ bool has_size; ++ int64_t size; ++ bool fatal; ++}; ++ ++struct q_obj_BLOCK_IO_ERROR_arg { ++ char *device; ++ char *node_name; ++ IoOperationType operation; ++ BlockErrorAction action; ++ bool has_nospace; ++ bool nospace; ++ char *reason; ++}; ++ ++struct q_obj_BLOCK_JOB_COMPLETED_arg { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ int64_t speed; ++ char *error; ++}; ++ ++struct q_obj_BLOCK_JOB_CANCELLED_arg { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ int64_t speed; ++}; ++ ++struct q_obj_BLOCK_JOB_ERROR_arg { ++ char *device; ++ IoOperationType operation; ++ BlockErrorAction action; ++}; ++ ++struct q_obj_BLOCK_JOB_READY_arg { ++ JobType type; ++ char *device; ++ int64_t len; ++ int64_t offset; ++ int64_t speed; ++}; ++ ++struct q_obj_BLOCK_JOB_PENDING_arg { ++ JobType type; ++ char *id; ++}; ++ ++struct q_obj_BLOCK_WRITE_THRESHOLD_arg { ++ char *node_name; ++ uint64_t amount_exceeded; ++ uint64_t write_threshold; ++}; ++ ++struct q_obj_block_set_write_threshold_arg { ++ char *node_name; ++ uint64_t write_threshold; ++}; ++ ++struct q_obj_x_blockdev_change_arg { ++ char *parent; ++ char *child; ++ char *node; ++}; ++ ++struct q_obj_x_blockdev_set_iothread_arg { ++ char *node_name; ++ StrOrNull *iothread; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_QUORUM_FAILURE_arg { ++ char *reference; ++ int64_t sector_num; ++ int64_t sectors_count; ++}; ++ ++struct q_obj_QUORUM_REPORT_BAD_arg { ++ QuorumOpType type; ++ char *error; ++ char *node_name; ++ int64_t sector_num; ++ int64_t sectors_count; ++}; ++ ++struct BlockdevSnapshotInternal { ++ char *device; ++ char *name; ++}; ++ ++void qapi_free_BlockdevSnapshotInternal(BlockdevSnapshotInternal *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevSnapshotInternal, qapi_free_BlockdevSnapshotInternal) ++ ++struct q_obj_blockdev_snapshot_delete_internal_sync_arg { ++ char *device; ++ char *id; ++ char *name; ++}; ++ ++struct BlockGraphInfoList { ++ BlockGraphInfoList *next; ++ BlockGraphInfo *value; ++}; ++ ++void qapi_free_BlockGraphInfoList(BlockGraphInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockGraphInfoList, qapi_free_BlockGraphInfoList) ++ ++struct DummyBlockCoreForceArrays { ++ BlockGraphInfoList *unused_block_graph_info; ++}; ++ ++void qapi_free_DummyBlockCoreForceArrays(DummyBlockCoreForceArrays *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(DummyBlockCoreForceArrays, qapi_free_DummyBlockCoreForceArrays) ++ ++#endif /* QAPI_TYPES_BLOCK_CORE_H */ +diff --git a/include/qapi/qapi-types-block-export.h b/include/qapi/qapi-types-block-export.h +new file mode 100644 +index 00000000..62e568e8 +--- /dev/null ++++ b/include/qapi/qapi-types-block-export.h +@@ -0,0 +1,274 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_BLOCK_EXPORT_H ++#define QAPI_TYPES_BLOCK_EXPORT_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qapi-types-sockets.h" ++#include "qapi-types-block-core.h" ++ ++typedef struct NbdServerOptions NbdServerOptions; ++ ++typedef struct q_obj_nbd_server_start_arg q_obj_nbd_server_start_arg; ++ ++typedef struct BlockExportOptionsNbdBase BlockExportOptionsNbdBase; ++ ++typedef struct BlockExportOptionsNbd BlockExportOptionsNbd; ++ ++typedef struct BlockExportOptionsVhostUserBlk BlockExportOptionsVhostUserBlk; ++ ++typedef enum FuseExportAllowOther { ++ FUSE_EXPORT_ALLOW_OTHER_OFF, ++ FUSE_EXPORT_ALLOW_OTHER_ON, ++ FUSE_EXPORT_ALLOW_OTHER_AUTO, ++ FUSE_EXPORT_ALLOW_OTHER__MAX, ++} FuseExportAllowOther; ++ ++#define FuseExportAllowOther_str(val) \ ++ qapi_enum_lookup(&FuseExportAllowOther_lookup, (val)) ++ ++extern const QEnumLookup FuseExportAllowOther_lookup; ++ ++#if defined(CONFIG_FUSE) ++typedef struct BlockExportOptionsFuse BlockExportOptionsFuse; ++#endif /* defined(CONFIG_FUSE) */ ++ ++typedef struct BlockExportOptionsVduseBlk BlockExportOptionsVduseBlk; ++ ++typedef struct NbdServerAddOptions NbdServerAddOptions; ++ ++typedef enum BlockExportRemoveMode { ++ BLOCK_EXPORT_REMOVE_MODE_SAFE, ++ BLOCK_EXPORT_REMOVE_MODE_HARD, ++ BLOCK_EXPORT_REMOVE_MODE__MAX, ++} BlockExportRemoveMode; ++ ++#define BlockExportRemoveMode_str(val) \ ++ qapi_enum_lookup(&BlockExportRemoveMode_lookup, (val)) ++ ++extern const QEnumLookup BlockExportRemoveMode_lookup; ++ ++typedef struct q_obj_nbd_server_remove_arg q_obj_nbd_server_remove_arg; ++ ++typedef enum BlockExportType { ++ BLOCK_EXPORT_TYPE_NBD, ++#if defined(CONFIG_VHOST_USER_BLK_SERVER) ++ BLOCK_EXPORT_TYPE_VHOST_USER_BLK, ++#endif /* defined(CONFIG_VHOST_USER_BLK_SERVER) */ ++#if defined(CONFIG_FUSE) ++ BLOCK_EXPORT_TYPE_FUSE, ++#endif /* defined(CONFIG_FUSE) */ ++#if defined(CONFIG_VDUSE_BLK_EXPORT) ++ BLOCK_EXPORT_TYPE_VDUSE_BLK, ++#endif /* defined(CONFIG_VDUSE_BLK_EXPORT) */ ++ BLOCK_EXPORT_TYPE__MAX, ++} BlockExportType; ++ ++#define BlockExportType_str(val) \ ++ qapi_enum_lookup(&BlockExportType_lookup, (val)) ++ ++extern const QEnumLookup BlockExportType_lookup; ++ ++typedef struct q_obj_BlockExportOptions_base q_obj_BlockExportOptions_base; ++ ++typedef struct BlockExportOptions BlockExportOptions; ++ ++typedef struct q_obj_block_export_del_arg q_obj_block_export_del_arg; ++ ++typedef struct q_obj_BLOCK_EXPORT_DELETED_arg q_obj_BLOCK_EXPORT_DELETED_arg; ++ ++typedef struct BlockExportInfo BlockExportInfo; ++ ++typedef struct BlockExportInfoList BlockExportInfoList; ++ ++struct NbdServerOptions { ++ SocketAddress *addr; ++ char *tls_creds; ++ char *tls_authz; ++ bool has_max_connections; ++ uint32_t max_connections; ++}; ++ ++void qapi_free_NbdServerOptions(NbdServerOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(NbdServerOptions, qapi_free_NbdServerOptions) ++ ++struct q_obj_nbd_server_start_arg { ++ SocketAddressLegacy *addr; ++ char *tls_creds; ++ char *tls_authz; ++ bool has_max_connections; ++ uint32_t max_connections; ++}; ++ ++struct BlockExportOptionsNbdBase { ++ char *name; ++ char *description; ++}; ++ ++void qapi_free_BlockExportOptionsNbdBase(BlockExportOptionsNbdBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsNbdBase, qapi_free_BlockExportOptionsNbdBase) ++ ++struct BlockExportOptionsNbd { ++ /* Members inherited from BlockExportOptionsNbdBase: */ ++ char *name; ++ char *description; ++ /* Own members: */ ++ bool has_bitmaps; ++ BlockDirtyBitmapOrStrList *bitmaps; ++ bool has_allocation_depth; ++ bool allocation_depth; ++}; ++ ++static inline BlockExportOptionsNbdBase *qapi_BlockExportOptionsNbd_base(const BlockExportOptionsNbd *obj) ++{ ++ return (BlockExportOptionsNbdBase *)obj; ++} ++ ++void qapi_free_BlockExportOptionsNbd(BlockExportOptionsNbd *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsNbd, qapi_free_BlockExportOptionsNbd) ++ ++struct BlockExportOptionsVhostUserBlk { ++ SocketAddress *addr; ++ bool has_logical_block_size; ++ uint64_t logical_block_size; ++ bool has_num_queues; ++ uint16_t num_queues; ++}; ++ ++void qapi_free_BlockExportOptionsVhostUserBlk(BlockExportOptionsVhostUserBlk *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsVhostUserBlk, qapi_free_BlockExportOptionsVhostUserBlk) ++ ++#if defined(CONFIG_FUSE) ++struct BlockExportOptionsFuse { ++ char *mountpoint; ++ bool has_growable; ++ bool growable; ++ bool has_allow_other; ++ FuseExportAllowOther allow_other; ++}; ++#endif /* defined(CONFIG_FUSE) */ ++ ++#if defined(CONFIG_FUSE) ++void qapi_free_BlockExportOptionsFuse(BlockExportOptionsFuse *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsFuse, qapi_free_BlockExportOptionsFuse) ++#endif /* defined(CONFIG_FUSE) */ ++ ++struct BlockExportOptionsVduseBlk { ++ char *name; ++ bool has_num_queues; ++ uint16_t num_queues; ++ bool has_queue_size; ++ uint16_t queue_size; ++ bool has_logical_block_size; ++ uint64_t logical_block_size; ++ char *serial; ++}; ++ ++void qapi_free_BlockExportOptionsVduseBlk(BlockExportOptionsVduseBlk *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptionsVduseBlk, qapi_free_BlockExportOptionsVduseBlk) ++ ++struct NbdServerAddOptions { ++ /* Members inherited from BlockExportOptionsNbdBase: */ ++ char *name; ++ char *description; ++ /* Own members: */ ++ char *device; ++ bool has_writable; ++ bool writable; ++ char *bitmap; ++}; ++ ++static inline BlockExportOptionsNbdBase *qapi_NbdServerAddOptions_base(const NbdServerAddOptions *obj) ++{ ++ return (BlockExportOptionsNbdBase *)obj; ++} ++ ++void qapi_free_NbdServerAddOptions(NbdServerAddOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(NbdServerAddOptions, qapi_free_NbdServerAddOptions) ++ ++struct q_obj_nbd_server_remove_arg { ++ char *name; ++ bool has_mode; ++ BlockExportRemoveMode mode; ++}; ++ ++struct q_obj_BlockExportOptions_base { ++ BlockExportType type; ++ char *id; ++ bool has_fixed_iothread; ++ bool fixed_iothread; ++ char *iothread; ++ char *node_name; ++ bool has_writable; ++ bool writable; ++ bool has_writethrough; ++ bool writethrough; ++}; ++ ++struct BlockExportOptions { ++ BlockExportType type; ++ char *id; ++ bool has_fixed_iothread; ++ bool fixed_iothread; ++ char *iothread; ++ char *node_name; ++ bool has_writable; ++ bool writable; ++ bool has_writethrough; ++ bool writethrough; ++ union { /* union tag is @type */ ++ BlockExportOptionsNbd nbd; ++#if defined(CONFIG_VHOST_USER_BLK_SERVER) ++ BlockExportOptionsVhostUserBlk vhost_user_blk; ++#endif /* defined(CONFIG_VHOST_USER_BLK_SERVER) */ ++#if defined(CONFIG_FUSE) ++ BlockExportOptionsFuse fuse; ++#endif /* defined(CONFIG_FUSE) */ ++#if defined(CONFIG_VDUSE_BLK_EXPORT) ++ BlockExportOptionsVduseBlk vduse_blk; ++#endif /* defined(CONFIG_VDUSE_BLK_EXPORT) */ ++ } u; ++}; ++ ++void qapi_free_BlockExportOptions(BlockExportOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportOptions, qapi_free_BlockExportOptions) ++ ++struct q_obj_block_export_del_arg { ++ char *id; ++ bool has_mode; ++ BlockExportRemoveMode mode; ++}; ++ ++struct q_obj_BLOCK_EXPORT_DELETED_arg { ++ char *id; ++}; ++ ++struct BlockExportInfo { ++ char *id; ++ BlockExportType type; ++ char *node_name; ++ bool shutting_down; ++}; ++ ++void qapi_free_BlockExportInfo(BlockExportInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportInfo, qapi_free_BlockExportInfo) ++ ++struct BlockExportInfoList { ++ BlockExportInfoList *next; ++ BlockExportInfo *value; ++}; ++ ++void qapi_free_BlockExportInfoList(BlockExportInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockExportInfoList, qapi_free_BlockExportInfoList) ++ ++#endif /* QAPI_TYPES_BLOCK_EXPORT_H */ +diff --git a/include/qapi/qapi-types-block.h b/include/qapi/qapi-types-block.h +new file mode 100644 +index 00000000..73e33b4b +--- /dev/null ++++ b/include/qapi/qapi-types-block.h +@@ -0,0 +1,161 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_BLOCK_H ++#define QAPI_TYPES_BLOCK_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qapi-types-block-core.h" ++ ++typedef enum BiosAtaTranslation { ++ BIOS_ATA_TRANSLATION_AUTO, ++ BIOS_ATA_TRANSLATION_NONE, ++ BIOS_ATA_TRANSLATION_LBA, ++ BIOS_ATA_TRANSLATION_LARGE, ++ BIOS_ATA_TRANSLATION_RECHS, ++ BIOS_ATA_TRANSLATION__MAX, ++} BiosAtaTranslation; ++ ++#define BiosAtaTranslation_str(val) \ ++ qapi_enum_lookup(&BiosAtaTranslation_lookup, (val)) ++ ++extern const QEnumLookup BiosAtaTranslation_lookup; ++ ++typedef enum FloppyDriveType { ++ FLOPPY_DRIVE_TYPE_144, ++ FLOPPY_DRIVE_TYPE_288, ++ FLOPPY_DRIVE_TYPE_120, ++ FLOPPY_DRIVE_TYPE_NONE, ++ FLOPPY_DRIVE_TYPE_AUTO, ++ FLOPPY_DRIVE_TYPE__MAX, ++} FloppyDriveType; ++ ++#define FloppyDriveType_str(val) \ ++ qapi_enum_lookup(&FloppyDriveType_lookup, (val)) ++ ++extern const QEnumLookup FloppyDriveType_lookup; ++ ++typedef struct PRManagerInfo PRManagerInfo; ++ ++typedef struct PRManagerInfoList PRManagerInfoList; ++ ++typedef struct q_obj_eject_arg q_obj_eject_arg; ++ ++typedef struct q_obj_blockdev_open_tray_arg q_obj_blockdev_open_tray_arg; ++ ++typedef struct q_obj_blockdev_close_tray_arg q_obj_blockdev_close_tray_arg; ++ ++typedef struct q_obj_blockdev_remove_medium_arg q_obj_blockdev_remove_medium_arg; ++ ++typedef struct q_obj_blockdev_insert_medium_arg q_obj_blockdev_insert_medium_arg; ++ ++typedef enum BlockdevChangeReadOnlyMode { ++ BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN, ++ BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_ONLY, ++ BLOCKDEV_CHANGE_READ_ONLY_MODE_READ_WRITE, ++ BLOCKDEV_CHANGE_READ_ONLY_MODE__MAX, ++} BlockdevChangeReadOnlyMode; ++ ++#define BlockdevChangeReadOnlyMode_str(val) \ ++ qapi_enum_lookup(&BlockdevChangeReadOnlyMode_lookup, (val)) ++ ++extern const QEnumLookup BlockdevChangeReadOnlyMode_lookup; ++ ++typedef struct q_obj_blockdev_change_medium_arg q_obj_blockdev_change_medium_arg; ++ ++typedef struct q_obj_DEVICE_TRAY_MOVED_arg q_obj_DEVICE_TRAY_MOVED_arg; ++ ++typedef struct q_obj_PR_MANAGER_STATUS_CHANGED_arg q_obj_PR_MANAGER_STATUS_CHANGED_arg; ++ ++typedef struct q_obj_block_latency_histogram_set_arg q_obj_block_latency_histogram_set_arg; ++ ++struct PRManagerInfo { ++ char *id; ++ bool connected; ++}; ++ ++void qapi_free_PRManagerInfo(PRManagerInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(PRManagerInfo, qapi_free_PRManagerInfo) ++ ++struct PRManagerInfoList { ++ PRManagerInfoList *next; ++ PRManagerInfo *value; ++}; ++ ++void qapi_free_PRManagerInfoList(PRManagerInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(PRManagerInfoList, qapi_free_PRManagerInfoList) ++ ++struct q_obj_eject_arg { ++ char *device; ++ char *id; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_blockdev_open_tray_arg { ++ char *device; ++ char *id; ++ bool has_force; ++ bool force; ++}; ++ ++struct q_obj_blockdev_close_tray_arg { ++ char *device; ++ char *id; ++}; ++ ++struct q_obj_blockdev_remove_medium_arg { ++ char *id; ++}; ++ ++struct q_obj_blockdev_insert_medium_arg { ++ char *id; ++ char *node_name; ++}; ++ ++struct q_obj_blockdev_change_medium_arg { ++ char *device; ++ char *id; ++ char *filename; ++ char *format; ++ bool has_force; ++ bool force; ++ bool has_read_only_mode; ++ BlockdevChangeReadOnlyMode read_only_mode; ++}; ++ ++struct q_obj_DEVICE_TRAY_MOVED_arg { ++ char *device; ++ char *id; ++ bool tray_open; ++}; ++ ++struct q_obj_PR_MANAGER_STATUS_CHANGED_arg { ++ char *id; ++ bool connected; ++}; ++ ++struct q_obj_block_latency_histogram_set_arg { ++ char *id; ++ bool has_boundaries; ++ uint64List *boundaries; ++ bool has_boundaries_read; ++ uint64List *boundaries_read; ++ bool has_boundaries_write; ++ uint64List *boundaries_write; ++ bool has_boundaries_zap; ++ uint64List *boundaries_zap; ++ bool has_boundaries_flush; ++ uint64List *boundaries_flush; ++}; ++ ++#endif /* QAPI_TYPES_BLOCK_H */ +diff --git a/include/qapi/qapi-types-common.h b/include/qapi/qapi-types-common.h +new file mode 100644 +index 00000000..1ce2d1c0 +--- /dev/null ++++ b/include/qapi/qapi-types-common.h +@@ -0,0 +1,163 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_COMMON_H ++#define QAPI_TYPES_COMMON_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum IoOperationType { ++ IO_OPERATION_TYPE_READ, ++ IO_OPERATION_TYPE_WRITE, ++ IO_OPERATION_TYPE__MAX, ++} IoOperationType; ++ ++#define IoOperationType_str(val) \ ++ qapi_enum_lookup(&IoOperationType_lookup, (val)) ++ ++extern const QEnumLookup IoOperationType_lookup; ++ ++typedef enum OnOffAuto { ++ ON_OFF_AUTO_AUTO, ++ ON_OFF_AUTO_ON, ++ ON_OFF_AUTO_OFF, ++ ON_OFF_AUTO__MAX, ++} OnOffAuto; ++ ++#define OnOffAuto_str(val) \ ++ qapi_enum_lookup(&OnOffAuto_lookup, (val)) ++ ++extern const QEnumLookup OnOffAuto_lookup; ++ ++typedef enum OnOffSplit { ++ ON_OFF_SPLIT_ON, ++ ON_OFF_SPLIT_OFF, ++ ON_OFF_SPLIT_SPLIT, ++ ON_OFF_SPLIT__MAX, ++} OnOffSplit; ++ ++#define OnOffSplit_str(val) \ ++ qapi_enum_lookup(&OnOffSplit_lookup, (val)) ++ ++extern const QEnumLookup OnOffSplit_lookup; ++ ++typedef struct StrOrNull StrOrNull; ++ ++typedef enum OffAutoPCIBAR { ++ OFF_AUTOPCIBAR_OFF, ++ OFF_AUTOPCIBAR_AUTO, ++ OFF_AUTOPCIBAR_BAR0, ++ OFF_AUTOPCIBAR_BAR1, ++ OFF_AUTOPCIBAR_BAR2, ++ OFF_AUTOPCIBAR_BAR3, ++ OFF_AUTOPCIBAR_BAR4, ++ OFF_AUTOPCIBAR_BAR5, ++ OFF_AUTOPCIBAR__MAX, ++} OffAutoPCIBAR; ++ ++#define OffAutoPCIBAR_str(val) \ ++ qapi_enum_lookup(&OffAutoPCIBAR_lookup, (val)) ++ ++extern const QEnumLookup OffAutoPCIBAR_lookup; ++ ++typedef enum PCIELinkSpeed { ++ PCIE_LINK_SPEED_2_5, ++ PCIE_LINK_SPEED_5, ++ PCIE_LINK_SPEED_8, ++ PCIE_LINK_SPEED_16, ++ PCIE_LINK_SPEED_32, ++ PCIE_LINK_SPEED_64, ++ PCIE_LINK_SPEED__MAX, ++} PCIELinkSpeed; ++ ++#define PCIELinkSpeed_str(val) \ ++ qapi_enum_lookup(&PCIELinkSpeed_lookup, (val)) ++ ++extern const QEnumLookup PCIELinkSpeed_lookup; ++ ++typedef enum PCIELinkWidth { ++ PCIE_LINK_WIDTH_1, ++ PCIE_LINK_WIDTH_2, ++ PCIE_LINK_WIDTH_4, ++ PCIE_LINK_WIDTH_8, ++ PCIE_LINK_WIDTH_12, ++ PCIE_LINK_WIDTH_16, ++ PCIE_LINK_WIDTH_32, ++ PCIE_LINK_WIDTH__MAX, ++} PCIELinkWidth; ++ ++#define PCIELinkWidth_str(val) \ ++ qapi_enum_lookup(&PCIELinkWidth_lookup, (val)) ++ ++extern const QEnumLookup PCIELinkWidth_lookup; ++ ++typedef enum HostMemPolicy { ++ HOST_MEM_POLICY_DEFAULT, ++ HOST_MEM_POLICY_PREFERRED, ++ HOST_MEM_POLICY_BIND, ++ HOST_MEM_POLICY_INTERLEAVE, ++ HOST_MEM_POLICY__MAX, ++} HostMemPolicy; ++ ++#define HostMemPolicy_str(val) \ ++ qapi_enum_lookup(&HostMemPolicy_lookup, (val)) ++ ++extern const QEnumLookup HostMemPolicy_lookup; ++ ++typedef enum NetFilterDirection { ++ NET_FILTER_DIRECTION_ALL, ++ NET_FILTER_DIRECTION_RX, ++ NET_FILTER_DIRECTION_TX, ++ NET_FILTER_DIRECTION__MAX, ++} NetFilterDirection; ++ ++#define NetFilterDirection_str(val) \ ++ qapi_enum_lookup(&NetFilterDirection_lookup, (val)) ++ ++extern const QEnumLookup NetFilterDirection_lookup; ++ ++typedef enum GrabToggleKeys { ++ GRAB_TOGGLE_KEYS_CTRL_CTRL, ++ GRAB_TOGGLE_KEYS_ALT_ALT, ++ GRAB_TOGGLE_KEYS_SHIFT_SHIFT, ++ GRAB_TOGGLE_KEYS_META_META, ++ GRAB_TOGGLE_KEYS_SCROLLLOCK, ++ GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK, ++ GRAB_TOGGLE_KEYS__MAX, ++} GrabToggleKeys; ++ ++#define GrabToggleKeys_str(val) \ ++ qapi_enum_lookup(&GrabToggleKeys_lookup, (val)) ++ ++extern const QEnumLookup GrabToggleKeys_lookup; ++ ++typedef struct HumanReadableText HumanReadableText; ++ ++struct StrOrNull { ++ QType type; ++ union { /* union tag is @type */ ++ char *s; ++ QNull *n; ++ } u; ++}; ++ ++void qapi_free_StrOrNull(StrOrNull *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(StrOrNull, qapi_free_StrOrNull) ++ ++struct HumanReadableText { ++ char *human_readable_text; ++}; ++ ++void qapi_free_HumanReadableText(HumanReadableText *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(HumanReadableText, qapi_free_HumanReadableText) ++ ++#endif /* QAPI_TYPES_COMMON_H */ +diff --git a/include/qapi/qapi-types-compat.h b/include/qapi/qapi-types-compat.h +new file mode 100644 +index 00000000..0b4c36ee +--- /dev/null ++++ b/include/qapi/qapi-types-compat.h +@@ -0,0 +1,57 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_COMPAT_H ++#define QAPI_TYPES_COMPAT_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum CompatPolicyInput { ++ COMPAT_POLICY_INPUT_ACCEPT, ++ COMPAT_POLICY_INPUT_REJECT, ++ COMPAT_POLICY_INPUT_CRASH, ++ COMPAT_POLICY_INPUT__MAX, ++} CompatPolicyInput; ++ ++#define CompatPolicyInput_str(val) \ ++ qapi_enum_lookup(&CompatPolicyInput_lookup, (val)) ++ ++extern const QEnumLookup CompatPolicyInput_lookup; ++ ++typedef enum CompatPolicyOutput { ++ COMPAT_POLICY_OUTPUT_ACCEPT, ++ COMPAT_POLICY_OUTPUT_HIDE, ++ COMPAT_POLICY_OUTPUT__MAX, ++} CompatPolicyOutput; ++ ++#define CompatPolicyOutput_str(val) \ ++ qapi_enum_lookup(&CompatPolicyOutput_lookup, (val)) ++ ++extern const QEnumLookup CompatPolicyOutput_lookup; ++ ++typedef struct CompatPolicy CompatPolicy; ++ ++struct CompatPolicy { ++ bool has_deprecated_input; ++ CompatPolicyInput deprecated_input; ++ bool has_deprecated_output; ++ CompatPolicyOutput deprecated_output; ++ bool has_unstable_input; ++ CompatPolicyInput unstable_input; ++ bool has_unstable_output; ++ CompatPolicyOutput unstable_output; ++}; ++ ++void qapi_free_CompatPolicy(CompatPolicy *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(CompatPolicy, qapi_free_CompatPolicy) ++ ++#endif /* QAPI_TYPES_COMPAT_H */ +diff --git a/include/qapi/qapi-types-crypto.h b/include/qapi/qapi-types-crypto.h +new file mode 100644 +index 00000000..14cf5d7c +--- /dev/null ++++ b/include/qapi/qapi-types-crypto.h +@@ -0,0 +1,538 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_CRYPTO_H ++#define QAPI_TYPES_CRYPTO_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum QCryptoTLSCredsEndpoint { ++ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, ++ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, ++ QCRYPTO_TLS_CREDS_ENDPOINT__MAX, ++} QCryptoTLSCredsEndpoint; ++ ++#define QCryptoTLSCredsEndpoint_str(val) \ ++ qapi_enum_lookup(&QCryptoTLSCredsEndpoint_lookup, (val)) ++ ++extern const QEnumLookup QCryptoTLSCredsEndpoint_lookup; ++ ++typedef enum QCryptoSecretFormat { ++ QCRYPTO_SECRET_FORMAT_RAW, ++ QCRYPTO_SECRET_FORMAT_BASE64, ++ QCRYPTO_SECRET_FORMAT__MAX, ++} QCryptoSecretFormat; ++ ++#define QCryptoSecretFormat_str(val) \ ++ qapi_enum_lookup(&QCryptoSecretFormat_lookup, (val)) ++ ++extern const QEnumLookup QCryptoSecretFormat_lookup; ++ ++typedef enum QCryptoHashAlgorithm { ++ QCRYPTO_HASH_ALG_MD5, ++ QCRYPTO_HASH_ALG_SHA1, ++ QCRYPTO_HASH_ALG_SHA224, ++ QCRYPTO_HASH_ALG_SHA256, ++ QCRYPTO_HASH_ALG_SHA384, ++ QCRYPTO_HASH_ALG_SHA512, ++ QCRYPTO_HASH_ALG_RIPEMD160, ++ QCRYPTO_HASH_ALG__MAX, ++} QCryptoHashAlgorithm; ++ ++#define QCryptoHashAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoHashAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoHashAlgorithm_lookup; ++ ++typedef enum QCryptoCipherAlgorithm { ++ QCRYPTO_CIPHER_ALG_AES_128, ++ QCRYPTO_CIPHER_ALG_AES_192, ++ QCRYPTO_CIPHER_ALG_AES_256, ++ QCRYPTO_CIPHER_ALG_DES, ++ QCRYPTO_CIPHER_ALG_3DES, ++ QCRYPTO_CIPHER_ALG_CAST5_128, ++ QCRYPTO_CIPHER_ALG_SERPENT_128, ++ QCRYPTO_CIPHER_ALG_SERPENT_192, ++ QCRYPTO_CIPHER_ALG_SERPENT_256, ++ QCRYPTO_CIPHER_ALG_TWOFISH_128, ++ QCRYPTO_CIPHER_ALG_TWOFISH_192, ++ QCRYPTO_CIPHER_ALG_TWOFISH_256, ++ QCRYPTO_CIPHER_ALG_SM4, ++ QCRYPTO_CIPHER_ALG__MAX, ++} QCryptoCipherAlgorithm; ++ ++#define QCryptoCipherAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoCipherAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoCipherAlgorithm_lookup; ++ ++typedef enum QCryptoCipherMode { ++ QCRYPTO_CIPHER_MODE_ECB, ++ QCRYPTO_CIPHER_MODE_CBC, ++ QCRYPTO_CIPHER_MODE_XTS, ++ QCRYPTO_CIPHER_MODE_CTR, ++ QCRYPTO_CIPHER_MODE__MAX, ++} QCryptoCipherMode; ++ ++#define QCryptoCipherMode_str(val) \ ++ qapi_enum_lookup(&QCryptoCipherMode_lookup, (val)) ++ ++extern const QEnumLookup QCryptoCipherMode_lookup; ++ ++typedef enum QCryptoIVGenAlgorithm { ++ QCRYPTO_IVGEN_ALG_PLAIN, ++ QCRYPTO_IVGEN_ALG_PLAIN64, ++ QCRYPTO_IVGEN_ALG_ESSIV, ++ QCRYPTO_IVGEN_ALG__MAX, ++} QCryptoIVGenAlgorithm; ++ ++#define QCryptoIVGenAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoIVGenAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoIVGenAlgorithm_lookup; ++ ++typedef enum QCryptoBlockFormat { ++ Q_CRYPTO_BLOCK_FORMAT_QCOW, ++ Q_CRYPTO_BLOCK_FORMAT_LUKS, ++ Q_CRYPTO_BLOCK_FORMAT__MAX, ++} QCryptoBlockFormat; ++ ++#define QCryptoBlockFormat_str(val) \ ++ qapi_enum_lookup(&QCryptoBlockFormat_lookup, (val)) ++ ++extern const QEnumLookup QCryptoBlockFormat_lookup; ++ ++typedef struct QCryptoBlockOptionsBase QCryptoBlockOptionsBase; ++ ++typedef struct QCryptoBlockOptionsQCow QCryptoBlockOptionsQCow; ++ ++typedef struct QCryptoBlockOptionsLUKS QCryptoBlockOptionsLUKS; ++ ++typedef struct QCryptoBlockCreateOptionsLUKS QCryptoBlockCreateOptionsLUKS; ++ ++typedef struct QCryptoBlockOpenOptions QCryptoBlockOpenOptions; ++ ++typedef struct QCryptoBlockCreateOptions QCryptoBlockCreateOptions; ++ ++typedef struct QCryptoBlockInfoBase QCryptoBlockInfoBase; ++ ++typedef struct QCryptoBlockInfoLUKSSlot QCryptoBlockInfoLUKSSlot; ++ ++typedef struct QCryptoBlockInfoLUKSSlotList QCryptoBlockInfoLUKSSlotList; ++ ++typedef struct QCryptoBlockInfoLUKS QCryptoBlockInfoLUKS; ++ ++typedef struct QCryptoBlockInfo QCryptoBlockInfo; ++ ++typedef enum QCryptoBlockLUKSKeyslotState { ++ Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE, ++ Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE, ++ Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE__MAX, ++} QCryptoBlockLUKSKeyslotState; ++ ++#define QCryptoBlockLUKSKeyslotState_str(val) \ ++ qapi_enum_lookup(&QCryptoBlockLUKSKeyslotState_lookup, (val)) ++ ++extern const QEnumLookup QCryptoBlockLUKSKeyslotState_lookup; ++ ++typedef struct QCryptoBlockAmendOptionsLUKS QCryptoBlockAmendOptionsLUKS; ++ ++typedef struct QCryptoBlockAmendOptions QCryptoBlockAmendOptions; ++ ++typedef struct SecretCommonProperties SecretCommonProperties; ++ ++typedef struct SecretProperties SecretProperties; ++ ++#if defined(CONFIG_SECRET_KEYRING) ++typedef struct SecretKeyringProperties SecretKeyringProperties; ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++typedef struct TlsCredsProperties TlsCredsProperties; ++ ++typedef struct TlsCredsAnonProperties TlsCredsAnonProperties; ++ ++typedef struct TlsCredsPskProperties TlsCredsPskProperties; ++ ++typedef struct TlsCredsX509Properties TlsCredsX509Properties; ++ ++typedef enum QCryptoAkCipherAlgorithm { ++ QCRYPTO_AKCIPHER_ALG_RSA, ++ QCRYPTO_AKCIPHER_ALG__MAX, ++} QCryptoAkCipherAlgorithm; ++ ++#define QCryptoAkCipherAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoAkCipherAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoAkCipherAlgorithm_lookup; ++ ++typedef enum QCryptoAkCipherKeyType { ++ QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, ++ QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, ++ QCRYPTO_AKCIPHER_KEY_TYPE__MAX, ++} QCryptoAkCipherKeyType; ++ ++#define QCryptoAkCipherKeyType_str(val) \ ++ qapi_enum_lookup(&QCryptoAkCipherKeyType_lookup, (val)) ++ ++extern const QEnumLookup QCryptoAkCipherKeyType_lookup; ++ ++typedef enum QCryptoRSAPaddingAlgorithm { ++ QCRYPTO_RSA_PADDING_ALG_RAW, ++ QCRYPTO_RSA_PADDING_ALG_PKCS1, ++ QCRYPTO_RSA_PADDING_ALG__MAX, ++} QCryptoRSAPaddingAlgorithm; ++ ++#define QCryptoRSAPaddingAlgorithm_str(val) \ ++ qapi_enum_lookup(&QCryptoRSAPaddingAlgorithm_lookup, (val)) ++ ++extern const QEnumLookup QCryptoRSAPaddingAlgorithm_lookup; ++ ++typedef struct QCryptoAkCipherOptionsRSA QCryptoAkCipherOptionsRSA; ++ ++typedef struct q_obj_QCryptoAkCipherOptions_base q_obj_QCryptoAkCipherOptions_base; ++ ++typedef struct QCryptoAkCipherOptions QCryptoAkCipherOptions; ++ ++struct QCryptoBlockOptionsBase { ++ QCryptoBlockFormat format; ++}; ++ ++void qapi_free_QCryptoBlockOptionsBase(QCryptoBlockOptionsBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockOptionsBase, qapi_free_QCryptoBlockOptionsBase) ++ ++struct QCryptoBlockOptionsQCow { ++ char *key_secret; ++}; ++ ++void qapi_free_QCryptoBlockOptionsQCow(QCryptoBlockOptionsQCow *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockOptionsQCow, qapi_free_QCryptoBlockOptionsQCow) ++ ++struct QCryptoBlockOptionsLUKS { ++ char *key_secret; ++}; ++ ++void qapi_free_QCryptoBlockOptionsLUKS(QCryptoBlockOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockOptionsLUKS, qapi_free_QCryptoBlockOptionsLUKS) ++ ++struct QCryptoBlockCreateOptionsLUKS { ++ /* Members inherited from QCryptoBlockOptionsLUKS: */ ++ char *key_secret; ++ /* Own members: */ ++ bool has_cipher_alg; ++ QCryptoCipherAlgorithm cipher_alg; ++ bool has_cipher_mode; ++ QCryptoCipherMode cipher_mode; ++ bool has_ivgen_alg; ++ QCryptoIVGenAlgorithm ivgen_alg; ++ bool has_ivgen_hash_alg; ++ QCryptoHashAlgorithm ivgen_hash_alg; ++ bool has_hash_alg; ++ QCryptoHashAlgorithm hash_alg; ++ bool has_iter_time; ++ int64_t iter_time; ++}; ++ ++static inline QCryptoBlockOptionsLUKS *qapi_QCryptoBlockCreateOptionsLUKS_base(const QCryptoBlockCreateOptionsLUKS *obj) ++{ ++ return (QCryptoBlockOptionsLUKS *)obj; ++} ++ ++void qapi_free_QCryptoBlockCreateOptionsLUKS(QCryptoBlockCreateOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockCreateOptionsLUKS, qapi_free_QCryptoBlockCreateOptionsLUKS) ++ ++struct QCryptoBlockOpenOptions { ++ /* Members inherited from QCryptoBlockOptionsBase: */ ++ QCryptoBlockFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockOptionsQCow qcow; ++ QCryptoBlockOptionsLUKS luks; ++ } u; ++}; ++ ++static inline QCryptoBlockOptionsBase *qapi_QCryptoBlockOpenOptions_base(const QCryptoBlockOpenOptions *obj) ++{ ++ return (QCryptoBlockOptionsBase *)obj; ++} ++ ++void qapi_free_QCryptoBlockOpenOptions(QCryptoBlockOpenOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockOpenOptions, qapi_free_QCryptoBlockOpenOptions) ++ ++struct QCryptoBlockCreateOptions { ++ /* Members inherited from QCryptoBlockOptionsBase: */ ++ QCryptoBlockFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockOptionsQCow qcow; ++ QCryptoBlockCreateOptionsLUKS luks; ++ } u; ++}; ++ ++static inline QCryptoBlockOptionsBase *qapi_QCryptoBlockCreateOptions_base(const QCryptoBlockCreateOptions *obj) ++{ ++ return (QCryptoBlockOptionsBase *)obj; ++} ++ ++void qapi_free_QCryptoBlockCreateOptions(QCryptoBlockCreateOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockCreateOptions, qapi_free_QCryptoBlockCreateOptions) ++ ++struct QCryptoBlockInfoBase { ++ QCryptoBlockFormat format; ++}; ++ ++void qapi_free_QCryptoBlockInfoBase(QCryptoBlockInfoBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfoBase, qapi_free_QCryptoBlockInfoBase) ++ ++struct QCryptoBlockInfoLUKSSlot { ++ bool active; ++ bool has_iters; ++ int64_t iters; ++ bool has_stripes; ++ int64_t stripes; ++ int64_t key_offset; ++}; ++ ++void qapi_free_QCryptoBlockInfoLUKSSlot(QCryptoBlockInfoLUKSSlot *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfoLUKSSlot, qapi_free_QCryptoBlockInfoLUKSSlot) ++ ++struct QCryptoBlockInfoLUKSSlotList { ++ QCryptoBlockInfoLUKSSlotList *next; ++ QCryptoBlockInfoLUKSSlot *value; ++}; ++ ++void qapi_free_QCryptoBlockInfoLUKSSlotList(QCryptoBlockInfoLUKSSlotList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfoLUKSSlotList, qapi_free_QCryptoBlockInfoLUKSSlotList) ++ ++struct QCryptoBlockInfoLUKS { ++ QCryptoCipherAlgorithm cipher_alg; ++ QCryptoCipherMode cipher_mode; ++ QCryptoIVGenAlgorithm ivgen_alg; ++ bool has_ivgen_hash_alg; ++ QCryptoHashAlgorithm ivgen_hash_alg; ++ QCryptoHashAlgorithm hash_alg; ++ bool detached_header; ++ int64_t payload_offset; ++ int64_t master_key_iters; ++ char *uuid; ++ QCryptoBlockInfoLUKSSlotList *slots; ++}; ++ ++void qapi_free_QCryptoBlockInfoLUKS(QCryptoBlockInfoLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfoLUKS, qapi_free_QCryptoBlockInfoLUKS) ++ ++struct QCryptoBlockInfo { ++ /* Members inherited from QCryptoBlockInfoBase: */ ++ QCryptoBlockFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockInfoLUKS luks; ++ } u; ++}; ++ ++static inline QCryptoBlockInfoBase *qapi_QCryptoBlockInfo_base(const QCryptoBlockInfo *obj) ++{ ++ return (QCryptoBlockInfoBase *)obj; ++} ++ ++void qapi_free_QCryptoBlockInfo(QCryptoBlockInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockInfo, qapi_free_QCryptoBlockInfo) ++ ++struct QCryptoBlockAmendOptionsLUKS { ++ QCryptoBlockLUKSKeyslotState state; ++ char *new_secret; ++ char *old_secret; ++ bool has_keyslot; ++ int64_t keyslot; ++ bool has_iter_time; ++ int64_t iter_time; ++ char *secret; ++}; ++ ++void qapi_free_QCryptoBlockAmendOptionsLUKS(QCryptoBlockAmendOptionsLUKS *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockAmendOptionsLUKS, qapi_free_QCryptoBlockAmendOptionsLUKS) ++ ++struct QCryptoBlockAmendOptions { ++ /* Members inherited from QCryptoBlockOptionsBase: */ ++ QCryptoBlockFormat format; ++ /* Own members: */ ++ union { /* union tag is @format */ ++ QCryptoBlockAmendOptionsLUKS luks; ++ } u; ++}; ++ ++static inline QCryptoBlockOptionsBase *qapi_QCryptoBlockAmendOptions_base(const QCryptoBlockAmendOptions *obj) ++{ ++ return (QCryptoBlockOptionsBase *)obj; ++} ++ ++void qapi_free_QCryptoBlockAmendOptions(QCryptoBlockAmendOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoBlockAmendOptions, qapi_free_QCryptoBlockAmendOptions) ++ ++struct SecretCommonProperties { ++ bool has_loaded; ++ bool loaded; ++ bool has_format; ++ QCryptoSecretFormat format; ++ char *keyid; ++ char *iv; ++}; ++ ++void qapi_free_SecretCommonProperties(SecretCommonProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SecretCommonProperties, qapi_free_SecretCommonProperties) ++ ++struct SecretProperties { ++ /* Members inherited from SecretCommonProperties: */ ++ bool has_loaded; ++ bool loaded; ++ bool has_format; ++ QCryptoSecretFormat format; ++ char *keyid; ++ char *iv; ++ /* Own members: */ ++ char *data; ++ char *file; ++}; ++ ++static inline SecretCommonProperties *qapi_SecretProperties_base(const SecretProperties *obj) ++{ ++ return (SecretCommonProperties *)obj; ++} ++ ++void qapi_free_SecretProperties(SecretProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SecretProperties, qapi_free_SecretProperties) ++ ++#if defined(CONFIG_SECRET_KEYRING) ++struct SecretKeyringProperties { ++ /* Members inherited from SecretCommonProperties: */ ++ bool has_loaded; ++ bool loaded; ++ bool has_format; ++ QCryptoSecretFormat format; ++ char *keyid; ++ char *iv; ++ /* Own members: */ ++ int32_t serial; ++}; ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++#if defined(CONFIG_SECRET_KEYRING) ++static inline SecretCommonProperties *qapi_SecretKeyringProperties_base(const SecretKeyringProperties *obj) ++{ ++ return (SecretCommonProperties *)obj; ++} ++ ++void qapi_free_SecretKeyringProperties(SecretKeyringProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SecretKeyringProperties, qapi_free_SecretKeyringProperties) ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++struct TlsCredsProperties { ++ bool has_verify_peer; ++ bool verify_peer; ++ char *dir; ++ bool has_endpoint; ++ QCryptoTLSCredsEndpoint endpoint; ++ char *priority; ++}; ++ ++void qapi_free_TlsCredsProperties(TlsCredsProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(TlsCredsProperties, qapi_free_TlsCredsProperties) ++ ++struct TlsCredsAnonProperties { ++ /* Members inherited from TlsCredsProperties: */ ++ bool has_verify_peer; ++ bool verify_peer; ++ char *dir; ++ bool has_endpoint; ++ QCryptoTLSCredsEndpoint endpoint; ++ char *priority; ++ /* Own members: */ ++ bool has_loaded; ++ bool loaded; ++}; ++ ++static inline TlsCredsProperties *qapi_TlsCredsAnonProperties_base(const TlsCredsAnonProperties *obj) ++{ ++ return (TlsCredsProperties *)obj; ++} ++ ++void qapi_free_TlsCredsAnonProperties(TlsCredsAnonProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(TlsCredsAnonProperties, qapi_free_TlsCredsAnonProperties) ++ ++struct TlsCredsPskProperties { ++ /* Members inherited from TlsCredsProperties: */ ++ bool has_verify_peer; ++ bool verify_peer; ++ char *dir; ++ bool has_endpoint; ++ QCryptoTLSCredsEndpoint endpoint; ++ char *priority; ++ /* Own members: */ ++ bool has_loaded; ++ bool loaded; ++ char *username; ++}; ++ ++static inline TlsCredsProperties *qapi_TlsCredsPskProperties_base(const TlsCredsPskProperties *obj) ++{ ++ return (TlsCredsProperties *)obj; ++} ++ ++void qapi_free_TlsCredsPskProperties(TlsCredsPskProperties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(TlsCredsPskProperties, qapi_free_TlsCredsPskProperties) ++ ++struct TlsCredsX509Properties { ++ /* Members inherited from TlsCredsProperties: */ ++ bool has_verify_peer; ++ bool verify_peer; ++ char *dir; ++ bool has_endpoint; ++ QCryptoTLSCredsEndpoint endpoint; ++ char *priority; ++ /* Own members: */ ++ bool has_loaded; ++ bool loaded; ++ bool has_sanity_check; ++ bool sanity_check; ++ char *passwordid; ++}; ++ ++static inline TlsCredsProperties *qapi_TlsCredsX509Properties_base(const TlsCredsX509Properties *obj) ++{ ++ return (TlsCredsProperties *)obj; ++} ++ ++void qapi_free_TlsCredsX509Properties(TlsCredsX509Properties *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(TlsCredsX509Properties, qapi_free_TlsCredsX509Properties) ++ ++struct QCryptoAkCipherOptionsRSA { ++ QCryptoHashAlgorithm hash_alg; ++ QCryptoRSAPaddingAlgorithm padding_alg; ++}; ++ ++void qapi_free_QCryptoAkCipherOptionsRSA(QCryptoAkCipherOptionsRSA *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherOptionsRSA, qapi_free_QCryptoAkCipherOptionsRSA) ++ ++struct q_obj_QCryptoAkCipherOptions_base { ++ QCryptoAkCipherAlgorithm alg; ++}; ++ ++struct QCryptoAkCipherOptions { ++ QCryptoAkCipherAlgorithm alg; ++ union { /* union tag is @alg */ ++ QCryptoAkCipherOptionsRSA rsa; ++ } u; ++}; ++ ++void qapi_free_QCryptoAkCipherOptions(QCryptoAkCipherOptions *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoAkCipherOptions, qapi_free_QCryptoAkCipherOptions) ++ ++#endif /* QAPI_TYPES_CRYPTO_H */ +diff --git a/include/qapi/qapi-types-error.h b/include/qapi/qapi-types-error.h +new file mode 100644 +index 00000000..549dcfcc +--- /dev/null ++++ b/include/qapi/qapi-types-error.h +@@ -0,0 +1,32 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_ERROR_H ++#define QAPI_TYPES_ERROR_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum QapiErrorClass { ++ QAPI_ERROR_CLASS_GENERICERROR, ++ QAPI_ERROR_CLASS_COMMANDNOTFOUND, ++ QAPI_ERROR_CLASS_DEVICENOTACTIVE, ++ QAPI_ERROR_CLASS_DEVICENOTFOUND, ++ QAPI_ERROR_CLASS_KVMMISSINGCAP, ++ QAPI_ERROR_CLASS__MAX, ++} QapiErrorClass; ++ ++#define QapiErrorClass_str(val) \ ++ qapi_enum_lookup(&QapiErrorClass_lookup, (val)) ++ ++extern const QEnumLookup QapiErrorClass_lookup; ++ ++#endif /* QAPI_TYPES_ERROR_H */ +diff --git a/include/qapi/qapi-types-job.h b/include/qapi/qapi-types-job.h +new file mode 100644 +index 00000000..7c3d08e4 +--- /dev/null ++++ b/include/qapi/qapi-types-job.h +@@ -0,0 +1,140 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_JOB_H ++#define QAPI_TYPES_JOB_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum JobType { ++ JOB_TYPE_COMMIT, ++ JOB_TYPE_STREAM, ++ JOB_TYPE_MIRROR, ++ JOB_TYPE_BACKUP, ++ JOB_TYPE_CREATE, ++ JOB_TYPE_AMEND, ++ JOB_TYPE_SNAPSHOT_LOAD, ++ JOB_TYPE_SNAPSHOT_SAVE, ++ JOB_TYPE_SNAPSHOT_DELETE, ++ JOB_TYPE__MAX, ++} JobType; ++ ++#define JobType_str(val) \ ++ qapi_enum_lookup(&JobType_lookup, (val)) ++ ++extern const QEnumLookup JobType_lookup; ++ ++typedef enum JobStatus { ++ JOB_STATUS_UNDEFINED, ++ JOB_STATUS_CREATED, ++ JOB_STATUS_RUNNING, ++ JOB_STATUS_PAUSED, ++ JOB_STATUS_READY, ++ JOB_STATUS_STANDBY, ++ JOB_STATUS_WAITING, ++ JOB_STATUS_PENDING, ++ JOB_STATUS_ABORTING, ++ JOB_STATUS_CONCLUDED, ++ JOB_STATUS_NULL, ++ JOB_STATUS__MAX, ++} JobStatus; ++ ++#define JobStatus_str(val) \ ++ qapi_enum_lookup(&JobStatus_lookup, (val)) ++ ++extern const QEnumLookup JobStatus_lookup; ++ ++typedef enum JobVerb { ++ JOB_VERB_CANCEL, ++ JOB_VERB_PAUSE, ++ JOB_VERB_RESUME, ++ JOB_VERB_SET_SPEED, ++ JOB_VERB_COMPLETE, ++ JOB_VERB_DISMISS, ++ JOB_VERB_FINALIZE, ++ JOB_VERB_CHANGE, ++ JOB_VERB__MAX, ++} JobVerb; ++ ++#define JobVerb_str(val) \ ++ qapi_enum_lookup(&JobVerb_lookup, (val)) ++ ++extern const QEnumLookup JobVerb_lookup; ++ ++typedef struct q_obj_JOB_STATUS_CHANGE_arg q_obj_JOB_STATUS_CHANGE_arg; ++ ++typedef struct q_obj_job_pause_arg q_obj_job_pause_arg; ++ ++typedef struct q_obj_job_resume_arg q_obj_job_resume_arg; ++ ++typedef struct q_obj_job_cancel_arg q_obj_job_cancel_arg; ++ ++typedef struct q_obj_job_complete_arg q_obj_job_complete_arg; ++ ++typedef struct q_obj_job_dismiss_arg q_obj_job_dismiss_arg; ++ ++typedef struct q_obj_job_finalize_arg q_obj_job_finalize_arg; ++ ++typedef struct JobInfo JobInfo; ++ ++typedef struct JobInfoList JobInfoList; ++ ++struct q_obj_JOB_STATUS_CHANGE_arg { ++ char *id; ++ JobStatus status; ++}; ++ ++struct q_obj_job_pause_arg { ++ char *id; ++}; ++ ++struct q_obj_job_resume_arg { ++ char *id; ++}; ++ ++struct q_obj_job_cancel_arg { ++ char *id; ++}; ++ ++struct q_obj_job_complete_arg { ++ char *id; ++}; ++ ++struct q_obj_job_dismiss_arg { ++ char *id; ++}; ++ ++struct q_obj_job_finalize_arg { ++ char *id; ++}; ++ ++struct JobInfo { ++ char *id; ++ JobType type; ++ JobStatus status; ++ int64_t current_progress; ++ int64_t total_progress; ++ char *error; ++}; ++ ++void qapi_free_JobInfo(JobInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(JobInfo, qapi_free_JobInfo) ++ ++struct JobInfoList { ++ JobInfoList *next; ++ JobInfo *value; ++}; ++ ++void qapi_free_JobInfoList(JobInfoList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(JobInfoList, qapi_free_JobInfoList) ++ ++#endif /* QAPI_TYPES_JOB_H */ +diff --git a/include/qapi/qapi-types-run-state.h b/include/qapi/qapi-types-run-state.h +new file mode 100644 +index 00000000..caaac070 +--- /dev/null ++++ b/include/qapi/qapi-types-run-state.h +@@ -0,0 +1,312 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_RUN_STATE_H ++#define QAPI_TYPES_RUN_STATE_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum RunState { ++ RUN_STATE_DEBUG, ++ RUN_STATE_INMIGRATE, ++ RUN_STATE_INTERNAL_ERROR, ++ RUN_STATE_IO_ERROR, ++ RUN_STATE_PAUSED, ++ RUN_STATE_POSTMIGRATE, ++ RUN_STATE_PRELAUNCH, ++ RUN_STATE_FINISH_MIGRATE, ++ RUN_STATE_RESTORE_VM, ++ RUN_STATE_RUNNING, ++ RUN_STATE_SAVE_VM, ++ RUN_STATE_SHUTDOWN, ++ RUN_STATE_SUSPENDED, ++ RUN_STATE_WATCHDOG, ++ RUN_STATE_GUEST_PANICKED, ++ RUN_STATE_COLO, ++ RUN_STATE__MAX, ++} RunState; ++ ++#define RunState_str(val) \ ++ qapi_enum_lookup(&RunState_lookup, (val)) ++ ++extern const QEnumLookup RunState_lookup; ++ ++typedef enum ShutdownCause { ++ SHUTDOWN_CAUSE_NONE, ++ SHUTDOWN_CAUSE_HOST_ERROR, ++ SHUTDOWN_CAUSE_HOST_QMP_QUIT, ++ SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET, ++ SHUTDOWN_CAUSE_HOST_SIGNAL, ++ SHUTDOWN_CAUSE_HOST_UI, ++ SHUTDOWN_CAUSE_GUEST_SHUTDOWN, ++ SHUTDOWN_CAUSE_GUEST_RESET, ++ SHUTDOWN_CAUSE_GUEST_PANIC, ++ SHUTDOWN_CAUSE_SUBSYSTEM_RESET, ++ SHUTDOWN_CAUSE_SNAPSHOT_LOAD, ++ SHUTDOWN_CAUSE__MAX, ++} ShutdownCause; ++ ++#define ShutdownCause_str(val) \ ++ qapi_enum_lookup(&ShutdownCause_lookup, (val)) ++ ++extern const QEnumLookup ShutdownCause_lookup; ++ ++typedef struct StatusInfo StatusInfo; ++ ++typedef struct q_obj_SHUTDOWN_arg q_obj_SHUTDOWN_arg; ++ ++typedef struct q_obj_RESET_arg q_obj_RESET_arg; ++ ++typedef struct q_obj_WATCHDOG_arg q_obj_WATCHDOG_arg; ++ ++typedef enum WatchdogAction { ++ WATCHDOG_ACTION_RESET, ++ WATCHDOG_ACTION_SHUTDOWN, ++ WATCHDOG_ACTION_POWEROFF, ++ WATCHDOG_ACTION_PAUSE, ++ WATCHDOG_ACTION_DEBUG, ++ WATCHDOG_ACTION_NONE, ++ WATCHDOG_ACTION_INJECT_NMI, ++ WATCHDOG_ACTION__MAX, ++} WatchdogAction; ++ ++#define WatchdogAction_str(val) \ ++ qapi_enum_lookup(&WatchdogAction_lookup, (val)) ++ ++extern const QEnumLookup WatchdogAction_lookup; ++ ++typedef enum RebootAction { ++ REBOOT_ACTION_RESET, ++ REBOOT_ACTION_SHUTDOWN, ++ REBOOT_ACTION__MAX, ++} RebootAction; ++ ++#define RebootAction_str(val) \ ++ qapi_enum_lookup(&RebootAction_lookup, (val)) ++ ++extern const QEnumLookup RebootAction_lookup; ++ ++typedef enum ShutdownAction { ++ SHUTDOWN_ACTION_POWEROFF, ++ SHUTDOWN_ACTION_PAUSE, ++ SHUTDOWN_ACTION__MAX, ++} ShutdownAction; ++ ++#define ShutdownAction_str(val) \ ++ qapi_enum_lookup(&ShutdownAction_lookup, (val)) ++ ++extern const QEnumLookup ShutdownAction_lookup; ++ ++typedef enum PanicAction { ++ PANIC_ACTION_PAUSE, ++ PANIC_ACTION_SHUTDOWN, ++ PANIC_ACTION_EXIT_FAILURE, ++ PANIC_ACTION_NONE, ++ PANIC_ACTION__MAX, ++} PanicAction; ++ ++#define PanicAction_str(val) \ ++ qapi_enum_lookup(&PanicAction_lookup, (val)) ++ ++extern const QEnumLookup PanicAction_lookup; ++ ++typedef struct q_obj_watchdog_set_action_arg q_obj_watchdog_set_action_arg; ++ ++typedef struct q_obj_set_action_arg q_obj_set_action_arg; ++ ++typedef struct q_obj_GUEST_PANICKED_arg q_obj_GUEST_PANICKED_arg; ++ ++typedef struct q_obj_GUEST_CRASHLOADED_arg q_obj_GUEST_CRASHLOADED_arg; ++ ++typedef enum GuestPanicAction { ++ GUEST_PANIC_ACTION_PAUSE, ++ GUEST_PANIC_ACTION_POWEROFF, ++ GUEST_PANIC_ACTION_RUN, ++ GUEST_PANIC_ACTION__MAX, ++} GuestPanicAction; ++ ++#define GuestPanicAction_str(val) \ ++ qapi_enum_lookup(&GuestPanicAction_lookup, (val)) ++ ++extern const QEnumLookup GuestPanicAction_lookup; ++ ++typedef enum GuestPanicInformationType { ++ GUEST_PANIC_INFORMATION_TYPE_HYPER_V, ++ GUEST_PANIC_INFORMATION_TYPE_S390, ++ GUEST_PANIC_INFORMATION_TYPE__MAX, ++} GuestPanicInformationType; ++ ++#define GuestPanicInformationType_str(val) \ ++ qapi_enum_lookup(&GuestPanicInformationType_lookup, (val)) ++ ++extern const QEnumLookup GuestPanicInformationType_lookup; ++ ++typedef struct q_obj_GuestPanicInformation_base q_obj_GuestPanicInformation_base; ++ ++typedef struct GuestPanicInformation GuestPanicInformation; ++ ++typedef struct GuestPanicInformationHyperV GuestPanicInformationHyperV; ++ ++typedef enum S390CrashReason { ++ S390_CRASH_REASON_UNKNOWN, ++ S390_CRASH_REASON_DISABLED_WAIT, ++ S390_CRASH_REASON_EXTINT_LOOP, ++ S390_CRASH_REASON_PGMINT_LOOP, ++ S390_CRASH_REASON_OPINT_LOOP, ++ S390_CRASH_REASON__MAX, ++} S390CrashReason; ++ ++#define S390CrashReason_str(val) \ ++ qapi_enum_lookup(&S390CrashReason_lookup, (val)) ++ ++extern const QEnumLookup S390CrashReason_lookup; ++ ++typedef struct GuestPanicInformationS390 GuestPanicInformationS390; ++ ++typedef struct q_obj_MEMORY_FAILURE_arg q_obj_MEMORY_FAILURE_arg; ++ ++typedef enum MemoryFailureRecipient { ++ MEMORY_FAILURE_RECIPIENT_HYPERVISOR, ++ MEMORY_FAILURE_RECIPIENT_GUEST, ++ MEMORY_FAILURE_RECIPIENT__MAX, ++} MemoryFailureRecipient; ++ ++#define MemoryFailureRecipient_str(val) \ ++ qapi_enum_lookup(&MemoryFailureRecipient_lookup, (val)) ++ ++extern const QEnumLookup MemoryFailureRecipient_lookup; ++ ++typedef enum MemoryFailureAction { ++ MEMORY_FAILURE_ACTION_IGNORE, ++ MEMORY_FAILURE_ACTION_INJECT, ++ MEMORY_FAILURE_ACTION_FATAL, ++ MEMORY_FAILURE_ACTION_RESET, ++ MEMORY_FAILURE_ACTION__MAX, ++} MemoryFailureAction; ++ ++#define MemoryFailureAction_str(val) \ ++ qapi_enum_lookup(&MemoryFailureAction_lookup, (val)) ++ ++extern const QEnumLookup MemoryFailureAction_lookup; ++ ++typedef struct MemoryFailureFlags MemoryFailureFlags; ++ ++typedef enum NotifyVmexitOption { ++ NOTIFY_VMEXIT_OPTION_RUN, ++ NOTIFY_VMEXIT_OPTION_INTERNAL_ERROR, ++ NOTIFY_VMEXIT_OPTION_DISABLE, ++ NOTIFY_VMEXIT_OPTION__MAX, ++} NotifyVmexitOption; ++ ++#define NotifyVmexitOption_str(val) \ ++ qapi_enum_lookup(&NotifyVmexitOption_lookup, (val)) ++ ++extern const QEnumLookup NotifyVmexitOption_lookup; ++ ++struct StatusInfo { ++ bool running; ++ RunState status; ++}; ++ ++void qapi_free_StatusInfo(StatusInfo *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(StatusInfo, qapi_free_StatusInfo) ++ ++struct q_obj_SHUTDOWN_arg { ++ bool guest; ++ ShutdownCause reason; ++}; ++ ++struct q_obj_RESET_arg { ++ bool guest; ++ ShutdownCause reason; ++}; ++ ++struct q_obj_WATCHDOG_arg { ++ WatchdogAction action; ++}; ++ ++struct q_obj_watchdog_set_action_arg { ++ WatchdogAction action; ++}; ++ ++struct q_obj_set_action_arg { ++ bool has_reboot; ++ RebootAction reboot; ++ bool has_shutdown; ++ ShutdownAction shutdown; ++ bool has_panic; ++ PanicAction panic; ++ bool has_watchdog; ++ WatchdogAction watchdog; ++}; ++ ++struct q_obj_GUEST_PANICKED_arg { ++ GuestPanicAction action; ++ GuestPanicInformation *info; ++}; ++ ++struct q_obj_GUEST_CRASHLOADED_arg { ++ GuestPanicAction action; ++ GuestPanicInformation *info; ++}; ++ ++struct q_obj_GuestPanicInformation_base { ++ GuestPanicInformationType type; ++}; ++ ++struct GuestPanicInformationHyperV { ++ uint64_t arg1; ++ uint64_t arg2; ++ uint64_t arg3; ++ uint64_t arg4; ++ uint64_t arg5; ++}; ++ ++struct GuestPanicInformationS390 { ++ uint32_t core; ++ uint64_t psw_mask; ++ uint64_t psw_addr; ++ S390CrashReason reason; ++}; ++ ++struct GuestPanicInformation { ++ GuestPanicInformationType type; ++ union { /* union tag is @type */ ++ GuestPanicInformationHyperV hyper_v; ++ GuestPanicInformationS390 s390; ++ } u; ++}; ++ ++void qapi_free_GuestPanicInformation(GuestPanicInformation *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GuestPanicInformation, qapi_free_GuestPanicInformation) ++ ++void qapi_free_GuestPanicInformationHyperV(GuestPanicInformationHyperV *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GuestPanicInformationHyperV, qapi_free_GuestPanicInformationHyperV) ++ ++void qapi_free_GuestPanicInformationS390(GuestPanicInformationS390 *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(GuestPanicInformationS390, qapi_free_GuestPanicInformationS390) ++ ++struct q_obj_MEMORY_FAILURE_arg { ++ MemoryFailureRecipient recipient; ++ MemoryFailureAction action; ++ MemoryFailureFlags *flags; ++}; ++ ++struct MemoryFailureFlags { ++ bool action_required; ++ bool recursive; ++}; ++ ++void qapi_free_MemoryFailureFlags(MemoryFailureFlags *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(MemoryFailureFlags, qapi_free_MemoryFailureFlags) ++ ++#endif /* QAPI_TYPES_RUN_STATE_H */ +diff --git a/include/qapi/qapi-types-sockets.h b/include/qapi/qapi-types-sockets.h +new file mode 100644 +index 00000000..3efdfe75 +--- /dev/null ++++ b/include/qapi/qapi-types-sockets.h +@@ -0,0 +1,220 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_SOCKETS_H ++#define QAPI_TYPES_SOCKETS_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum NetworkAddressFamily { ++ NETWORK_ADDRESS_FAMILY_IPV4, ++ NETWORK_ADDRESS_FAMILY_IPV6, ++ NETWORK_ADDRESS_FAMILY_UNIX, ++ NETWORK_ADDRESS_FAMILY_VSOCK, ++ NETWORK_ADDRESS_FAMILY_UNKNOWN, ++ NETWORK_ADDRESS_FAMILY__MAX, ++} NetworkAddressFamily; ++ ++#define NetworkAddressFamily_str(val) \ ++ qapi_enum_lookup(&NetworkAddressFamily_lookup, (val)) ++ ++extern const QEnumLookup NetworkAddressFamily_lookup; ++ ++typedef struct InetSocketAddressBase InetSocketAddressBase; ++ ++typedef struct InetSocketAddress InetSocketAddress; ++ ++typedef struct UnixSocketAddress UnixSocketAddress; ++ ++typedef struct VsockSocketAddress VsockSocketAddress; ++ ++typedef struct FdSocketAddress FdSocketAddress; ++ ++typedef struct InetSocketAddressWrapper InetSocketAddressWrapper; ++ ++typedef struct UnixSocketAddressWrapper UnixSocketAddressWrapper; ++ ++typedef struct VsockSocketAddressWrapper VsockSocketAddressWrapper; ++ ++typedef struct FdSocketAddressWrapper FdSocketAddressWrapper; ++ ++typedef struct q_obj_SocketAddressLegacy_base q_obj_SocketAddressLegacy_base; ++ ++typedef struct SocketAddressLegacy SocketAddressLegacy; ++ ++typedef enum SocketAddressType { ++ SOCKET_ADDRESS_TYPE_INET, ++ SOCKET_ADDRESS_TYPE_UNIX, ++ SOCKET_ADDRESS_TYPE_VSOCK, ++ SOCKET_ADDRESS_TYPE_FD, ++ SOCKET_ADDRESS_TYPE__MAX, ++} SocketAddressType; ++ ++#define SocketAddressType_str(val) \ ++ qapi_enum_lookup(&SocketAddressType_lookup, (val)) ++ ++extern const QEnumLookup SocketAddressType_lookup; ++ ++typedef struct q_obj_SocketAddress_base q_obj_SocketAddress_base; ++ ++typedef struct SocketAddress SocketAddress; ++ ++typedef struct SocketAddressList SocketAddressList; ++ ++typedef struct InetSocketAddressBaseList InetSocketAddressBaseList; ++ ++struct InetSocketAddressBase { ++ char *host; ++ char *port; ++}; ++ ++void qapi_free_InetSocketAddressBase(InetSocketAddressBase *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(InetSocketAddressBase, qapi_free_InetSocketAddressBase) ++ ++struct InetSocketAddress { ++ /* Members inherited from InetSocketAddressBase: */ ++ char *host; ++ char *port; ++ /* Own members: */ ++ bool has_numeric; ++ bool numeric; ++ bool has_to; ++ uint16_t to; ++ bool has_ipv4; ++ bool ipv4; ++ bool has_ipv6; ++ bool ipv6; ++ bool has_keep_alive; ++ bool keep_alive; ++#if defined(HAVE_IPPROTO_MPTCP) ++ bool has_mptcp; ++ bool mptcp; ++#endif /* defined(HAVE_IPPROTO_MPTCP) */ ++}; ++ ++static inline InetSocketAddressBase *qapi_InetSocketAddress_base(const InetSocketAddress *obj) ++{ ++ return (InetSocketAddressBase *)obj; ++} ++ ++void qapi_free_InetSocketAddress(InetSocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(InetSocketAddress, qapi_free_InetSocketAddress) ++ ++struct UnixSocketAddress { ++ char *path; ++#if defined(CONFIG_LINUX) ++ bool has_abstract; ++ bool abstract; ++#endif /* defined(CONFIG_LINUX) */ ++#if defined(CONFIG_LINUX) ++ bool has_tight; ++ bool tight; ++#endif /* defined(CONFIG_LINUX) */ ++}; ++ ++void qapi_free_UnixSocketAddress(UnixSocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(UnixSocketAddress, qapi_free_UnixSocketAddress) ++ ++struct VsockSocketAddress { ++ char *cid; ++ char *port; ++}; ++ ++void qapi_free_VsockSocketAddress(VsockSocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(VsockSocketAddress, qapi_free_VsockSocketAddress) ++ ++struct FdSocketAddress { ++ char *str; ++}; ++ ++void qapi_free_FdSocketAddress(FdSocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(FdSocketAddress, qapi_free_FdSocketAddress) ++ ++struct InetSocketAddressWrapper { ++ InetSocketAddress *data; ++}; ++ ++void qapi_free_InetSocketAddressWrapper(InetSocketAddressWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(InetSocketAddressWrapper, qapi_free_InetSocketAddressWrapper) ++ ++struct UnixSocketAddressWrapper { ++ UnixSocketAddress *data; ++}; ++ ++void qapi_free_UnixSocketAddressWrapper(UnixSocketAddressWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(UnixSocketAddressWrapper, qapi_free_UnixSocketAddressWrapper) ++ ++struct VsockSocketAddressWrapper { ++ VsockSocketAddress *data; ++}; ++ ++void qapi_free_VsockSocketAddressWrapper(VsockSocketAddressWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(VsockSocketAddressWrapper, qapi_free_VsockSocketAddressWrapper) ++ ++struct FdSocketAddressWrapper { ++ FdSocketAddress *data; ++}; ++ ++void qapi_free_FdSocketAddressWrapper(FdSocketAddressWrapper *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(FdSocketAddressWrapper, qapi_free_FdSocketAddressWrapper) ++ ++struct q_obj_SocketAddressLegacy_base { ++ SocketAddressType type; ++}; ++ ++struct SocketAddressLegacy { ++ SocketAddressType type; ++ union { /* union tag is @type */ ++ InetSocketAddressWrapper inet; ++ UnixSocketAddressWrapper q_unix; ++ VsockSocketAddressWrapper vsock; ++ FdSocketAddressWrapper fd; ++ } u; ++}; ++ ++void qapi_free_SocketAddressLegacy(SocketAddressLegacy *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SocketAddressLegacy, qapi_free_SocketAddressLegacy) ++ ++struct q_obj_SocketAddress_base { ++ SocketAddressType type; ++}; ++ ++struct SocketAddress { ++ SocketAddressType type; ++ union { /* union tag is @type */ ++ InetSocketAddress inet; ++ UnixSocketAddress q_unix; ++ VsockSocketAddress vsock; ++ FdSocketAddress fd; ++ } u; ++}; ++ ++void qapi_free_SocketAddress(SocketAddress *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SocketAddress, qapi_free_SocketAddress) ++ ++struct SocketAddressList { ++ SocketAddressList *next; ++ SocketAddress *value; ++}; ++ ++void qapi_free_SocketAddressList(SocketAddressList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(SocketAddressList, qapi_free_SocketAddressList) ++ ++struct InetSocketAddressBaseList { ++ InetSocketAddressBaseList *next; ++ InetSocketAddressBase *value; ++}; ++ ++void qapi_free_InetSocketAddressBaseList(InetSocketAddressBaseList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(InetSocketAddressBaseList, qapi_free_InetSocketAddressBaseList) ++ ++#endif /* QAPI_TYPES_SOCKETS_H */ +diff --git a/include/qapi/qapi-types-yank.h b/include/qapi/qapi-types-yank.h +new file mode 100644 +index 00000000..da699a92 +--- /dev/null ++++ b/include/qapi/qapi-types-yank.h +@@ -0,0 +1,83 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_TYPES_YANK_H ++#define QAPI_TYPES_YANK_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++typedef enum YankInstanceType { ++ YANK_INSTANCE_TYPE_BLOCK_NODE, ++ YANK_INSTANCE_TYPE_CHARDEV, ++ YANK_INSTANCE_TYPE_MIGRATION, ++ YANK_INSTANCE_TYPE__MAX, ++} YankInstanceType; ++ ++#define YankInstanceType_str(val) \ ++ qapi_enum_lookup(&YankInstanceType_lookup, (val)) ++ ++extern const QEnumLookup YankInstanceType_lookup; ++ ++typedef struct YankInstanceBlockNode YankInstanceBlockNode; ++ ++typedef struct YankInstanceChardev YankInstanceChardev; ++ ++typedef struct q_obj_YankInstance_base q_obj_YankInstance_base; ++ ++typedef struct YankInstance YankInstance; ++ ++typedef struct YankInstanceList YankInstanceList; ++ ++typedef struct q_obj_yank_arg q_obj_yank_arg; ++ ++struct YankInstanceBlockNode { ++ char *node_name; ++}; ++ ++void qapi_free_YankInstanceBlockNode(YankInstanceBlockNode *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(YankInstanceBlockNode, qapi_free_YankInstanceBlockNode) ++ ++struct YankInstanceChardev { ++ char *id; ++}; ++ ++void qapi_free_YankInstanceChardev(YankInstanceChardev *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(YankInstanceChardev, qapi_free_YankInstanceChardev) ++ ++struct q_obj_YankInstance_base { ++ YankInstanceType type; ++}; ++ ++struct YankInstance { ++ YankInstanceType type; ++ union { /* union tag is @type */ ++ YankInstanceBlockNode block_node; ++ YankInstanceChardev chardev; ++ } u; ++}; ++ ++void qapi_free_YankInstance(YankInstance *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(YankInstance, qapi_free_YankInstance) ++ ++struct YankInstanceList { ++ YankInstanceList *next; ++ YankInstance *value; ++}; ++ ++void qapi_free_YankInstanceList(YankInstanceList *obj); ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(YankInstanceList, qapi_free_YankInstanceList) ++ ++struct q_obj_yank_arg { ++ YankInstanceList *instances; ++}; ++ ++#endif /* QAPI_TYPES_YANK_H */ +diff --git a/include/qapi/qapi-visit-block-core.h b/include/qapi/qapi-visit-block-core.h +new file mode 100644 +index 00000000..9462b281 +--- /dev/null ++++ b/include/qapi/qapi-visit-block-core.h +@@ -0,0 +1,960 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_BLOCK_CORE_H ++#define QAPI_VISIT_BLOCK_CORE_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-block-core.h" ++ ++#include "qapi-visit-common.h" ++#include "qapi-visit-crypto.h" ++#include "qapi-visit-job.h" ++#include "qapi-visit-sockets.h" ++ ++bool visit_type_SnapshotInfo_members(Visitor *v, SnapshotInfo *obj, Error **errp); ++ ++bool visit_type_SnapshotInfo(Visitor *v, const char *name, ++ SnapshotInfo **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2EncryptionBase_members(Visitor *v, ImageInfoSpecificQCow2EncryptionBase *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2EncryptionBase(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2EncryptionBase **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2Encryption_members(Visitor *v, ImageInfoSpecificQCow2Encryption *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2Encryption(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2Encryption **obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfoList(Visitor *v, const char *name, ++ Qcow2BitmapInfoList **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2_members(Visitor *v, ImageInfoSpecificQCow2 *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2 **obj, Error **errp); ++ ++bool visit_type_VmdkExtentInfoList(Visitor *v, const char *name, ++ VmdkExtentInfoList **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificVmdk_members(Visitor *v, ImageInfoSpecificVmdk *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificVmdk(Visitor *v, const char *name, ++ ImageInfoSpecificVmdk **obj, Error **errp); ++ ++bool visit_type_VmdkExtentInfo_members(Visitor *v, VmdkExtentInfo *obj, Error **errp); ++ ++bool visit_type_VmdkExtentInfo(Visitor *v, const char *name, ++ VmdkExtentInfo **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificRbd_members(Visitor *v, ImageInfoSpecificRbd *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificRbd(Visitor *v, const char *name, ++ ImageInfoSpecificRbd **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificFile_members(Visitor *v, ImageInfoSpecificFile *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificFile(Visitor *v, const char *name, ++ ImageInfoSpecificFile **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificKind(Visitor *v, const char *name, ++ ImageInfoSpecificKind *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2Wrapper_members(Visitor *v, ImageInfoSpecificQCow2Wrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificQCow2Wrapper(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2Wrapper **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificVmdkWrapper_members(Visitor *v, ImageInfoSpecificVmdkWrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificVmdkWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificVmdkWrapper **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificLUKSWrapper_members(Visitor *v, ImageInfoSpecificLUKSWrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificLUKSWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificLUKSWrapper **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificRbdWrapper_members(Visitor *v, ImageInfoSpecificRbdWrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificRbdWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificRbdWrapper **obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificFileWrapper_members(Visitor *v, ImageInfoSpecificFileWrapper *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecificFileWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificFileWrapper **obj, Error **errp); ++ ++bool visit_type_q_obj_ImageInfoSpecific_base_members(Visitor *v, q_obj_ImageInfoSpecific_base *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecific_members(Visitor *v, ImageInfoSpecific *obj, Error **errp); ++ ++bool visit_type_ImageInfoSpecific(Visitor *v, const char *name, ++ ImageInfoSpecific **obj, Error **errp); ++ ++bool visit_type_SnapshotInfoList(Visitor *v, const char *name, ++ SnapshotInfoList **obj, Error **errp); ++ ++bool visit_type_BlockNodeInfo_members(Visitor *v, BlockNodeInfo *obj, Error **errp); ++ ++bool visit_type_BlockNodeInfo(Visitor *v, const char *name, ++ BlockNodeInfo **obj, Error **errp); ++ ++bool visit_type_ImageInfo_members(Visitor *v, ImageInfo *obj, Error **errp); ++ ++bool visit_type_ImageInfo(Visitor *v, const char *name, ++ ImageInfo **obj, Error **errp); ++ ++bool visit_type_BlockChildInfo_members(Visitor *v, BlockChildInfo *obj, Error **errp); ++ ++bool visit_type_BlockChildInfo(Visitor *v, const char *name, ++ BlockChildInfo **obj, Error **errp); ++ ++bool visit_type_BlockChildInfoList(Visitor *v, const char *name, ++ BlockChildInfoList **obj, Error **errp); ++ ++bool visit_type_BlockGraphInfo_members(Visitor *v, BlockGraphInfo *obj, Error **errp); ++ ++bool visit_type_BlockGraphInfo(Visitor *v, const char *name, ++ BlockGraphInfo **obj, Error **errp); ++ ++bool visit_type_ImageCheck_members(Visitor *v, ImageCheck *obj, Error **errp); ++ ++bool visit_type_ImageCheck(Visitor *v, const char *name, ++ ImageCheck **obj, Error **errp); ++ ++bool visit_type_MapEntry_members(Visitor *v, MapEntry *obj, Error **errp); ++ ++bool visit_type_MapEntry(Visitor *v, const char *name, ++ MapEntry **obj, Error **errp); ++ ++bool visit_type_BlockdevCacheInfo_members(Visitor *v, BlockdevCacheInfo *obj, Error **errp); ++ ++bool visit_type_BlockdevCacheInfo(Visitor *v, const char *name, ++ BlockdevCacheInfo **obj, Error **errp); ++ ++bool visit_type_BlockDirtyInfoList(Visitor *v, const char *name, ++ BlockDirtyInfoList **obj, Error **errp); ++ ++bool visit_type_BlockDeviceInfo_members(Visitor *v, BlockDeviceInfo *obj, Error **errp); ++ ++bool visit_type_BlockDeviceInfo(Visitor *v, const char *name, ++ BlockDeviceInfo **obj, Error **errp); ++ ++bool visit_type_BlockDeviceIoStatus(Visitor *v, const char *name, ++ BlockDeviceIoStatus *obj, Error **errp); ++ ++bool visit_type_BlockDirtyInfo_members(Visitor *v, BlockDirtyInfo *obj, Error **errp); ++ ++bool visit_type_BlockDirtyInfo(Visitor *v, const char *name, ++ BlockDirtyInfo **obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfoFlags(Visitor *v, const char *name, ++ Qcow2BitmapInfoFlags *obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfoFlagsList(Visitor *v, const char *name, ++ Qcow2BitmapInfoFlagsList **obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfo_members(Visitor *v, Qcow2BitmapInfo *obj, Error **errp); ++ ++bool visit_type_Qcow2BitmapInfo(Visitor *v, const char *name, ++ Qcow2BitmapInfo **obj, Error **errp); ++ ++bool visit_type_BlockLatencyHistogramInfo_members(Visitor *v, BlockLatencyHistogramInfo *obj, Error **errp); ++ ++bool visit_type_BlockLatencyHistogramInfo(Visitor *v, const char *name, ++ BlockLatencyHistogramInfo **obj, Error **errp); ++ ++bool visit_type_BlockInfo_members(Visitor *v, BlockInfo *obj, Error **errp); ++ ++bool visit_type_BlockInfo(Visitor *v, const char *name, ++ BlockInfo **obj, Error **errp); ++ ++bool visit_type_BlockMeasureInfo_members(Visitor *v, BlockMeasureInfo *obj, Error **errp); ++ ++bool visit_type_BlockMeasureInfo(Visitor *v, const char *name, ++ BlockMeasureInfo **obj, Error **errp); ++ ++bool visit_type_BlockInfoList(Visitor *v, const char *name, ++ BlockInfoList **obj, Error **errp); ++ ++bool visit_type_BlockDeviceTimedStats_members(Visitor *v, BlockDeviceTimedStats *obj, Error **errp); ++ ++bool visit_type_BlockDeviceTimedStats(Visitor *v, const char *name, ++ BlockDeviceTimedStats **obj, Error **errp); ++ ++bool visit_type_BlockDeviceTimedStatsList(Visitor *v, const char *name, ++ BlockDeviceTimedStatsList **obj, Error **errp); ++ ++bool visit_type_BlockDeviceStats_members(Visitor *v, BlockDeviceStats *obj, Error **errp); ++ ++bool visit_type_BlockDeviceStats(Visitor *v, const char *name, ++ BlockDeviceStats **obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecificFile_members(Visitor *v, BlockStatsSpecificFile *obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecificFile(Visitor *v, const char *name, ++ BlockStatsSpecificFile **obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecificNvme_members(Visitor *v, BlockStatsSpecificNvme *obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecificNvme(Visitor *v, const char *name, ++ BlockStatsSpecificNvme **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockStatsSpecific_base_members(Visitor *v, q_obj_BlockStatsSpecific_base *obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecific_members(Visitor *v, BlockStatsSpecific *obj, Error **errp); ++ ++bool visit_type_BlockStatsSpecific(Visitor *v, const char *name, ++ BlockStatsSpecific **obj, Error **errp); ++ ++bool visit_type_BlockStats_members(Visitor *v, BlockStats *obj, Error **errp); ++ ++bool visit_type_BlockStats(Visitor *v, const char *name, ++ BlockStats **obj, Error **errp); ++ ++bool visit_type_q_obj_query_blockstats_arg_members(Visitor *v, q_obj_query_blockstats_arg *obj, Error **errp); ++ ++bool visit_type_BlockStatsList(Visitor *v, const char *name, ++ BlockStatsList **obj, Error **errp); ++ ++bool visit_type_BlockdevOnError(Visitor *v, const char *name, ++ BlockdevOnError *obj, Error **errp); ++ ++bool visit_type_MirrorSyncMode(Visitor *v, const char *name, ++ MirrorSyncMode *obj, Error **errp); ++ ++bool visit_type_BitmapSyncMode(Visitor *v, const char *name, ++ BitmapSyncMode *obj, Error **errp); ++ ++bool visit_type_MirrorCopyMode(Visitor *v, const char *name, ++ MirrorCopyMode *obj, Error **errp); ++ ++bool visit_type_BlockJobInfoMirror_members(Visitor *v, BlockJobInfoMirror *obj, Error **errp); ++ ++bool visit_type_BlockJobInfoMirror(Visitor *v, const char *name, ++ BlockJobInfoMirror **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockJobInfo_base_members(Visitor *v, q_obj_BlockJobInfo_base *obj, Error **errp); ++ ++bool visit_type_BlockJobInfo_members(Visitor *v, BlockJobInfo *obj, Error **errp); ++ ++bool visit_type_BlockJobInfo(Visitor *v, const char *name, ++ BlockJobInfo **obj, Error **errp); ++ ++bool visit_type_BlockJobInfoList(Visitor *v, const char *name, ++ BlockJobInfoList **obj, Error **errp); ++ ++bool visit_type_q_obj_block_resize_arg_members(Visitor *v, q_obj_block_resize_arg *obj, Error **errp); ++ ++bool visit_type_NewImageMode(Visitor *v, const char *name, ++ NewImageMode *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshotSync_members(Visitor *v, BlockdevSnapshotSync *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshotSync(Visitor *v, const char *name, ++ BlockdevSnapshotSync **obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshot_members(Visitor *v, BlockdevSnapshot *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshot(Visitor *v, const char *name, ++ BlockdevSnapshot **obj, Error **errp); ++ ++bool visit_type_BackupPerf_members(Visitor *v, BackupPerf *obj, Error **errp); ++ ++bool visit_type_BackupPerf(Visitor *v, const char *name, ++ BackupPerf **obj, Error **errp); ++ ++bool visit_type_BackupCommon_members(Visitor *v, BackupCommon *obj, Error **errp); ++ ++bool visit_type_BackupCommon(Visitor *v, const char *name, ++ BackupCommon **obj, Error **errp); ++ ++bool visit_type_DriveBackup_members(Visitor *v, DriveBackup *obj, Error **errp); ++ ++bool visit_type_DriveBackup(Visitor *v, const char *name, ++ DriveBackup **obj, Error **errp); ++ ++bool visit_type_BlockdevBackup_members(Visitor *v, BlockdevBackup *obj, Error **errp); ++ ++bool visit_type_BlockdevBackup(Visitor *v, const char *name, ++ BlockdevBackup **obj, Error **errp); ++ ++bool visit_type_q_obj_change_backing_file_arg_members(Visitor *v, q_obj_change_backing_file_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_commit_arg_members(Visitor *v, q_obj_block_commit_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_query_named_block_nodes_arg_members(Visitor *v, q_obj_query_named_block_nodes_arg *obj, Error **errp); ++ ++bool visit_type_BlockDeviceInfoList(Visitor *v, const char *name, ++ BlockDeviceInfoList **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphNodeType(Visitor *v, const char *name, ++ XDbgBlockGraphNodeType *obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphNode_members(Visitor *v, XDbgBlockGraphNode *obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphNode(Visitor *v, const char *name, ++ XDbgBlockGraphNode **obj, Error **errp); ++ ++bool visit_type_BlockPermission(Visitor *v, const char *name, ++ BlockPermission *obj, Error **errp); ++ ++bool visit_type_BlockPermissionList(Visitor *v, const char *name, ++ BlockPermissionList **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphEdge_members(Visitor *v, XDbgBlockGraphEdge *obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphEdge(Visitor *v, const char *name, ++ XDbgBlockGraphEdge **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphNodeList(Visitor *v, const char *name, ++ XDbgBlockGraphNodeList **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraphEdgeList(Visitor *v, const char *name, ++ XDbgBlockGraphEdgeList **obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraph_members(Visitor *v, XDbgBlockGraph *obj, Error **errp); ++ ++bool visit_type_XDbgBlockGraph(Visitor *v, const char *name, ++ XDbgBlockGraph **obj, Error **errp); ++ ++bool visit_type_DriveMirror_members(Visitor *v, DriveMirror *obj, Error **errp); ++ ++bool visit_type_DriveMirror(Visitor *v, const char *name, ++ DriveMirror **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmap_members(Visitor *v, BlockDirtyBitmap *obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmap(Visitor *v, const char *name, ++ BlockDirtyBitmap **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapAdd_members(Visitor *v, BlockDirtyBitmapAdd *obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapAdd(Visitor *v, const char *name, ++ BlockDirtyBitmapAdd **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapOrStr(Visitor *v, const char *name, ++ BlockDirtyBitmapOrStr **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapOrStrList(Visitor *v, const char *name, ++ BlockDirtyBitmapOrStrList **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapMerge_members(Visitor *v, BlockDirtyBitmapMerge *obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapMerge(Visitor *v, const char *name, ++ BlockDirtyBitmapMerge **obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapSha256_members(Visitor *v, BlockDirtyBitmapSha256 *obj, Error **errp); ++ ++bool visit_type_BlockDirtyBitmapSha256(Visitor *v, const char *name, ++ BlockDirtyBitmapSha256 **obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_mirror_arg_members(Visitor *v, q_obj_blockdev_mirror_arg *obj, Error **errp); ++ ++bool visit_type_BlockIOThrottle_members(Visitor *v, BlockIOThrottle *obj, Error **errp); ++ ++bool visit_type_BlockIOThrottle(Visitor *v, const char *name, ++ BlockIOThrottle **obj, Error **errp); ++ ++bool visit_type_ThrottleLimits_members(Visitor *v, ThrottleLimits *obj, Error **errp); ++ ++bool visit_type_ThrottleLimits(Visitor *v, const char *name, ++ ThrottleLimits **obj, Error **errp); ++ ++bool visit_type_ThrottleGroupProperties_members(Visitor *v, ThrottleGroupProperties *obj, Error **errp); ++ ++bool visit_type_ThrottleGroupProperties(Visitor *v, const char *name, ++ ThrottleGroupProperties **obj, Error **errp); ++ ++bool visit_type_q_obj_block_stream_arg_members(Visitor *v, q_obj_block_stream_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_set_speed_arg_members(Visitor *v, q_obj_block_job_set_speed_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_cancel_arg_members(Visitor *v, q_obj_block_job_cancel_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_pause_arg_members(Visitor *v, q_obj_block_job_pause_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_resume_arg_members(Visitor *v, q_obj_block_job_resume_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_complete_arg_members(Visitor *v, q_obj_block_job_complete_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_dismiss_arg_members(Visitor *v, q_obj_block_job_dismiss_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_job_finalize_arg_members(Visitor *v, q_obj_block_job_finalize_arg *obj, Error **errp); ++ ++bool visit_type_BlockJobChangeOptionsMirror_members(Visitor *v, BlockJobChangeOptionsMirror *obj, Error **errp); ++ ++bool visit_type_BlockJobChangeOptionsMirror(Visitor *v, const char *name, ++ BlockJobChangeOptionsMirror **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockJobChangeOptions_base_members(Visitor *v, q_obj_BlockJobChangeOptions_base *obj, Error **errp); ++ ++bool visit_type_BlockJobChangeOptions_members(Visitor *v, BlockJobChangeOptions *obj, Error **errp); ++ ++bool visit_type_BlockJobChangeOptions(Visitor *v, const char *name, ++ BlockJobChangeOptions **obj, Error **errp); ++ ++bool visit_type_BlockdevDiscardOptions(Visitor *v, const char *name, ++ BlockdevDiscardOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevDetectZeroesOptions(Visitor *v, const char *name, ++ BlockdevDetectZeroesOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevAioOptions(Visitor *v, const char *name, ++ BlockdevAioOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevCacheOptions_members(Visitor *v, BlockdevCacheOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevCacheOptions(Visitor *v, const char *name, ++ BlockdevCacheOptions **obj, Error **errp); ++ ++bool visit_type_BlockdevDriver(Visitor *v, const char *name, ++ BlockdevDriver *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsFile_members(Visitor *v, BlockdevOptionsFile *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsFile(Visitor *v, const char *name, ++ BlockdevOptionsFile **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNull_members(Visitor *v, BlockdevOptionsNull *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNull(Visitor *v, const char *name, ++ BlockdevOptionsNull **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNVMe_members(Visitor *v, BlockdevOptionsNVMe *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNVMe(Visitor *v, const char *name, ++ BlockdevOptionsNVMe **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVVFAT_members(Visitor *v, BlockdevOptionsVVFAT *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVVFAT(Visitor *v, const char *name, ++ BlockdevOptionsVVFAT **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGenericFormat_members(Visitor *v, BlockdevOptionsGenericFormat *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGenericFormat(Visitor *v, const char *name, ++ BlockdevOptionsGenericFormat **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsLUKS_members(Visitor *v, BlockdevOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsLUKS(Visitor *v, const char *name, ++ BlockdevOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGenericCOWFormat_members(Visitor *v, BlockdevOptionsGenericCOWFormat *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGenericCOWFormat(Visitor *v, const char *name, ++ BlockdevOptionsGenericCOWFormat **obj, Error **errp); ++ ++bool visit_type_Qcow2OverlapCheckMode(Visitor *v, const char *name, ++ Qcow2OverlapCheckMode *obj, Error **errp); ++ ++bool visit_type_Qcow2OverlapCheckFlags_members(Visitor *v, Qcow2OverlapCheckFlags *obj, Error **errp); ++ ++bool visit_type_Qcow2OverlapCheckFlags(Visitor *v, const char *name, ++ Qcow2OverlapCheckFlags **obj, Error **errp); ++ ++bool visit_type_Qcow2OverlapChecks(Visitor *v, const char *name, ++ Qcow2OverlapChecks **obj, Error **errp); ++ ++bool visit_type_BlockdevQcowEncryptionFormat(Visitor *v, const char *name, ++ BlockdevQcowEncryptionFormat *obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevQcowEncryption_base_members(Visitor *v, q_obj_BlockdevQcowEncryption_base *obj, Error **errp); ++ ++bool visit_type_BlockdevQcowEncryption_members(Visitor *v, BlockdevQcowEncryption *obj, Error **errp); ++ ++bool visit_type_BlockdevQcowEncryption(Visitor *v, const char *name, ++ BlockdevQcowEncryption **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQcow_members(Visitor *v, BlockdevOptionsQcow *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQcow(Visitor *v, const char *name, ++ BlockdevOptionsQcow **obj, Error **errp); ++ ++bool visit_type_BlockdevQcow2EncryptionFormat(Visitor *v, const char *name, ++ BlockdevQcow2EncryptionFormat *obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevQcow2Encryption_base_members(Visitor *v, q_obj_BlockdevQcow2Encryption_base *obj, Error **errp); ++ ++bool visit_type_BlockdevQcow2Encryption_members(Visitor *v, BlockdevQcow2Encryption *obj, Error **errp); ++ ++bool visit_type_BlockdevQcow2Encryption(Visitor *v, const char *name, ++ BlockdevQcow2Encryption **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsPreallocate_members(Visitor *v, BlockdevOptionsPreallocate *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsPreallocate(Visitor *v, const char *name, ++ BlockdevOptionsPreallocate **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQcow2_members(Visitor *v, BlockdevOptionsQcow2 *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQcow2(Visitor *v, const char *name, ++ BlockdevOptionsQcow2 **obj, Error **errp); ++ ++bool visit_type_SshHostKeyCheckMode(Visitor *v, const char *name, ++ SshHostKeyCheckMode *obj, Error **errp); ++ ++bool visit_type_SshHostKeyCheckHashType(Visitor *v, const char *name, ++ SshHostKeyCheckHashType *obj, Error **errp); ++ ++bool visit_type_SshHostKeyHash_members(Visitor *v, SshHostKeyHash *obj, Error **errp); ++ ++bool visit_type_SshHostKeyHash(Visitor *v, const char *name, ++ SshHostKeyHash **obj, Error **errp); ++ ++bool visit_type_q_obj_SshHostKeyCheck_base_members(Visitor *v, q_obj_SshHostKeyCheck_base *obj, Error **errp); ++ ++bool visit_type_SshHostKeyCheck_members(Visitor *v, SshHostKeyCheck *obj, Error **errp); ++ ++bool visit_type_SshHostKeyCheck(Visitor *v, const char *name, ++ SshHostKeyCheck **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsSsh_members(Visitor *v, BlockdevOptionsSsh *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsSsh(Visitor *v, const char *name, ++ BlockdevOptionsSsh **obj, Error **errp); ++ ++bool visit_type_BlkdebugEvent(Visitor *v, const char *name, ++ BlkdebugEvent *obj, Error **errp); ++ ++bool visit_type_BlkdebugIOType(Visitor *v, const char *name, ++ BlkdebugIOType *obj, Error **errp); ++ ++bool visit_type_BlkdebugInjectErrorOptions_members(Visitor *v, BlkdebugInjectErrorOptions *obj, Error **errp); ++ ++bool visit_type_BlkdebugInjectErrorOptions(Visitor *v, const char *name, ++ BlkdebugInjectErrorOptions **obj, Error **errp); ++ ++bool visit_type_BlkdebugSetStateOptions_members(Visitor *v, BlkdebugSetStateOptions *obj, Error **errp); ++ ++bool visit_type_BlkdebugSetStateOptions(Visitor *v, const char *name, ++ BlkdebugSetStateOptions **obj, Error **errp); ++ ++bool visit_type_BlkdebugInjectErrorOptionsList(Visitor *v, const char *name, ++ BlkdebugInjectErrorOptionsList **obj, Error **errp); ++ ++bool visit_type_BlkdebugSetStateOptionsList(Visitor *v, const char *name, ++ BlkdebugSetStateOptionsList **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkdebug_members(Visitor *v, BlockdevOptionsBlkdebug *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkdebug(Visitor *v, const char *name, ++ BlockdevOptionsBlkdebug **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlklogwrites_members(Visitor *v, BlockdevOptionsBlklogwrites *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlklogwrites(Visitor *v, const char *name, ++ BlockdevOptionsBlklogwrites **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkverify_members(Visitor *v, BlockdevOptionsBlkverify *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkverify(Visitor *v, const char *name, ++ BlockdevOptionsBlkverify **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkreplay_members(Visitor *v, BlockdevOptionsBlkreplay *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsBlkreplay(Visitor *v, const char *name, ++ BlockdevOptionsBlkreplay **obj, Error **errp); ++ ++bool visit_type_QuorumReadPattern(Visitor *v, const char *name, ++ QuorumReadPattern *obj, Error **errp); ++ ++bool visit_type_BlockdevRefList(Visitor *v, const char *name, ++ BlockdevRefList **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQuorum_members(Visitor *v, BlockdevOptionsQuorum *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsQuorum(Visitor *v, const char *name, ++ BlockdevOptionsQuorum **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGluster_members(Visitor *v, BlockdevOptionsGluster *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsGluster(Visitor *v, const char *name, ++ BlockdevOptionsGluster **obj, Error **errp); ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsIoUring_members(Visitor *v, BlockdevOptionsIoUring *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsIoUring(Visitor *v, const char *name, ++ BlockdevOptionsIoUring **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsNvmeIoUring_members(Visitor *v, BlockdevOptionsNvmeIoUring *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNvmeIoUring(Visitor *v, const char *name, ++ BlockdevOptionsNvmeIoUring **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVfioPci_members(Visitor *v, BlockdevOptionsVirtioBlkVfioPci *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVirtioBlkVfioPci(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVfioPci **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVhostUser_members(Visitor *v, BlockdevOptionsVirtioBlkVhostUser *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVirtioBlkVhostUser(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVhostUser **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVhostVdpa_members(Visitor *v, BlockdevOptionsVirtioBlkVhostVdpa *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsVirtioBlkVhostVdpa(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVhostVdpa **obj, Error **errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ ++bool visit_type_IscsiTransport(Visitor *v, const char *name, ++ IscsiTransport *obj, Error **errp); ++ ++bool visit_type_IscsiHeaderDigest(Visitor *v, const char *name, ++ IscsiHeaderDigest *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsIscsi_members(Visitor *v, BlockdevOptionsIscsi *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsIscsi(Visitor *v, const char *name, ++ BlockdevOptionsIscsi **obj, Error **errp); ++ ++bool visit_type_RbdAuthMode(Visitor *v, const char *name, ++ RbdAuthMode *obj, Error **errp); ++ ++bool visit_type_RbdImageEncryptionFormat(Visitor *v, const char *name, ++ RbdImageEncryptionFormat *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKSBase_members(Visitor *v, RbdEncryptionOptionsLUKSBase *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKSBase(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKSBase **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKSBase_members(Visitor *v, RbdEncryptionCreateOptionsLUKSBase *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKSBase(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKSBase **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKS_members(Visitor *v, RbdEncryptionOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKS(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKS2_members(Visitor *v, RbdEncryptionOptionsLUKS2 *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKS2(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKS2 **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKSAny_members(Visitor *v, RbdEncryptionOptionsLUKSAny *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptionsLUKSAny(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKSAny **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS_members(Visitor *v, RbdEncryptionCreateOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS2_members(Visitor *v, RbdEncryptionCreateOptionsLUKS2 *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS2(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKS2 **obj, Error **errp); ++ ++bool visit_type_q_obj_RbdEncryptionOptions_base_members(Visitor *v, q_obj_RbdEncryptionOptions_base *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptions_members(Visitor *v, RbdEncryptionOptions *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionOptions(Visitor *v, const char *name, ++ RbdEncryptionOptions **obj, Error **errp); ++ ++bool visit_type_q_obj_RbdEncryptionCreateOptions_base_members(Visitor *v, q_obj_RbdEncryptionCreateOptions_base *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptions_members(Visitor *v, RbdEncryptionCreateOptions *obj, Error **errp); ++ ++bool visit_type_RbdEncryptionCreateOptions(Visitor *v, const char *name, ++ RbdEncryptionCreateOptions **obj, Error **errp); ++ ++bool visit_type_RbdAuthModeList(Visitor *v, const char *name, ++ RbdAuthModeList **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsRbd_members(Visitor *v, BlockdevOptionsRbd *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsRbd(Visitor *v, const char *name, ++ BlockdevOptionsRbd **obj, Error **errp); ++ ++#if defined(CONFIG_REPLICATION) ++bool visit_type_ReplicationMode(Visitor *v, const char *name, ++ ReplicationMode *obj, Error **errp); ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++bool visit_type_BlockdevOptionsReplication_members(Visitor *v, BlockdevOptionsReplication *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsReplication(Visitor *v, const char *name, ++ BlockdevOptionsReplication **obj, Error **errp); ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++bool visit_type_NFSTransport(Visitor *v, const char *name, ++ NFSTransport *obj, Error **errp); ++ ++bool visit_type_NFSServer_members(Visitor *v, NFSServer *obj, Error **errp); ++ ++bool visit_type_NFSServer(Visitor *v, const char *name, ++ NFSServer **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNfs_members(Visitor *v, BlockdevOptionsNfs *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNfs(Visitor *v, const char *name, ++ BlockdevOptionsNfs **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlBase_members(Visitor *v, BlockdevOptionsCurlBase *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlBase(Visitor *v, const char *name, ++ BlockdevOptionsCurlBase **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlHttp_members(Visitor *v, BlockdevOptionsCurlHttp *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlHttp(Visitor *v, const char *name, ++ BlockdevOptionsCurlHttp **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlHttps_members(Visitor *v, BlockdevOptionsCurlHttps *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlHttps(Visitor *v, const char *name, ++ BlockdevOptionsCurlHttps **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlFtp_members(Visitor *v, BlockdevOptionsCurlFtp *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlFtp(Visitor *v, const char *name, ++ BlockdevOptionsCurlFtp **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlFtps_members(Visitor *v, BlockdevOptionsCurlFtps *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCurlFtps(Visitor *v, const char *name, ++ BlockdevOptionsCurlFtps **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNbd_members(Visitor *v, BlockdevOptionsNbd *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsNbd(Visitor *v, const char *name, ++ BlockdevOptionsNbd **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsRaw_members(Visitor *v, BlockdevOptionsRaw *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsRaw(Visitor *v, const char *name, ++ BlockdevOptionsRaw **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsThrottle_members(Visitor *v, BlockdevOptionsThrottle *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsThrottle(Visitor *v, const char *name, ++ BlockdevOptionsThrottle **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCor_members(Visitor *v, BlockdevOptionsCor *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCor(Visitor *v, const char *name, ++ BlockdevOptionsCor **obj, Error **errp); ++ ++bool visit_type_OnCbwError(Visitor *v, const char *name, ++ OnCbwError *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCbw_members(Visitor *v, BlockdevOptionsCbw *obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsCbw(Visitor *v, const char *name, ++ BlockdevOptionsCbw **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevOptions_base_members(Visitor *v, q_obj_BlockdevOptions_base *obj, Error **errp); ++ ++bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevOptions(Visitor *v, const char *name, ++ BlockdevOptions **obj, Error **errp); ++ ++bool visit_type_BlockdevRef(Visitor *v, const char *name, ++ BlockdevRef **obj, Error **errp); ++ ++bool visit_type_BlockdevRefOrNull(Visitor *v, const char *name, ++ BlockdevRefOrNull **obj, Error **errp); ++ ++bool visit_type_BlockdevOptionsList(Visitor *v, const char *name, ++ BlockdevOptionsList **obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_reopen_arg_members(Visitor *v, q_obj_blockdev_reopen_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_del_arg_members(Visitor *v, q_obj_blockdev_del_arg *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsFile_members(Visitor *v, BlockdevCreateOptionsFile *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsFile(Visitor *v, const char *name, ++ BlockdevCreateOptionsFile **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsGluster_members(Visitor *v, BlockdevCreateOptionsGluster *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsGluster(Visitor *v, const char *name, ++ BlockdevCreateOptionsGluster **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsLUKS_members(Visitor *v, BlockdevCreateOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsLUKS(Visitor *v, const char *name, ++ BlockdevCreateOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsNfs_members(Visitor *v, BlockdevCreateOptionsNfs *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsNfs(Visitor *v, const char *name, ++ BlockdevCreateOptionsNfs **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsParallels_members(Visitor *v, BlockdevCreateOptionsParallels *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsParallels(Visitor *v, const char *name, ++ BlockdevCreateOptionsParallels **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQcow_members(Visitor *v, BlockdevCreateOptionsQcow *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQcow(Visitor *v, const char *name, ++ BlockdevCreateOptionsQcow **obj, Error **errp); ++ ++bool visit_type_BlockdevQcow2Version(Visitor *v, const char *name, ++ BlockdevQcow2Version *obj, Error **errp); ++ ++bool visit_type_Qcow2CompressionType(Visitor *v, const char *name, ++ Qcow2CompressionType *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQcow2_members(Visitor *v, BlockdevCreateOptionsQcow2 *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQcow2(Visitor *v, const char *name, ++ BlockdevCreateOptionsQcow2 **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQed_members(Visitor *v, BlockdevCreateOptionsQed *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsQed(Visitor *v, const char *name, ++ BlockdevCreateOptionsQed **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsRbd_members(Visitor *v, BlockdevCreateOptionsRbd *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsRbd(Visitor *v, const char *name, ++ BlockdevCreateOptionsRbd **obj, Error **errp); ++ ++bool visit_type_BlockdevVmdkSubformat(Visitor *v, const char *name, ++ BlockdevVmdkSubformat *obj, Error **errp); ++ ++bool visit_type_BlockdevVmdkAdapterType(Visitor *v, const char *name, ++ BlockdevVmdkAdapterType *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVmdk_members(Visitor *v, BlockdevCreateOptionsVmdk *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVmdk(Visitor *v, const char *name, ++ BlockdevCreateOptionsVmdk **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsSsh_members(Visitor *v, BlockdevCreateOptionsSsh *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsSsh(Visitor *v, const char *name, ++ BlockdevCreateOptionsSsh **obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVdi_members(Visitor *v, BlockdevCreateOptionsVdi *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVdi(Visitor *v, const char *name, ++ BlockdevCreateOptionsVdi **obj, Error **errp); ++ ++bool visit_type_BlockdevVhdxSubformat(Visitor *v, const char *name, ++ BlockdevVhdxSubformat *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVhdx_members(Visitor *v, BlockdevCreateOptionsVhdx *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVhdx(Visitor *v, const char *name, ++ BlockdevCreateOptionsVhdx **obj, Error **errp); ++ ++bool visit_type_BlockdevVpcSubformat(Visitor *v, const char *name, ++ BlockdevVpcSubformat *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVpc_members(Visitor *v, BlockdevCreateOptionsVpc *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptionsVpc(Visitor *v, const char *name, ++ BlockdevCreateOptionsVpc **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevCreateOptions_base_members(Visitor *v, q_obj_BlockdevCreateOptions_base *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevCreateOptions(Visitor *v, const char *name, ++ BlockdevCreateOptions **obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_create_arg_members(Visitor *v, q_obj_blockdev_create_arg *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptionsLUKS_members(Visitor *v, BlockdevAmendOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptionsLUKS(Visitor *v, const char *name, ++ BlockdevAmendOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptionsQcow2_members(Visitor *v, BlockdevAmendOptionsQcow2 *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptionsQcow2(Visitor *v, const char *name, ++ BlockdevAmendOptionsQcow2 **obj, Error **errp); ++ ++bool visit_type_q_obj_BlockdevAmendOptions_base_members(Visitor *v, q_obj_BlockdevAmendOptions_base *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *obj, Error **errp); ++ ++bool visit_type_BlockdevAmendOptions(Visitor *v, const char *name, ++ BlockdevAmendOptions **obj, Error **errp); ++ ++bool visit_type_q_obj_x_blockdev_amend_arg_members(Visitor *v, q_obj_x_blockdev_amend_arg *obj, Error **errp); ++ ++bool visit_type_BlockErrorAction(Visitor *v, const char *name, ++ BlockErrorAction *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_IMAGE_CORRUPTED_arg_members(Visitor *v, q_obj_BLOCK_IMAGE_CORRUPTED_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_IO_ERROR_arg_members(Visitor *v, q_obj_BLOCK_IO_ERROR_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_COMPLETED_arg_members(Visitor *v, q_obj_BLOCK_JOB_COMPLETED_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_CANCELLED_arg_members(Visitor *v, q_obj_BLOCK_JOB_CANCELLED_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(Visitor *v, q_obj_BLOCK_JOB_ERROR_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_READY_arg_members(Visitor *v, q_obj_BLOCK_JOB_READY_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_JOB_PENDING_arg_members(Visitor *v, q_obj_BLOCK_JOB_PENDING_arg *obj, Error **errp); ++ ++bool visit_type_PreallocMode(Visitor *v, const char *name, ++ PreallocMode *obj, Error **errp); ++ ++bool visit_type_q_obj_BLOCK_WRITE_THRESHOLD_arg_members(Visitor *v, q_obj_BLOCK_WRITE_THRESHOLD_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_block_set_write_threshold_arg_members(Visitor *v, q_obj_block_set_write_threshold_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_x_blockdev_change_arg_members(Visitor *v, q_obj_x_blockdev_change_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_x_blockdev_set_iothread_arg_members(Visitor *v, q_obj_x_blockdev_set_iothread_arg *obj, Error **errp); ++ ++bool visit_type_QuorumOpType(Visitor *v, const char *name, ++ QuorumOpType *obj, Error **errp); ++ ++bool visit_type_q_obj_QUORUM_FAILURE_arg_members(Visitor *v, q_obj_QUORUM_FAILURE_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_QUORUM_REPORT_BAD_arg_members(Visitor *v, q_obj_QUORUM_REPORT_BAD_arg *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshotInternal_members(Visitor *v, BlockdevSnapshotInternal *obj, Error **errp); ++ ++bool visit_type_BlockdevSnapshotInternal(Visitor *v, const char *name, ++ BlockdevSnapshotInternal **obj, Error **errp); ++ ++bool visit_type_q_obj_blockdev_snapshot_delete_internal_sync_arg_members(Visitor *v, q_obj_blockdev_snapshot_delete_internal_sync_arg *obj, Error **errp); ++ ++bool visit_type_BlockGraphInfoList(Visitor *v, const char *name, ++ BlockGraphInfoList **obj, Error **errp); ++ ++bool visit_type_DummyBlockCoreForceArrays_members(Visitor *v, DummyBlockCoreForceArrays *obj, Error **errp); ++ ++bool visit_type_DummyBlockCoreForceArrays(Visitor *v, const char *name, ++ DummyBlockCoreForceArrays **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_BLOCK_CORE_H */ +diff --git a/include/qapi/qapi-visit-common.h b/include/qapi/qapi-visit-common.h +new file mode 100644 +index 00000000..f0e770d0 +--- /dev/null ++++ b/include/qapi/qapi-visit-common.h +@@ -0,0 +1,55 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_COMMON_H ++#define QAPI_VISIT_COMMON_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-common.h" ++ ++ ++bool visit_type_IoOperationType(Visitor *v, const char *name, ++ IoOperationType *obj, Error **errp); ++ ++bool visit_type_OnOffAuto(Visitor *v, const char *name, ++ OnOffAuto *obj, Error **errp); ++ ++bool visit_type_OnOffSplit(Visitor *v, const char *name, ++ OnOffSplit *obj, Error **errp); ++ ++bool visit_type_StrOrNull(Visitor *v, const char *name, ++ StrOrNull **obj, Error **errp); ++ ++bool visit_type_OffAutoPCIBAR(Visitor *v, const char *name, ++ OffAutoPCIBAR *obj, Error **errp); ++ ++bool visit_type_PCIELinkSpeed(Visitor *v, const char *name, ++ PCIELinkSpeed *obj, Error **errp); ++ ++bool visit_type_PCIELinkWidth(Visitor *v, const char *name, ++ PCIELinkWidth *obj, Error **errp); ++ ++bool visit_type_HostMemPolicy(Visitor *v, const char *name, ++ HostMemPolicy *obj, Error **errp); ++ ++bool visit_type_NetFilterDirection(Visitor *v, const char *name, ++ NetFilterDirection *obj, Error **errp); ++ ++bool visit_type_GrabToggleKeys(Visitor *v, const char *name, ++ GrabToggleKeys *obj, Error **errp); ++ ++bool visit_type_HumanReadableText_members(Visitor *v, HumanReadableText *obj, Error **errp); ++ ++bool visit_type_HumanReadableText(Visitor *v, const char *name, ++ HumanReadableText **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_COMMON_H */ +diff --git a/include/qapi/qapi-visit-crypto.h b/include/qapi/qapi-visit-crypto.h +new file mode 100644 +index 00000000..64b5404f +--- /dev/null ++++ b/include/qapi/qapi-visit-crypto.h +@@ -0,0 +1,165 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_CRYPTO_H ++#define QAPI_VISIT_CRYPTO_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-crypto.h" ++ ++ ++bool visit_type_QCryptoTLSCredsEndpoint(Visitor *v, const char *name, ++ QCryptoTLSCredsEndpoint *obj, Error **errp); ++ ++bool visit_type_QCryptoSecretFormat(Visitor *v, const char *name, ++ QCryptoSecretFormat *obj, Error **errp); ++ ++bool visit_type_QCryptoHashAlgorithm(Visitor *v, const char *name, ++ QCryptoHashAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoCipherAlgorithm(Visitor *v, const char *name, ++ QCryptoCipherAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoCipherMode(Visitor *v, const char *name, ++ QCryptoCipherMode *obj, Error **errp); ++ ++bool visit_type_QCryptoIVGenAlgorithm(Visitor *v, const char *name, ++ QCryptoIVGenAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockFormat(Visitor *v, const char *name, ++ QCryptoBlockFormat *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsBase_members(Visitor *v, QCryptoBlockOptionsBase *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsBase(Visitor *v, const char *name, ++ QCryptoBlockOptionsBase **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsQCow_members(Visitor *v, QCryptoBlockOptionsQCow *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsQCow(Visitor *v, const char *name, ++ QCryptoBlockOptionsQCow **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsLUKS_members(Visitor *v, QCryptoBlockOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockCreateOptionsLUKS_members(Visitor *v, QCryptoBlockCreateOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockCreateOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockCreateOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOpenOptions_members(Visitor *v, QCryptoBlockOpenOptions *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockOpenOptions(Visitor *v, const char *name, ++ QCryptoBlockOpenOptions **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockCreateOptions_members(Visitor *v, QCryptoBlockCreateOptions *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockCreateOptions(Visitor *v, const char *name, ++ QCryptoBlockCreateOptions **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoBase_members(Visitor *v, QCryptoBlockInfoBase *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoBase(Visitor *v, const char *name, ++ QCryptoBlockInfoBase **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKSSlot_members(Visitor *v, QCryptoBlockInfoLUKSSlot *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKSSlot(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKSSlot **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKSSlotList(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKSSlotList **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKS_members(Visitor *v, QCryptoBlockInfoLUKS *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfoLUKS(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKS **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfo_members(Visitor *v, QCryptoBlockInfo *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockInfo(Visitor *v, const char *name, ++ QCryptoBlockInfo **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockLUKSKeyslotState(Visitor *v, const char *name, ++ QCryptoBlockLUKSKeyslotState *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockAmendOptionsLUKS_members(Visitor *v, QCryptoBlockAmendOptionsLUKS *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockAmendOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockAmendOptionsLUKS **obj, Error **errp); ++ ++bool visit_type_QCryptoBlockAmendOptions_members(Visitor *v, QCryptoBlockAmendOptions *obj, Error **errp); ++ ++bool visit_type_QCryptoBlockAmendOptions(Visitor *v, const char *name, ++ QCryptoBlockAmendOptions **obj, Error **errp); ++ ++bool visit_type_SecretCommonProperties_members(Visitor *v, SecretCommonProperties *obj, Error **errp); ++ ++bool visit_type_SecretCommonProperties(Visitor *v, const char *name, ++ SecretCommonProperties **obj, Error **errp); ++ ++bool visit_type_SecretProperties_members(Visitor *v, SecretProperties *obj, Error **errp); ++ ++bool visit_type_SecretProperties(Visitor *v, const char *name, ++ SecretProperties **obj, Error **errp); ++ ++#if defined(CONFIG_SECRET_KEYRING) ++bool visit_type_SecretKeyringProperties_members(Visitor *v, SecretKeyringProperties *obj, Error **errp); ++ ++bool visit_type_SecretKeyringProperties(Visitor *v, const char *name, ++ SecretKeyringProperties **obj, Error **errp); ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++bool visit_type_TlsCredsProperties_members(Visitor *v, TlsCredsProperties *obj, Error **errp); ++ ++bool visit_type_TlsCredsProperties(Visitor *v, const char *name, ++ TlsCredsProperties **obj, Error **errp); ++ ++bool visit_type_TlsCredsAnonProperties_members(Visitor *v, TlsCredsAnonProperties *obj, Error **errp); ++ ++bool visit_type_TlsCredsAnonProperties(Visitor *v, const char *name, ++ TlsCredsAnonProperties **obj, Error **errp); ++ ++bool visit_type_TlsCredsPskProperties_members(Visitor *v, TlsCredsPskProperties *obj, Error **errp); ++ ++bool visit_type_TlsCredsPskProperties(Visitor *v, const char *name, ++ TlsCredsPskProperties **obj, Error **errp); ++ ++bool visit_type_TlsCredsX509Properties_members(Visitor *v, TlsCredsX509Properties *obj, Error **errp); ++ ++bool visit_type_TlsCredsX509Properties(Visitor *v, const char *name, ++ TlsCredsX509Properties **obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherAlgorithm(Visitor *v, const char *name, ++ QCryptoAkCipherAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherKeyType(Visitor *v, const char *name, ++ QCryptoAkCipherKeyType *obj, Error **errp); ++ ++bool visit_type_QCryptoRSAPaddingAlgorithm(Visitor *v, const char *name, ++ QCryptoRSAPaddingAlgorithm *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherOptionsRSA_members(Visitor *v, QCryptoAkCipherOptionsRSA *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherOptionsRSA(Visitor *v, const char *name, ++ QCryptoAkCipherOptionsRSA **obj, Error **errp); ++ ++bool visit_type_q_obj_QCryptoAkCipherOptions_base_members(Visitor *v, q_obj_QCryptoAkCipherOptions_base *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherOptions_members(Visitor *v, QCryptoAkCipherOptions *obj, Error **errp); ++ ++bool visit_type_QCryptoAkCipherOptions(Visitor *v, const char *name, ++ QCryptoAkCipherOptions **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_CRYPTO_H */ +diff --git a/include/qapi/qapi-visit-job.h b/include/qapi/qapi-visit-job.h +new file mode 100644 +index 00000000..5b5df705 +--- /dev/null ++++ b/include/qapi/qapi-visit-job.h +@@ -0,0 +1,51 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_JOB_H ++#define QAPI_VISIT_JOB_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-job.h" ++ ++ ++bool visit_type_JobType(Visitor *v, const char *name, ++ JobType *obj, Error **errp); ++ ++bool visit_type_JobStatus(Visitor *v, const char *name, ++ JobStatus *obj, Error **errp); ++ ++bool visit_type_JobVerb(Visitor *v, const char *name, ++ JobVerb *obj, Error **errp); ++ ++bool visit_type_q_obj_JOB_STATUS_CHANGE_arg_members(Visitor *v, q_obj_JOB_STATUS_CHANGE_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_pause_arg_members(Visitor *v, q_obj_job_pause_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_resume_arg_members(Visitor *v, q_obj_job_resume_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_cancel_arg_members(Visitor *v, q_obj_job_cancel_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_complete_arg_members(Visitor *v, q_obj_job_complete_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_dismiss_arg_members(Visitor *v, q_obj_job_dismiss_arg *obj, Error **errp); ++ ++bool visit_type_q_obj_job_finalize_arg_members(Visitor *v, q_obj_job_finalize_arg *obj, Error **errp); ++ ++bool visit_type_JobInfo_members(Visitor *v, JobInfo *obj, Error **errp); ++ ++bool visit_type_JobInfo(Visitor *v, const char *name, ++ JobInfo **obj, Error **errp); ++ ++bool visit_type_JobInfoList(Visitor *v, const char *name, ++ JobInfoList **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_JOB_H */ +diff --git a/include/qapi/qapi-visit-sockets.h b/include/qapi/qapi-visit-sockets.h +new file mode 100644 +index 00000000..5cec7a91 +--- /dev/null ++++ b/include/qapi/qapi-visit-sockets.h +@@ -0,0 +1,91 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_SOCKETS_H ++#define QAPI_VISIT_SOCKETS_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-sockets.h" ++ ++ ++bool visit_type_NetworkAddressFamily(Visitor *v, const char *name, ++ NetworkAddressFamily *obj, Error **errp); ++ ++bool visit_type_InetSocketAddressBase_members(Visitor *v, InetSocketAddressBase *obj, Error **errp); ++ ++bool visit_type_InetSocketAddressBase(Visitor *v, const char *name, ++ InetSocketAddressBase **obj, Error **errp); ++ ++bool visit_type_InetSocketAddress_members(Visitor *v, InetSocketAddress *obj, Error **errp); ++ ++bool visit_type_InetSocketAddress(Visitor *v, const char *name, ++ InetSocketAddress **obj, Error **errp); ++ ++bool visit_type_UnixSocketAddress_members(Visitor *v, UnixSocketAddress *obj, Error **errp); ++ ++bool visit_type_UnixSocketAddress(Visitor *v, const char *name, ++ UnixSocketAddress **obj, Error **errp); ++ ++bool visit_type_VsockSocketAddress_members(Visitor *v, VsockSocketAddress *obj, Error **errp); ++ ++bool visit_type_VsockSocketAddress(Visitor *v, const char *name, ++ VsockSocketAddress **obj, Error **errp); ++ ++bool visit_type_FdSocketAddress_members(Visitor *v, FdSocketAddress *obj, Error **errp); ++ ++bool visit_type_FdSocketAddress(Visitor *v, const char *name, ++ FdSocketAddress **obj, Error **errp); ++ ++bool visit_type_InetSocketAddressWrapper_members(Visitor *v, InetSocketAddressWrapper *obj, Error **errp); ++ ++bool visit_type_InetSocketAddressWrapper(Visitor *v, const char *name, ++ InetSocketAddressWrapper **obj, Error **errp); ++ ++bool visit_type_UnixSocketAddressWrapper_members(Visitor *v, UnixSocketAddressWrapper *obj, Error **errp); ++ ++bool visit_type_UnixSocketAddressWrapper(Visitor *v, const char *name, ++ UnixSocketAddressWrapper **obj, Error **errp); ++ ++bool visit_type_VsockSocketAddressWrapper_members(Visitor *v, VsockSocketAddressWrapper *obj, Error **errp); ++ ++bool visit_type_VsockSocketAddressWrapper(Visitor *v, const char *name, ++ VsockSocketAddressWrapper **obj, Error **errp); ++ ++bool visit_type_FdSocketAddressWrapper_members(Visitor *v, FdSocketAddressWrapper *obj, Error **errp); ++ ++bool visit_type_FdSocketAddressWrapper(Visitor *v, const char *name, ++ FdSocketAddressWrapper **obj, Error **errp); ++ ++bool visit_type_q_obj_SocketAddressLegacy_base_members(Visitor *v, q_obj_SocketAddressLegacy_base *obj, Error **errp); ++ ++bool visit_type_SocketAddressLegacy_members(Visitor *v, SocketAddressLegacy *obj, Error **errp); ++ ++bool visit_type_SocketAddressLegacy(Visitor *v, const char *name, ++ SocketAddressLegacy **obj, Error **errp); ++ ++bool visit_type_SocketAddressType(Visitor *v, const char *name, ++ SocketAddressType *obj, Error **errp); ++ ++bool visit_type_q_obj_SocketAddress_base_members(Visitor *v, q_obj_SocketAddress_base *obj, Error **errp); ++ ++bool visit_type_SocketAddress_members(Visitor *v, SocketAddress *obj, Error **errp); ++ ++bool visit_type_SocketAddress(Visitor *v, const char *name, ++ SocketAddress **obj, Error **errp); ++ ++bool visit_type_SocketAddressList(Visitor *v, const char *name, ++ SocketAddressList **obj, Error **errp); ++ ++bool visit_type_InetSocketAddressBaseList(Visitor *v, const char *name, ++ InetSocketAddressBaseList **obj, Error **errp); ++ ++#endif /* QAPI_VISIT_SOCKETS_H */ +diff --git a/include/qapi/qapi-visit-yank.h b/include/qapi/qapi-visit-yank.h +new file mode 100644 +index 00000000..3397ee66 +--- /dev/null ++++ b/include/qapi/qapi-visit-yank.h +@@ -0,0 +1,45 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_VISIT_YANK_H ++#define QAPI_VISIT_YANK_H ++ ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi-types-yank.h" ++ ++ ++bool visit_type_YankInstanceType(Visitor *v, const char *name, ++ YankInstanceType *obj, Error **errp); ++ ++bool visit_type_YankInstanceBlockNode_members(Visitor *v, YankInstanceBlockNode *obj, Error **errp); ++ ++bool visit_type_YankInstanceBlockNode(Visitor *v, const char *name, ++ YankInstanceBlockNode **obj, Error **errp); ++ ++bool visit_type_YankInstanceChardev_members(Visitor *v, YankInstanceChardev *obj, Error **errp); ++ ++bool visit_type_YankInstanceChardev(Visitor *v, const char *name, ++ YankInstanceChardev **obj, Error **errp); ++ ++bool visit_type_q_obj_YankInstance_base_members(Visitor *v, q_obj_YankInstance_base *obj, Error **errp); ++ ++bool visit_type_YankInstance_members(Visitor *v, YankInstance *obj, Error **errp); ++ ++bool visit_type_YankInstance(Visitor *v, const char *name, ++ YankInstance **obj, Error **errp); ++ ++bool visit_type_YankInstanceList(Visitor *v, const char *name, ++ YankInstanceList **obj, Error **errp); ++ ++bool visit_type_q_obj_yank_arg_members(Visitor *v, q_obj_yank_arg *obj, Error **errp); ++ ++#endif /* QAPI_VISIT_YANK_H */ +diff --git a/include/qapi/qmp-event.h b/include/qapi/qmp-event.h +new file mode 100644 +index 00000000..b60f1d3a +--- /dev/null ++++ b/include/qapi/qmp-event.h +@@ -0,0 +1,18 @@ ++/* ++ * QMP Event related ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * ++ * Authors: ++ * Wenchao Xia ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QMP_EVENT_H ++#define QMP_EVENT_H ++ ++QDict *qmp_event_build_dict(const char *event_name); ++#endif +diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h +new file mode 100644 +index 00000000..f2e95681 +--- /dev/null ++++ b/include/qapi/qmp/dispatch.h +@@ -0,0 +1,67 @@ ++/* ++ * Core Definitions for QAPI/QMP Dispatch ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_QMP_DISPATCH_H ++#define QAPI_QMP_DISPATCH_H ++ ++#include "monitor/monitor.h" ++#include "qemu/queue.h" ++ ++typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); ++ ++typedef enum QmpCommandOptions ++{ ++ QCO_NO_SUCCESS_RESP = (1U << 0), ++ QCO_ALLOW_OOB = (1U << 1), ++ QCO_ALLOW_PRECONFIG = (1U << 2), ++ QCO_COROUTINE = (1U << 3), ++} QmpCommandOptions; ++ ++typedef struct QmpCommand ++{ ++ const char *name; ++ /* Runs in coroutine context if QCO_COROUTINE is set */ ++ QmpCommandFunc *fn; ++ QmpCommandOptions options; ++ unsigned special_features; ++ QTAILQ_ENTRY(QmpCommand) node; ++ bool enabled; ++ const char *disable_reason; ++} QmpCommand; ++ ++typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; ++ ++void qmp_register_command(QmpCommandList *cmds, const char *name, ++ QmpCommandFunc *fn, QmpCommandOptions options, ++ unsigned special_features); ++const QmpCommand *qmp_find_command(const QmpCommandList *cmds, ++ const char *name); ++void qmp_disable_command(QmpCommandList *cmds, const char *name, ++ const char *err_msg); ++void qmp_enable_command(QmpCommandList *cmds, const char *name); ++ ++bool qmp_command_is_enabled(const QmpCommand *cmd); ++bool qmp_command_available(const QmpCommand *cmd, Error **errp); ++const char *qmp_command_name(const QmpCommand *cmd); ++bool qmp_has_success_response(const QmpCommand *cmd); ++QDict *qmp_error_response(Error *err); ++QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request, ++ bool allow_oob, Monitor *cur_mon); ++bool qmp_is_oob(const QDict *dict); ++ ++typedef void (*qmp_cmd_callback_fn)(const QmpCommand *cmd, void *opaque); ++ ++void qmp_for_each_command(const QmpCommandList *cmds, qmp_cmd_callback_fn fn, ++ void *opaque); ++ ++#endif +diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h +new file mode 100644 +index 00000000..7345a9bd +--- /dev/null ++++ b/include/qapi/qmp/json-parser.h +@@ -0,0 +1,46 @@ ++/* ++ * JSON Parser ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_QMP_JSON_PARSER_H ++#define QAPI_QMP_JSON_PARSER_H ++ ++typedef struct JSONLexer { ++ int start_state, state; ++ GString *token; ++ int x, y; ++} JSONLexer; ++ ++typedef struct JSONMessageParser { ++ void (*emit)(void *opaque, QObject *json, Error *err); ++ void *opaque; ++ va_list *ap; ++ JSONLexer lexer; ++ int brace_count; ++ int bracket_count; ++ GQueue tokens; ++ uint64_t token_size; ++} JSONMessageParser; ++ ++void json_message_parser_init(JSONMessageParser *parser, ++ void (*emit)(void *opaque, QObject *json, ++ Error *err), ++ void *opaque, va_list *ap); ++ ++void json_message_parser_feed(JSONMessageParser *parser, ++ const char *buffer, size_t size); ++ ++void json_message_parser_flush(JSONMessageParser *parser); ++ ++void json_message_parser_destroy(JSONMessageParser *parser); ++ ++#endif +diff --git a/include/qapi/qmp/json-writer.h b/include/qapi/qmp/json-writer.h +new file mode 100644 +index 00000000..b70ba640 +--- /dev/null ++++ b/include/qapi/qmp/json-writer.h +@@ -0,0 +1,35 @@ ++/* ++ * JSON Writer ++ * ++ * Copyright (c) 2020 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef JSON_WRITER_H ++#define JSON_WRITER_H ++ ++JSONWriter *json_writer_new(bool pretty); ++const char *json_writer_get(JSONWriter *); ++GString *json_writer_get_and_free(JSONWriter *); ++void json_writer_free(JSONWriter *); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(JSONWriter, json_writer_free) ++ ++void json_writer_start_object(JSONWriter *, const char *name); ++void json_writer_end_object(JSONWriter *); ++void json_writer_start_array(JSONWriter *, const char *name); ++void json_writer_end_array(JSONWriter *); ++void json_writer_bool(JSONWriter *, const char *name, bool val); ++void json_writer_null(JSONWriter *, const char *name); ++void json_writer_int64(JSONWriter *, const char *name, int64_t val); ++void json_writer_uint64(JSONWriter *, const char *name, uint64_t val); ++void json_writer_double(JSONWriter *, const char *name, double val); ++void json_writer_str(JSONWriter *, const char *name, const char *str); ++ ++#endif +diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h +new file mode 100644 +index 00000000..0d097269 +--- /dev/null ++++ b/include/qapi/qmp/qbool.h +@@ -0,0 +1,31 @@ ++/* ++ * QBool Module ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QBOOL_H ++#define QBOOL_H ++ ++#include "qapi/qmp/qobject.h" ++ ++struct QBool { ++ struct QObjectBase_ base; ++ bool value; ++}; ++ ++void qbool_unref(QBool *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QBool, qbool_unref) ++ ++QBool *qbool_from_bool(bool value); ++bool qbool_get_bool(const QBool *qb); ++ ++#endif /* QBOOL_H */ +diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h +new file mode 100644 +index 00000000..82e90fc0 +--- /dev/null ++++ b/include/qapi/qmp/qdict.h +@@ -0,0 +1,71 @@ ++/* ++ * QDict Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QDICT_H ++#define QDICT_H ++ ++#include "qapi/qmp/qobject.h" ++#include "qemu/queue.h" ++ ++#define QDICT_BUCKET_MAX 512 ++ ++typedef struct QDictEntry { ++ char *key; ++ QObject *value; ++ QLIST_ENTRY(QDictEntry) next; ++} QDictEntry; ++ ++struct QDict { ++ struct QObjectBase_ base; ++ size_t size; ++ QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; ++}; ++ ++void qdict_unref(QDict *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QDict, qdict_unref) ++ ++/* Object API */ ++QDict *qdict_new(void); ++const char *qdict_entry_key(const QDictEntry *entry); ++QObject *qdict_entry_value(const QDictEntry *entry); ++size_t qdict_size(const QDict *qdict); ++void qdict_put_obj(QDict *qdict, const char *key, QObject *value); ++void qdict_del(QDict *qdict, const char *key); ++int qdict_haskey(const QDict *qdict, const char *key); ++QObject *qdict_get(const QDict *qdict, const char *key); ++const QDictEntry *qdict_first(const QDict *qdict); ++const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry); ++ ++/* Helper to qdict_put_obj(), accepts any object */ ++#define qdict_put(qdict, key, obj) \ ++ qdict_put_obj(qdict, key, QOBJECT(obj)) ++ ++void qdict_put_bool(QDict *qdict, const char *key, bool value); ++void qdict_put_int(QDict *qdict, const char *key, int64_t value); ++void qdict_put_null(QDict *qdict, const char *key); ++void qdict_put_str(QDict *qdict, const char *key, const char *value); ++ ++double qdict_get_double(const QDict *qdict, const char *key); ++int64_t qdict_get_int(const QDict *qdict, const char *key); ++bool qdict_get_bool(const QDict *qdict, const char *key); ++QList *qdict_get_qlist(const QDict *qdict, const char *key); ++QDict *qdict_get_qdict(const QDict *qdict, const char *key); ++const char *qdict_get_str(const QDict *qdict, const char *key); ++int64_t qdict_get_try_int(const QDict *qdict, const char *key, ++ int64_t def_value); ++bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value); ++const char *qdict_get_try_str(const QDict *qdict, const char *key); ++ ++QDict *qdict_clone_shallow(const QDict *src); ++ ++#endif /* QDICT_H */ +diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h +new file mode 100644 +index 00000000..38e89762 +--- /dev/null ++++ b/include/qapi/qmp/qerror.h +@@ -0,0 +1,32 @@ ++/* ++ * QError Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++#ifndef QERROR_H ++#define QERROR_H ++ ++/* ++ * These macros will go away, please don't use in new code, and do not ++ * add new ones! ++ */ ++ ++#define QERR_INVALID_PARAMETER_VALUE \ ++ "Parameter '%s' expects %s" ++ ++#define QERR_MISSING_PARAMETER \ ++ "Parameter '%s' is missing" ++ ++#define QERR_PROPERTY_VALUE_OUT_OF_RANGE \ ++ "Property %s.%s doesn't take value %" PRId64 " (minimum: %" PRId64 ", maximum: %" PRId64 ")" ++ ++#define QERR_UNSUPPORTED \ ++ "this feature or command is not currently supported" ++ ++#endif /* QERROR_H */ +diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h +new file mode 100644 +index 00000000..7bd8d2de +--- /dev/null ++++ b/include/qapi/qmp/qjson.h +@@ -0,0 +1,31 @@ ++/* ++ * QObject JSON integration ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QJSON_H ++#define QJSON_H ++ ++QObject *qobject_from_json(const char *string, Error **errp); ++ ++QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap) ++ G_GNUC_PRINTF(1, 0); ++QObject *qobject_from_jsonf_nofail(const char *string, ...) ++ G_GNUC_PRINTF(1, 2); ++QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap) ++ G_GNUC_PRINTF(1, 0); ++QDict *qdict_from_jsonf_nofail(const char *string, ...) ++ G_GNUC_PRINTF(1, 2); ++ ++GString *qobject_to_json(const QObject *obj); ++GString *qobject_to_json_pretty(const QObject *obj, bool pretty); ++ ++#endif /* QJSON_H */ +diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h +new file mode 100644 +index 00000000..e4e985d4 +--- /dev/null ++++ b/include/qapi/qmp/qlist.h +@@ -0,0 +1,69 @@ ++/* ++ * QList Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QLIST_H ++#define QLIST_H ++ ++#include "qapi/qmp/qobject.h" ++#include "qemu/queue.h" ++ ++typedef struct QListEntry { ++ QObject *value; ++ QTAILQ_ENTRY(QListEntry) next; ++} QListEntry; ++ ++struct QList { ++ struct QObjectBase_ base; ++ QTAILQ_HEAD(,QListEntry) head; ++}; ++ ++void qlist_unref(QList *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QList, qlist_unref) ++ ++#define qlist_append(qlist, obj) \ ++ qlist_append_obj(qlist, QOBJECT(obj)) ++ ++void qlist_append_bool(QList *qlist, bool value); ++void qlist_append_int(QList *qlist, int64_t value); ++void qlist_append_null(QList *qlist); ++void qlist_append_str(QList *qlist, const char *value); ++ ++#define QLIST_FOREACH_ENTRY(qlist, var) \ ++ for ((var) = QTAILQ_FIRST(&(qlist)->head); \ ++ (var); \ ++ (var) = QTAILQ_NEXT((var), next)) ++ ++static inline QObject *qlist_entry_obj(const QListEntry *entry) ++{ ++ return entry->value; ++} ++ ++QList *qlist_new(void); ++QList *qlist_copy(QList *src); ++void qlist_append_obj(QList *qlist, QObject *obj); ++QObject *qlist_pop(QList *qlist); ++QObject *qlist_peek(QList *qlist); ++int qlist_empty(const QList *qlist); ++size_t qlist_size(const QList *qlist); ++ ++static inline const QListEntry *qlist_first(const QList *qlist) ++{ ++ return QTAILQ_FIRST(&qlist->head); ++} ++ ++static inline const QListEntry *qlist_next(const QListEntry *entry) ++{ ++ return QTAILQ_NEXT(entry, next); ++} ++ ++#endif /* QLIST_H */ +diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h +new file mode 100644 +index 00000000..7feb7c7d +--- /dev/null ++++ b/include/qapi/qmp/qnull.h +@@ -0,0 +1,33 @@ ++/* ++ * QNull ++ * ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QNULL_H ++#define QNULL_H ++ ++#include "qapi/qmp/qobject.h" ++ ++struct QNull { ++ struct QObjectBase_ base; ++}; ++ ++extern QNull qnull_; ++ ++static inline QNull *qnull(void) ++{ ++ return qobject_ref(&qnull_); ++} ++ ++void qnull_unref(QNull *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QNull, qnull_unref) ++ ++#endif /* QNULL_H */ +diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h +new file mode 100644 +index 00000000..e86788dd +--- /dev/null ++++ b/include/qapi/qmp/qnum.h +@@ -0,0 +1,75 @@ ++/* ++ * QNum Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * Anthony Liguori ++ * Marc-AndrĆ© Lureau ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QNUM_H ++#define QNUM_H ++ ++#include "qapi/qmp/qobject.h" ++ ++typedef enum { ++ QNUM_I64, ++ QNUM_U64, ++ QNUM_DOUBLE ++} QNumKind; ++ ++/* ++ * QNum encapsulates how our dialect of JSON fills in the blanks left ++ * by the JSON specification (RFC 8259) regarding numbers. ++ * ++ * Conceptually, we treat number as an abstract type with three ++ * concrete subtypes: floating-point, signed integer, unsigned ++ * integer. QNum implements this as a discriminated union of double, ++ * int64_t, uint64_t. ++ * ++ * The JSON parser picks the subtype as follows. If the number has a ++ * decimal point or an exponent, it is floating-point. Else if it ++ * fits into int64_t, it's signed integer. Else if it fits into ++ * uint64_t, it's unsigned integer. Else it's floating-point. ++ * ++ * Any number can serve as double: qnum_get_double() converts under ++ * the hood. ++ * ++ * An integer can serve as signed / unsigned integer as long as it is ++ * in range: qnum_get_try_int() / qnum_get_try_uint() check range and ++ * convert under the hood. ++ */ ++struct QNum { ++ struct QObjectBase_ base; ++ QNumKind kind; ++ union { ++ int64_t i64; ++ uint64_t u64; ++ double dbl; ++ } u; ++}; ++ ++void qnum_unref(QNum *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QNum, qnum_unref) ++ ++QNum *qnum_from_int(int64_t value); ++QNum *qnum_from_uint(uint64_t value); ++QNum *qnum_from_double(double value); ++ ++bool qnum_get_try_int(const QNum *qn, int64_t *val); ++int64_t qnum_get_int(const QNum *qn); ++ ++bool qnum_get_try_uint(const QNum *qn, uint64_t *val); ++uint64_t qnum_get_uint(const QNum *qn); ++ ++double qnum_get_double(QNum *qn); ++ ++char *qnum_to_string(QNum *qn); ++ ++#endif /* QNUM_H */ +diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h +new file mode 100644 +index 00000000..256d7826 +--- /dev/null ++++ b/include/qapi/qmp/qobject.h +@@ -0,0 +1,144 @@ ++/* ++ * QEMU Object Model. ++ * ++ * Based on ideas by Avi Kivity ++ * ++ * Copyright (C) 2009, 2015 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ * QObject Reference Counts Terminology ++ * ------------------------------------ ++ * ++ * - Returning references: A function that returns an object may ++ * return it as either a weak or a strong reference. If the ++ * reference is strong, you are responsible for calling ++ * qobject_unref() on the reference when you are done. ++ * ++ * If the reference is weak, the owner of the reference may free it at ++ * any time in the future. Before storing the reference anywhere, you ++ * should call qobject_ref() to make the reference strong. ++ * ++ * - Transferring ownership: when you transfer ownership of a reference ++ * by calling a function, you are no longer responsible for calling ++ * qobject_unref() when the reference is no longer needed. In other words, ++ * when the function returns you must behave as if the reference to the ++ * passed object was weak. ++ */ ++#ifndef QOBJECT_H ++#define QOBJECT_H ++ ++#include "qapi/qapi-builtin-types.h" ++ ++/* Not for use outside include/qapi/qmp/ */ ++struct QObjectBase_ { ++ QType type; ++ size_t refcnt; ++}; ++ ++/* this struct must have no other members than base */ ++struct QObject { ++ struct QObjectBase_ base; ++}; ++ ++/* ++ * Preprocessor sorcery ahead: use a different identifier for the ++ * local variable in each expansion, so we can nest macro calls ++ * without shadowing variables. ++ */ ++#define QOBJECT_INTERNAL(obj, _obj) ({ \ ++ typeof(obj) _obj = (obj); \ ++ _obj ? container_of(&_obj->base, QObject, base) : NULL; \ ++}) ++#define QOBJECT(obj) QOBJECT_INTERNAL((obj), MAKE_IDENTIFIER(_obj)) ++ ++/* Required for qobject_to() */ ++#define QTYPE_CAST_TO_QNull QTYPE_QNULL ++#define QTYPE_CAST_TO_QNum QTYPE_QNUM ++#define QTYPE_CAST_TO_QString QTYPE_QSTRING ++#define QTYPE_CAST_TO_QDict QTYPE_QDICT ++#define QTYPE_CAST_TO_QList QTYPE_QLIST ++#define QTYPE_CAST_TO_QBool QTYPE_QBOOL ++ ++QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7, ++ "The QTYPE_CAST_TO_* list needs to be extended"); ++ ++#define qobject_to(type, obj) \ ++ ((type *)qobject_check_type(obj, glue(QTYPE_CAST_TO_, type))) ++ ++static inline void qobject_ref_impl(QObject *obj) ++{ ++ if (obj) { ++ obj->base.refcnt++; ++ } ++} ++ ++/** ++ * qobject_is_equal(): Return whether the two objects are equal. ++ * ++ * Any of the pointers may be NULL; return true if both are. Always ++ * return false if only one is (therefore a QNull object is not ++ * considered equal to a NULL pointer). ++ */ ++bool qobject_is_equal(const QObject *x, const QObject *y); ++ ++/** ++ * qobject_destroy(): Free resources used by the object ++ * For use via qobject_unref() only! ++ */ ++void qobject_destroy(QObject *obj); ++ ++static inline void qobject_unref_impl(QObject *obj) ++{ ++ assert(!obj || obj->base.refcnt); ++ if (obj && --obj->base.refcnt == 0) { ++ qobject_destroy(obj); ++ } ++} ++ ++/** ++ * qobject_ref(): Increment QObject's reference count ++ * ++ * Returns: the same @obj. The type of @obj will be propagated to the ++ * return type. ++ */ ++#define qobject_ref(obj) ({ \ ++ typeof(obj) _o = (obj); \ ++ qobject_ref_impl(QOBJECT(_o)); \ ++ _o; \ ++}) ++ ++/** ++ * qobject_unref(): Decrement QObject's reference count, deallocate ++ * when it reaches zero ++ */ ++#define qobject_unref(obj) qobject_unref_impl(QOBJECT(obj)) ++ ++/** ++ * qobject_type(): Return the QObject's type ++ */ ++static inline QType qobject_type(const QObject *obj) ++{ ++ assert(QTYPE_NONE < obj->base.type && obj->base.type < QTYPE__MAX); ++ return obj->base.type; ++} ++ ++/** ++ * qobject_check_type(): Helper function for the qobject_to() macro. ++ * Return @obj, but only if @obj is not NULL and @type is equal to ++ * @obj's type. Return NULL otherwise. ++ */ ++static inline QObject *qobject_check_type(const QObject *obj, QType type) ++{ ++ if (obj && qobject_type(obj) == type) { ++ return (QObject *)obj; ++ } else { ++ return NULL; ++ } ++} ++ ++#endif /* QOBJECT_H */ +diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h +new file mode 100644 +index 00000000..318d815d +--- /dev/null ++++ b/include/qapi/qmp/qstring.h +@@ -0,0 +1,33 @@ ++/* ++ * QString Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QSTRING_H ++#define QSTRING_H ++ ++#include "qapi/qmp/qobject.h" ++ ++struct QString { ++ struct QObjectBase_ base; ++ const char *string; ++}; ++ ++void qstring_unref(QString *q); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QString, qstring_unref) ++ ++QString *qstring_new(void); ++QString *qstring_from_str(const char *str); ++QString *qstring_from_substr(const char *str, size_t start, size_t end); ++QString *qstring_from_gstring(GString *gstr); ++const char *qstring_get_str(const QString *qstring); ++ ++#endif /* QSTRING_H */ +diff --git a/include/qapi/qobject-input-visitor.h b/include/qapi/qobject-input-visitor.h +new file mode 100644 +index 00000000..95985e25 +--- /dev/null ++++ b/include/qapi/qobject-input-visitor.h +@@ -0,0 +1,82 @@ ++/* ++ * Input Visitor ++ * ++ * Copyright (C) 2017 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QOBJECT_INPUT_VISITOR_H ++#define QOBJECT_INPUT_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct QObjectInputVisitor QObjectInputVisitor; ++ ++/* ++ * Create a QObject input visitor for @obj ++ * ++ * A QObject input visitor visit builds a QAPI object from a QObject. ++ * This simultaneously walks the QAPI object being built and the ++ * QObject. The latter walk starts at @obj. ++ * ++ * visit_type_FOO() creates an instance of QAPI type FOO. The visited ++ * QObject must match FOO. QDict matches struct/union types, QList ++ * matches list types, QString matches type 'str' and enumeration ++ * types, QNum matches integer and float types, QBool matches type ++ * 'bool'. Type 'any' is matched by QObject. A QAPI alternate type ++ * is matched when one of its member types is. ++ * ++ * visit_start_struct() ... visit_end_struct() visits a QDict and ++ * creates a QAPI struct/union. Visits in between visit the ++ * dictionary members. visit_optional() is true when the QDict has ++ * this member. visit_check_struct() fails if unvisited members ++ * remain. ++ * ++ * visit_start_list() ... visit_end_list() visits a QList and creates ++ * a QAPI list. Visits in between visit list members, one after the ++ * other. visit_next_list() returns NULL when all QList members have ++ * been visited. visit_check_list() fails if unvisited members ++ * remain. ++ * ++ * visit_start_alternate() ... visit_end_alternate() visits a QObject ++ * and creates a QAPI alternate. The visit in between visits the same ++ * QObject and initializes the alternate member that is in use. ++ * ++ * Error messages refer to parts of @obj in JavaScript/Python syntax. ++ * For example, 'a.b[2]' refers to the second member of the QList ++ * member 'b' of the QDict member 'a' of QDict @obj. ++ * ++ * The caller is responsible for freeing the visitor with ++ * visit_free(). ++ */ ++Visitor *qobject_input_visitor_new(QObject *obj); ++ ++/* ++ * Create a QObject input visitor for @obj for use with keyval_parse() ++ * ++ * This is like qobject_input_visitor_new(), except scalars are all ++ * QString, and error messages refer to parts of @obj in the syntax ++ * keyval_parse() uses for KEYs. ++ */ ++Visitor *qobject_input_visitor_new_keyval(QObject *obj); ++ ++/* ++ * Create a QObject input visitor for parsing @str. ++ * ++ * If @str looks like JSON, parse it as JSON, else as KEY=VALUE,... ++ * @implied_key applies to KEY=VALUE, and works as in keyval_parse(). ++ * On failure, store an error through @errp and return NULL. ++ * On success, return a new QObject input visitor for the parse. ++ */ ++Visitor *qobject_input_visitor_new_str(const char *str, ++ const char *implied_key, ++ Error **errp); ++ ++#endif +diff --git a/include/qapi/qobject-output-visitor.h b/include/qapi/qobject-output-visitor.h +new file mode 100644 +index 00000000..2b1726ba +--- /dev/null ++++ b/include/qapi/qobject-output-visitor.h +@@ -0,0 +1,56 @@ ++/* ++ * Output Visitor ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QOBJECT_OUTPUT_VISITOR_H ++#define QOBJECT_OUTPUT_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct QObjectOutputVisitor QObjectOutputVisitor; ++ ++/** ++ * Create a QObject output visitor for @obj ++ * ++ * A QObject output visitor visit builds a QObject from QAPI Object. ++ * This simultaneously walks the QAPI object and the QObject being ++ * built. The latter walk starts at @obj. ++ * ++ * visit_type_FOO() creates a QObject for QAPI type FOO. It creates a ++ * QDict for struct/union types, a QList for list types, QString for ++ * type 'str' and enumeration types, QNum for integer and float ++ * types, QBool for type 'bool'. For type 'any', it increments the ++ * QObject's reference count. For QAPI alternate types, it creates ++ * the QObject for the member that is in use. ++ * ++ * visit_start_struct() ... visit_end_struct() visits a QAPI ++ * struct/union and creates a QDict. Visits in between visit the ++ * members. visit_optional() is true when the struct/union has this ++ * member. visit_check_struct() does nothing. ++ * ++ * visit_start_list() ... visit_end_list() visits a QAPI list and ++ * creates a QList. Visits in between visit list members, one after ++ * the other. visit_next_list() returns NULL when all QAPI list ++ * members have been visited. visit_check_list() does nothing. ++ * ++ * visit_start_alternate() ... visit_end_alternate() visits a QAPI ++ * alternate. The visit in between creates the QObject for the ++ * alternate member that is in use. ++ * ++ * Errors are not expected to happen. ++ * ++ * The caller is responsible for freeing the visitor with ++ * visit_free(). ++ */ ++Visitor *qobject_output_visitor_new(QObject **result); ++ ++#endif +diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h +new file mode 100644 +index 00000000..921f3875 +--- /dev/null ++++ b/include/qapi/string-input-visitor.h +@@ -0,0 +1,27 @@ ++/* ++ * String parsing Visitor ++ * ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef STRING_INPUT_VISITOR_H ++#define STRING_INPUT_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct StringInputVisitor StringInputVisitor; ++ ++/* ++ * The string input visitor does not implement support for visiting ++ * QAPI structs, alternates, null, or arbitrary QTypes. Only flat lists ++ * of integers (except type "size") are supported. ++ */ ++Visitor *string_input_visitor_new(const char *str); ++ ++#endif +diff --git a/include/qapi/string-output-visitor.h b/include/qapi/string-output-visitor.h +new file mode 100644 +index 00000000..b1ee473b +--- /dev/null ++++ b/include/qapi/string-output-visitor.h +@@ -0,0 +1,35 @@ ++/* ++ * String printing Visitor ++ * ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef STRING_OUTPUT_VISITOR_H ++#define STRING_OUTPUT_VISITOR_H ++ ++#include "qapi/visitor.h" ++ ++typedef struct StringOutputVisitor StringOutputVisitor; ++ ++/* ++ * Create a new string output visitor. ++ * ++ * Using @human creates output that is a bit easier for humans to read ++ * (for example, showing integer values in both decimal and hex). ++ * ++ * If everything else succeeds, pass @result to visit_complete() to ++ * collect the result of the visit. ++ * ++ * The string output visitor does not implement support for alternates, null, ++ * or arbitrary QTypes. Struct fields are not shown. It also requires a ++ * non-null list argument to visit_start_list(). ++ */ ++Visitor *string_output_visitor_new(bool human, char **result); ++ ++#endif +diff --git a/include/qapi/util.h b/include/qapi/util.h +new file mode 100644 +index 00000000..b8254247 +--- /dev/null ++++ b/include/qapi/util.h +@@ -0,0 +1,72 @@ ++/* ++ * QAPI util functions ++ * ++ * Copyright Fujitsu, Inc. 2014 ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_UTIL_H ++#define QAPI_UTIL_H ++ ++typedef enum { ++ QAPI_DEPRECATED, ++ QAPI_UNSTABLE, ++} QapiSpecialFeature; ++ ++typedef struct QEnumLookup { ++ const char *const *array; ++ const unsigned char *const special_features; ++ const int size; ++} QEnumLookup; ++ ++const char *qapi_enum_lookup(const QEnumLookup *lookup, int val); ++int qapi_enum_parse(const QEnumLookup *lookup, const char *buf, ++ int def, Error **errp); ++bool qapi_bool_parse(const char *name, const char *value, bool *obj, ++ Error **errp); ++ ++int parse_qapi_name(const char *name, bool complete); ++ ++/* ++ * For any GenericList @list, insert @element at the front. ++ * ++ * Note that this macro evaluates @element exactly once, so it is safe ++ * to have side-effects with that argument. ++ */ ++#define QAPI_LIST_PREPEND(list, element) do { \ ++ typeof(list) _tmp = g_malloc(sizeof(*(list))); \ ++ _tmp->value = (element); \ ++ _tmp->next = (list); \ ++ (list) = _tmp; \ ++} while (0) ++ ++/* ++ * For any pointer to a GenericList @tail (usually the 'next' member of a ++ * list element), insert @element at the back and update the tail. ++ * ++ * Note that this macro evaluates @element exactly once, so it is safe ++ * to have side-effects with that argument. ++ */ ++#define QAPI_LIST_APPEND(tail, element) do { \ ++ *(tail) = g_malloc0(sizeof(**(tail))); \ ++ (*(tail))->value = (element); \ ++ (tail) = &(*(tail))->next; \ ++} while (0) ++ ++/* ++ * For any GenericList @list, return its length. ++ */ ++#define QAPI_LIST_LENGTH(list) \ ++ ({ \ ++ size_t _len = 0; \ ++ typeof_strip_qual(list) _tail; \ ++ for (_tail = list; _tail != NULL; _tail = _tail->next) { \ ++ _len++; \ ++ } \ ++ _len; \ ++ }) ++ ++#endif +diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h +new file mode 100644 +index 00000000..2badec5b +--- /dev/null ++++ b/include/qapi/visitor-impl.h +@@ -0,0 +1,137 @@ ++/* ++ * Core Definitions for QAPI Visitor implementations ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * ++ * Author: Paolo Bonizni ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++#ifndef QAPI_VISITOR_IMPL_H ++#define QAPI_VISITOR_IMPL_H ++ ++#include "qapi/visitor.h" ++ ++/* ++ * This file describes the callback interface for implementing a QAPI ++ * visitor. For the client interface, see visitor.h. When ++ * implementing the callbacks, it is easiest to declare a struct with ++ * 'Visitor visitor;' as the first member. A callback's contract ++ * matches the corresponding public functions' contract unless stated ++ * otherwise. In the comments below, some callbacks are marked "must ++ * be set for $TYPE visits to work"; if a visitor implementation omits ++ * that callback, it should also document that it is only useful for a ++ * subset of QAPI. ++ */ ++ ++/* ++ * There are four classes of visitors; setting the class determines ++ * how QAPI enums are visited, as well as what additional restrictions ++ * can be asserted. The values are intentionally chosen so as to ++ * permit some assertions based on whether a given bit is set (that ++ * is, some assertions apply to input and clone visitors, some ++ * assertions apply to output and clone visitors). ++ */ ++typedef enum VisitorType { ++ VISITOR_INPUT = 1, ++ VISITOR_OUTPUT = 2, ++ VISITOR_CLONE = 3, ++ VISITOR_DEALLOC = 4, ++} VisitorType; ++ ++struct Visitor ++{ ++ /* ++ * Only input visitors may fail! ++ */ ++ ++ /* Must be set to visit structs */ ++ bool (*start_struct)(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp); ++ ++ /* Optional; intended for input visitors */ ++ bool (*check_struct)(Visitor *v, Error **errp); ++ ++ /* Must be set to visit structs */ ++ void (*end_struct)(Visitor *v, void **obj); ++ ++ /* Must be set; implementations may require @list to be non-null, ++ * but must document it. */ ++ bool (*start_list)(Visitor *v, const char *name, GenericList **list, ++ size_t size, Error **errp); ++ ++ /* Must be set */ ++ GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size); ++ ++ /* Optional; intended for input visitors */ ++ bool (*check_list)(Visitor *v, Error **errp); ++ ++ /* Must be set */ ++ void (*end_list)(Visitor *v, void **list); ++ ++ /* Must be set by input and clone visitors to visit alternates */ ++ bool (*start_alternate)(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp); ++ ++ /* Optional */ ++ void (*end_alternate)(Visitor *v, void **obj); ++ ++ /* Must be set */ ++ bool (*type_int64)(Visitor *v, const char *name, int64_t *obj, ++ Error **errp); ++ ++ /* Must be set */ ++ bool (*type_uint64)(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp); ++ ++ /* Optional; fallback is type_uint64() */ ++ bool (*type_size)(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp); ++ ++ /* Must be set */ ++ bool (*type_bool)(Visitor *v, const char *name, bool *obj, Error **errp); ++ ++ /* Must be set */ ++ bool (*type_str)(Visitor *v, const char *name, char **obj, Error **errp); ++ ++ /* Must be set to visit numbers */ ++ bool (*type_number)(Visitor *v, const char *name, double *obj, ++ Error **errp); ++ ++ /* Must be set to visit arbitrary QTypes */ ++ bool (*type_any)(Visitor *v, const char *name, QObject **obj, ++ Error **errp); ++ ++ /* Must be set to visit explicit null values. */ ++ bool (*type_null)(Visitor *v, const char *name, QNull **obj, ++ Error **errp); ++ ++ /* Must be set for input visitors to visit structs, optional otherwise. ++ The core takes care of the return type in the public interface. */ ++ void (*optional)(Visitor *v, const char *name, bool *present); ++ ++ /* Optional */ ++ bool (*policy_reject)(Visitor *v, const char *name, ++ unsigned special_features, Error **errp); ++ ++ /* Optional */ ++ bool (*policy_skip)(Visitor *v, const char *name, ++ unsigned special_features); ++ ++ /* Must be set */ ++ VisitorType type; ++ ++ /* Optional */ ++ struct CompatPolicy compat_policy; ++ ++ /* Must be set for output visitors, optional otherwise. */ ++ void (*complete)(Visitor *v, void *opaque); ++ ++ /* Must be set */ ++ void (*free)(Visitor *v); ++}; ++ ++#endif +diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h +new file mode 100644 +index 00000000..27b85d47 +--- /dev/null ++++ b/include/qapi/visitor.h +@@ -0,0 +1,713 @@ ++/* ++ * Core Definitions for QAPI Visitor Classes ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QAPI_VISITOR_H ++#define QAPI_VISITOR_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qapi/qapi-types-compat.h" ++ ++/* ++ * The QAPI schema defines both a set of C data types, and a QMP wire ++ * format. QAPI objects can contain references to other QAPI objects, ++ * resulting in a directed acyclic graph. QAPI also generates visitor ++ * functions to walk these graphs. This file represents the interface ++ * for doing work at each node of a QAPI graph; it can also be used ++ * for a virtual walk, where there is no actual QAPI C struct. ++ * ++ * There are four kinds of visitors: input visitors (QObject, string, ++ * and QemuOpts) parse an external representation and build the ++ * corresponding QAPI object, output visitors (QObject and string) ++ * take a QAPI object and generate an external representation, the ++ * dealloc visitor takes a QAPI object (possibly partially ++ * constructed) and recursively frees it, and the clone visitor ++ * performs a deep clone of a QAPI object. ++ * ++ * While the dealloc and QObject input/output visitors are general, ++ * the string, QemuOpts, and clone visitors have some implementation ++ * limitations; see the documentation for each visitor for more ++ * details on what it supports. Also, see visitor-impl.h for the ++ * callback contracts implemented by each visitor, and ++ * docs/devel/qapi-code-gen.rst for more about the QAPI code ++ * generator. ++ * ++ * All of the visitors are created via: ++ * ++ * Visitor *subtype_visitor_new(parameters...); ++ * ++ * A visitor should be used for exactly one top-level visit_type_FOO() ++ * or virtual walk; if that is successful, the caller can optionally ++ * call visit_complete() (useful only for output visits, but safe to ++ * call on all visits). Then, regardless of success or failure, the ++ * user should call visit_free() to clean up resources. It is okay to ++ * free the visitor without completing the visit, if some other error ++ * is detected in the meantime. ++ * ++ * The clone and dealloc visitor should not be used directly outside ++ * of QAPI code. Use the qapi_free_FOO() and QAPI_CLONE() instead, ++ * described below. ++ * ++ * All QAPI types have a corresponding function with a signature ++ * roughly compatible with this: ++ * ++ * bool visit_type_FOO(Visitor *v, const char *name, T obj, Error **errp); ++ * ++ * where T is FOO for scalar types, and FOO * otherwise. The scalar ++ * visitors are declared here; the remaining visitors are generated in ++ * qapi-visit-MODULE.h. ++ * ++ * The @name parameter of visit_type_FOO() describes the relation ++ * between this QAPI value and its parent container. When visiting ++ * the root of a tree, @name is ignored; when visiting a member of an ++ * object, @name is the key associated with the value; when visiting a ++ * member of a list, @name is NULL; and when visiting the member of an ++ * alternate, @name should equal the name used for visiting the ++ * alternate. ++ * ++ * The visit_type_FOO() functions take a non-null @obj argument; they ++ * allocate *@obj during input visits, leave it unchanged during ++ * output and clone visits, and free it (recursively) during a dealloc ++ * visit. ++ * ++ * Each function also takes the customary @errp argument (see ++ * qapi/error.h for details), for reporting any errors (such as if a ++ * member @name is not present, or is present but not the specified ++ * type). Only input visitors can fail. ++ * ++ * If an error is detected during visit_type_FOO() with an input ++ * visitor, then *@obj will be set to NULL for pointer types, and left ++ * unchanged for scalar types. ++ * ++ * Using an output or clone visitor with an incomplete object has ++ * undefined behavior (other than a special case for visit_type_str() ++ * treating NULL like ""), while the dealloc visitor safely handles ++ * incomplete objects. Since input visitors never produce an ++ * incomplete object, such an object is possible only by manual ++ * construction. ++ * ++ * visit_type_FOO() returns true on success, false on error. ++ * ++ * For the QAPI object types (structs, unions, and alternates), there ++ * is an additional generated function in qapi-visit-MODULE.h ++ * compatible with: ++ * ++ * bool visit_type_FOO_members(Visitor *v, FOO *obj, Error **errp); ++ * ++ * for visiting the members of a type without also allocating the QAPI ++ * struct. It also returns true on success, false on error. ++ * ++ * Additionally, QAPI pointer types (structs, unions, alternates, and ++ * lists) have a generated function in qapi-types-MODULE.h compatible ++ * with: ++ * ++ * void qapi_free_FOO(FOO *obj); ++ * ++ * Does nothing when @obj is NULL. ++ * ++ * Such objects may also be used with macro ++ * ++ * Type *QAPI_CLONE(Type, src); ++ * ++ * in order to perform a deep clone of @src. ++ * ++ * For QAPI types can that inherit from a base type, a function is ++ * generated for going from the derived type to the base type: ++ * ++ * BASE *qapi_CHILD_base(CHILD *obj); ++ * ++ * Typical input visitor usage involves: ++ * ++ * ++ * Foo *f; ++ * Error *err = NULL; ++ * Visitor *v; ++ * ++ * v = FOO_visitor_new(...); ++ * if (!visit_type_Foo(v, NULL, &f, &err)) { ++ * ...handle error... ++ * } else { ++ * ...use f... ++ * } ++ * visit_free(v); ++ * qapi_free_Foo(f); ++ * ++ * ++ * For a list, it is: ++ * ++ * FooList *l; ++ * Error *err = NULL; ++ * Visitor *v; ++ * ++ * v = FOO_visitor_new(...); ++ * if (!visit_type_FooList(v, NULL, &l, &err)) { ++ * ...handle error... ++ * } else { ++ * for ( ; l; l = l->next) { ++ * ...use l->value... ++ * } ++ * } ++ * visit_free(v); ++ * qapi_free_FooList(l); ++ * ++ * ++ * Typical output visitor usage: ++ * ++ * ++ * Foo *f = ...obtain populated object... ++ * Visitor *v; ++ * Type *result; ++ * ++ * v = FOO_visitor_new(..., &result); ++ * visit_type_Foo(v, NULL, &f, &error_abort); ++ * visit_complete(v, &result); ++ * visit_free(v); ++ * ...use result... ++ * ++ * ++ * It is also possible to use the visitors to do a virtual walk, where ++ * no actual QAPI object is present. In this situation, decisions ++ * about what needs to be walked are made by the calling code, and ++ * structured visits are split between pairs of start and end methods ++ * (where the end method must be called if the start function ++ * succeeded, even if an intermediate visit encounters an error). ++ * Thus, a virtual walk corresponding to '{ "list": [1, 2] }' looks ++ * like: ++ * ++ * ++ * Visitor *v; ++ * Error *err = NULL; ++ * bool ok = false; ++ * int value; ++ * ++ * v = FOO_visitor_new(...); ++ * if (!visit_start_struct(v, NULL, NULL, 0, &err)) { ++ * goto out; ++ * } ++ * if (!visit_start_list(v, "list", NULL, 0, &err)) { ++ * goto outobj; ++ * } ++ * value = 1; ++ * if (!visit_type_int(v, NULL, &value, &err)) { ++ * goto outlist; ++ * } ++ * value = 2; ++ * if (!visit_type_int(v, NULL, &value, &err)) { ++ * goto outlist; ++ * } ++ * ok = true; ++ * outlist: ++ * if (ok) { ++ * ok = visit_check_list(v, &err); ++ * } ++ * visit_end_list(v, NULL); ++ * if (ok) { ++ * ok = visit_check_struct(v, &err); ++ * } ++ * outobj: ++ * visit_end_struct(v, NULL); ++ * out: ++ * visit_free(v); ++ * ++ * ++ * This file provides helpers for use by the generated ++ * visit_type_FOO(): visit_optional() for the 'has_member' field ++ * associated with optional 'member' in the C struct, ++ * visit_next_list() for advancing through a FooList linked list, and ++ * visit_is_input() for cleaning up on failure. ++ */ ++ ++/*** Useful types ***/ ++ ++/* This struct is layout-compatible with all other *List structs ++ * created by the QAPI generator. It is used as a typical ++ * singly-linked list. */ ++typedef struct GenericList { ++ struct GenericList *next; ++ char padding[]; ++} GenericList; ++ ++/* This struct is layout-compatible with all Alternate types ++ * created by the QAPI generator. */ ++typedef struct GenericAlternate { ++ QType type; ++ char padding[]; ++} GenericAlternate; ++ ++/*** Visitor cleanup ***/ ++ ++/* ++ * Complete the visit, collecting any output. ++ * ++ * May only be called only once after a successful top-level ++ * visit_type_FOO() or visit_end_ITEM(), and marks the end of the ++ * visit. The @opaque pointer should match the output parameter ++ * passed to the subtype_visitor_new() used to create an output ++ * visitor, or NULL for any other visitor. Needed for output ++ * visitors, but may also be called with other visitors. ++ */ ++void visit_complete(Visitor *v, void *opaque); ++ ++/* ++ * Free @v and any resources it has tied up. ++ * ++ * May be called whether or not the visit has been successfully ++ * completed, but should not be called until a top-level ++ * visit_type_FOO() or visit_start_ITEM() has been performed on the ++ * visitor. Safe if @v is NULL. ++ */ ++void visit_free(Visitor *v); ++ ++ ++/*** Visiting structures ***/ ++ ++/* ++ * Start visiting an object @obj (struct or union). ++ * ++ * @name expresses the relationship of this object to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL for a real walk, in which case @size ++ * determines how much memory an input or clone visitor will allocate ++ * into *@obj. @obj may also be NULL for a virtual walk, in which ++ * case @size is ignored. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * After visit_start_struct() succeeds, the caller may visit its ++ * members one after the other, passing the member's name and address ++ * within the struct. Finally, visit_end_struct() needs to be called ++ * with the same @obj to clean up, even if intermediate visits fail. ++ * See the examples above. ++ * ++ * FIXME Should this be named visit_start_object, since it is also ++ * used for QAPI unions, and maps to JSON objects? ++ */ ++bool visit_start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp); ++ ++/* ++ * Prepare for completing an object visit. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * Should be called prior to visit_end_struct() if all other ++ * intermediate visit steps were successful, to allow the visitor one ++ * last chance to report errors. May be skipped on a cleanup path, ++ * where there is no need to check for further errors. ++ */ ++bool visit_check_struct(Visitor *v, Error **errp); ++ ++/* ++ * Complete an object visit started earlier. ++ * ++ * @obj must match what was passed to the paired visit_start_struct(). ++ * ++ * Must be called after any successful use of visit_start_struct(), ++ * even if intermediate processing was skipped due to errors, to allow ++ * the backend to release any resources. Destroying the visitor early ++ * with visit_free() behaves as if this was implicitly called. ++ */ ++void visit_end_struct(Visitor *v, void **obj); ++ ++ ++/*** Visiting lists ***/ ++ ++/* ++ * Start visiting a list. ++ * ++ * @name expresses the relationship of this list to its parent ++ * container; see the general description of @name above. ++ * ++ * @list must be non-NULL for a real walk, in which case @size ++ * determines how much memory an input or clone visitor will allocate ++ * into *@list (at least sizeof(GenericList)). Some visitors also ++ * allow @list to be NULL for a virtual walk, in which case @size is ++ * ignored. ++ * ++ * On failure, set *@list to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * After visit_start_list() succeeds, the caller may visit its members ++ * one after the other. A real visit (where @list is non-NULL) uses ++ * visit_next_list() for traversing the linked list, while a virtual ++ * visit (where @list is NULL) uses other means. For each list ++ * element, call the appropriate visit_type_FOO() with name set to ++ * NULL and obj set to the address of the value member of the list ++ * element. Finally, visit_end_list() needs to be called with the ++ * same @list to clean up, even if intermediate visits fail. See the ++ * examples above. ++ */ ++bool visit_start_list(Visitor *v, const char *name, GenericList **list, ++ size_t size, Error **errp); ++ ++/* ++ * Iterate over a GenericList during a non-virtual list visit. ++ * ++ * @size represents the size of a linked list node (at least ++ * sizeof(GenericList)). ++ * ++ * @tail must not be NULL; on the first call, @tail is the value of ++ * *list after visit_start_list(), and on subsequent calls @tail must ++ * be the previously returned value. Should be called in a loop until ++ * a NULL return; for each non-NULL return, the caller then calls the ++ * appropriate visit_type_*() for the element type of the list, with ++ * that function's name parameter set to NULL and obj set to the ++ * address of @tail->value. ++ */ ++GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size); ++ ++/* ++ * Prepare for completing a list visit. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * Should be called prior to visit_end_list() if all other ++ * intermediate visit steps were successful, to allow the visitor one ++ * last chance to report errors. May be skipped on a cleanup path, ++ * where there is no need to check for further errors. ++ */ ++bool visit_check_list(Visitor *v, Error **errp); ++ ++/* ++ * Complete a list visit started earlier. ++ * ++ * @list must match what was passed to the paired visit_start_list(). ++ * ++ * Must be called after any successful use of visit_start_list(), even ++ * if intermediate processing was skipped due to errors, to allow the ++ * backend to release any resources. Destroying the visitor early ++ * with visit_free() behaves as if this was implicitly called. ++ */ ++void visit_end_list(Visitor *v, void **list); ++ ++ ++/*** Visiting alternates ***/ ++ ++/* ++ * Start the visit of an alternate @obj. ++ * ++ * @name expresses the relationship of this alternate to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must not be NULL. Input and clone visitors use @size to ++ * determine how much memory to allocate into *@obj, then determine ++ * the qtype of the next thing to be visited, and store it in ++ * (*@obj)->type. Other visitors leave @obj unchanged. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * If successful, this must be paired with visit_end_alternate() with ++ * the same @obj to clean up, even if visiting the contents of the ++ * alternate fails. ++ */ ++bool visit_start_alternate(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp); ++ ++/* ++ * Finish visiting an alternate type. ++ * ++ * @obj must match what was passed to the paired visit_start_alternate(). ++ * ++ * Must be called after any successful use of visit_start_alternate(), ++ * even if intermediate processing was skipped due to errors, to allow ++ * the backend to release any resources. Destroying the visitor early ++ * with visit_free() behaves as if this was implicitly called. ++ * ++ */ ++void visit_end_alternate(Visitor *v, void **obj); ++ ++ ++/*** Other helpers ***/ ++ ++/* ++ * Does optional struct member @name need visiting? ++ * ++ * @name must not be NULL. This function is only useful between ++ * visit_start_struct() and visit_end_struct(), since only objects ++ * have optional keys. ++ * ++ * @present points to the address of the optional member's has_ flag. ++ * ++ * Input visitors set *@present according to input; other visitors ++ * leave it unchanged. In either case, return *@present for ++ * convenience. ++ */ ++bool visit_optional(Visitor *v, const char *name, bool *present); ++ ++/* ++ * Should we reject member @name due to policy? ++ * ++ * @special_features is the member's special features encoded as a ++ * bitset of QapiSpecialFeature. ++ * ++ * @name must not be NULL. This function is only useful between ++ * visit_start_struct() and visit_end_struct(), since only objects ++ * have deprecated members. ++ */ ++bool visit_policy_reject(Visitor *v, const char *name, ++ unsigned special_features, Error **errp); ++ ++/* ++ * ++ * Should we skip member @name due to policy? ++ * ++ * @special_features is the member's special features encoded as a ++ * bitset of QapiSpecialFeature. ++ * ++ * @name must not be NULL. This function is only useful between ++ * visit_start_struct() and visit_end_struct(), since only objects ++ * have deprecated members. ++ */ ++bool visit_policy_skip(Visitor *v, const char *name, ++ unsigned special_features); ++ ++/* ++ * Set policy for handling deprecated management interfaces. ++ * ++ * Intended use: call visit_set_policy(v, &compat_policy) when ++ * visiting management interface input or output. ++ */ ++void visit_set_policy(Visitor *v, CompatPolicy *policy); ++ ++/* ++ * Visit an enum value. ++ * ++ * @name expresses the relationship of this enum to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors parse input and set *@obj to ++ * the enumeration value, leaving @obj unchanged on error; other ++ * visitors use *@obj but leave it unchanged. ++ * ++ * Currently, all input visitors parse text input, and all output ++ * visitors produce text output. The mapping between enumeration ++ * values and strings is done by the visitor core, using @lookup. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * May call visit_type_str() under the hood, and the enum visit may ++ * fail even if the corresponding string visit succeeded; this implies ++ * that an input visitor's visit_type_str() must have no unwelcome ++ * side effects. ++ */ ++bool visit_type_enum(Visitor *v, const char *name, int *obj, ++ const QEnumLookup *lookup, Error **errp); ++ ++/* ++ * Check if visitor is an input visitor. ++ */ ++bool visit_is_input(Visitor *v); ++ ++/* ++ * Check if visitor is a dealloc visitor. ++ */ ++bool visit_is_dealloc(Visitor *v); ++ ++/*** Visiting built-in types ***/ ++ ++/* ++ * Visit an integer value. ++ * ++ * @name expresses the relationship of this integer to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors will leave *@obj unchanged. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ */ ++bool visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp); ++ ++/* ++ * Visit a uint8_t value. ++ * Like visit_type_int(), except clamps the value to uint8_t range. ++ */ ++bool visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a uint16_t value. ++ * Like visit_type_int(), except clamps the value to uint16_t range. ++ */ ++bool visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a uint32_t value. ++ * Like visit_type_int(), except clamps the value to uint32_t range. ++ */ ++bool visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a uint64_t value. ++ * Like visit_type_int(), except clamps the value to uint64_t range, ++ * that is, ensures it is unsigned. ++ */ ++bool visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp); ++ ++/* ++ * Visit an int8_t value. ++ * Like visit_type_int(), except clamps the value to int8_t range. ++ */ ++bool visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp); ++ ++/* ++ * Visit an int16_t value. ++ * Like visit_type_int(), except clamps the value to int16_t range. ++ */ ++bool visit_type_int16(Visitor *v, const char *name, int16_t *obj, ++ Error **errp); ++ ++/* ++ * Visit an int32_t value. ++ * Like visit_type_int(), except clamps the value to int32_t range. ++ */ ++bool visit_type_int32(Visitor *v, const char *name, int32_t *obj, ++ Error **errp); ++ ++/* ++ * Visit an int64_t value. ++ * Identical to visit_type_int(). ++ */ ++bool visit_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a uint64_t value. ++ * Like visit_type_uint64(), except that some visitors may choose to ++ * recognize additional syntax, such as suffixes for easily scaling ++ * values. ++ */ ++bool visit_type_size(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp); ++ ++/* ++ * Visit a boolean value. ++ * ++ * @name expresses the relationship of this boolean to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors will leave *@obj unchanged. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ */ ++bool visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp); ++ ++/* ++ * Visit a string value. ++ * ++ * @name expresses the relationship of this string to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input and clone visitors set *@obj to the ++ * value (always using "" rather than NULL for an empty string). ++ * Other visitors leave *@obj unchanged, and commonly treat NULL like ++ * "". ++ * ++ * It is safe to cast away const when preparing a (const char *) value ++ * into @obj for use by an output visitor. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * FIXME: Callers that try to output NULL *obj should not be allowed. ++ */ ++bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp); ++ ++/* ++ * Visit a number (i.e. double) value. ++ * ++ * @name expresses the relationship of this number to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors will leave *@obj unchanged. Visitors should ++ * document if infinity or NaN are not permitted. ++ * ++ * On failure, store an error through @errp. Can happen only when @v ++ * is an input visitor. ++ * ++ * Return true on success, false on failure. ++ */ ++bool visit_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp); ++ ++/* ++ * Visit an arbitrary value. ++ * ++ * @name expresses the relationship of this value to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors will leave *@obj unchanged. *@obj must be non-NULL ++ * for output visitors. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ * ++ * Note that some kinds of input can't express arbitrary QObject. ++ * E.g. the visitor returned by qobject_input_visitor_new_keyval() ++ * can't create numbers or booleans, only strings. ++ */ ++bool visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp); ++ ++/* ++ * Visit a JSON null value. ++ * ++ * @name expresses the relationship of the null value to its parent ++ * container; see the general description of @name above. ++ * ++ * @obj must be non-NULL. Input visitors set *@obj to the value; ++ * other visitors ignore *@obj. ++ * ++ * On failure, set *@obj to NULL and store an error through @errp. ++ * Can happen only when @v is an input visitor. ++ * ++ * Return true on success, false on failure. ++ */ ++bool visit_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp); ++ ++#endif +diff --git a/include/qcow2.h b/include/qcow2.h +new file mode 100644 +index 00000000..a9e3481c +--- /dev/null ++++ b/include/qcow2.h +@@ -0,0 +1,1074 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_QCOW2_H ++#define BLOCK_QCOW2_H ++ ++#include "crypto/block.h" ++#include "qemu/coroutine.h" ++#include "qemu/units.h" ++#include "block/block_int.h" ++ ++//#define DEBUG_ALLOC ++//#define DEBUG_ALLOC2 ++//#define DEBUG_EXT ++ ++#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb) ++ ++#define QCOW_CRYPT_NONE 0 ++#define QCOW_CRYPT_AES 1 ++#define QCOW_CRYPT_LUKS 2 ++ ++#define QCOW_MAX_CRYPT_CLUSTERS 32 ++#define QCOW_MAX_SNAPSHOTS 65536 ++ ++/* Field widths in qcow2 mean normal cluster offsets cannot reach ++ * 64PB; depending on cluster size, compressed clusters can have a ++ * smaller limit (64PB for up to 16k clusters, then ramps down to ++ * 512TB for 2M clusters). */ ++#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1) ++ ++/* 8 MB refcount table is enough for 2 PB images at 64k cluster size ++ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ ++#define QCOW_MAX_REFTABLE_SIZE (8 * MiB) ++ ++/* 32 MB L1 table is enough for 2 PB images at 64k cluster size ++ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ ++#define QCOW_MAX_L1_SIZE (32 * MiB) ++ ++/* Allow for an average of 1k per snapshot table entry, should be plenty of ++ * space for snapshot names and IDs */ ++#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS) ++ ++/* Maximum amount of extra data per snapshot table entry to accept */ ++#define QCOW_MAX_SNAPSHOT_EXTRA_DATA 1024 ++ ++/* Bitmap header extension constraints */ ++#define QCOW2_MAX_BITMAPS 65535 ++#define QCOW2_MAX_BITMAP_DIRECTORY_SIZE (1024 * QCOW2_MAX_BITMAPS) ++ ++/* Maximum of parallel sub-request per guest request */ ++#define QCOW2_MAX_WORKERS 8 ++ ++/* indicate that the refcount of the referenced cluster is exactly one. */ ++#define QCOW_OFLAG_COPIED (1ULL << 63) ++/* indicate that the cluster is compressed (they never have the copied flag) */ ++#define QCOW_OFLAG_COMPRESSED (1ULL << 62) ++/* The cluster reads as all zeros */ ++#define QCOW_OFLAG_ZERO (1ULL << 0) ++ ++#define QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER 32 ++ ++/* The subcluster X [0..31] is allocated */ ++#define QCOW_OFLAG_SUB_ALLOC(X) (1ULL << (X)) ++/* The subcluster X [0..31] reads as zeroes */ ++#define QCOW_OFLAG_SUB_ZERO(X) (QCOW_OFLAG_SUB_ALLOC(X) << 32) ++/* Subclusters [X, Y) (0 <= X <= Y <= 32) are allocated */ ++#define QCOW_OFLAG_SUB_ALLOC_RANGE(X, Y) \ ++ (QCOW_OFLAG_SUB_ALLOC(Y) - QCOW_OFLAG_SUB_ALLOC(X)) ++/* Subclusters [X, Y) (0 <= X <= Y <= 32) read as zeroes */ ++#define QCOW_OFLAG_SUB_ZERO_RANGE(X, Y) \ ++ (QCOW_OFLAG_SUB_ALLOC_RANGE(X, Y) << 32) ++/* L2 entry bitmap with all allocation bits set */ ++#define QCOW_L2_BITMAP_ALL_ALLOC (QCOW_OFLAG_SUB_ALLOC_RANGE(0, 32)) ++/* L2 entry bitmap with all "read as zeroes" bits set */ ++#define QCOW_L2_BITMAP_ALL_ZEROES (QCOW_OFLAG_SUB_ZERO_RANGE(0, 32)) ++ ++/* Size of normal and extended L2 entries */ ++#define L2E_SIZE_NORMAL (sizeof(uint64_t)) ++#define L2E_SIZE_EXTENDED (sizeof(uint64_t) * 2) ++ ++/* Size of L1 table entries */ ++#define L1E_SIZE (sizeof(uint64_t)) ++ ++/* Size of reftable entries */ ++#define REFTABLE_ENTRY_SIZE (sizeof(uint64_t)) ++ ++#define MIN_CLUSTER_BITS 9 ++#define MAX_CLUSTER_BITS 21 ++ ++/* Defined in the qcow2 spec (compressed cluster descriptor) */ ++#define QCOW2_COMPRESSED_SECTOR_SIZE 512U ++ ++/* Must be at least 2 to cover COW */ ++#define MIN_L2_CACHE_SIZE 2 /* cache entries */ ++ ++/* Must be at least 4 to cover all cases of refcount table growth */ ++#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ ++ ++#ifdef CONFIG_LINUX ++#define DEFAULT_L2_CACHE_MAX_SIZE (32 * MiB) ++#define DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */ ++#else ++#define DEFAULT_L2_CACHE_MAX_SIZE (8 * MiB) ++/* Cache clean interval is currently available only on Linux, so must be 0 */ ++#define DEFAULT_CACHE_CLEAN_INTERVAL 0 ++#endif ++ ++#define DEFAULT_CLUSTER_SIZE 65536 ++ ++#define QCOW2_OPT_DATA_FILE "data-file" ++#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" ++#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" ++#define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" ++#define QCOW2_OPT_DISCARD_OTHER "pass-discard-other" ++#define QCOW2_OPT_DISCARD_NO_UNREF "discard-no-unref" ++#define QCOW2_OPT_OVERLAP "overlap-check" ++#define QCOW2_OPT_OVERLAP_TEMPLATE "overlap-check.template" ++#define QCOW2_OPT_OVERLAP_MAIN_HEADER "overlap-check.main-header" ++#define QCOW2_OPT_OVERLAP_ACTIVE_L1 "overlap-check.active-l1" ++#define QCOW2_OPT_OVERLAP_ACTIVE_L2 "overlap-check.active-l2" ++#define QCOW2_OPT_OVERLAP_REFCOUNT_TABLE "overlap-check.refcount-table" ++#define QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK "overlap-check.refcount-block" ++#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" ++#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" ++#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" ++#define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory" ++#define QCOW2_OPT_CACHE_SIZE "cache-size" ++#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" ++#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" ++#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size" ++#define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval" ++ ++typedef struct QCowHeader { ++ uint32_t magic; ++ uint32_t version; ++ uint64_t backing_file_offset; ++ uint32_t backing_file_size; ++ uint32_t cluster_bits; ++ uint64_t size; /* in bytes */ ++ uint32_t crypt_method; ++ uint32_t l1_size; /* XXX: save number of clusters instead ? */ ++ uint64_t l1_table_offset; ++ uint64_t refcount_table_offset; ++ uint32_t refcount_table_clusters; ++ uint32_t nb_snapshots; ++ uint64_t snapshots_offset; ++ ++ /* The following fields are only valid for version >= 3 */ ++ uint64_t incompatible_features; ++ uint64_t compatible_features; ++ uint64_t autoclear_features; ++ ++ uint32_t refcount_order; ++ uint32_t header_length; ++ ++ /* Additional fields */ ++ uint8_t compression_type; ++ ++ /* header must be a multiple of 8 */ ++ uint8_t padding[7]; ++} QEMU_PACKED QCowHeader; ++ ++QEMU_BUILD_BUG_ON(!QEMU_IS_ALIGNED(sizeof(QCowHeader), 8)); ++ ++typedef struct QEMU_PACKED QCowSnapshotHeader { ++ /* header is 8 byte aligned */ ++ uint64_t l1_table_offset; ++ ++ uint32_t l1_size; ++ uint16_t id_str_size; ++ uint16_t name_size; ++ ++ uint32_t date_sec; ++ uint32_t date_nsec; ++ ++ uint64_t vm_clock_nsec; ++ ++ uint32_t vm_state_size; ++ uint32_t extra_data_size; /* for extension */ ++ /* extra data follows */ ++ /* id_str follows */ ++ /* name follows */ ++} QCowSnapshotHeader; ++ ++typedef struct QEMU_PACKED QCowSnapshotExtraData { ++ uint64_t vm_state_size_large; ++ uint64_t disk_size; ++ uint64_t icount; ++} QCowSnapshotExtraData; ++ ++ ++typedef struct QCowSnapshot { ++ uint64_t l1_table_offset; ++ uint32_t l1_size; ++ char *id_str; ++ char *name; ++ uint64_t disk_size; ++ uint64_t vm_state_size; ++ uint32_t date_sec; ++ uint32_t date_nsec; ++ uint64_t vm_clock_nsec; ++ /* icount value for the moment when snapshot was taken */ ++ uint64_t icount; ++ /* Size of all extra data, including QCowSnapshotExtraData if available */ ++ uint32_t extra_data_size; ++ /* Data beyond QCowSnapshotExtraData, if any */ ++ void *unknown_extra_data; ++} QCowSnapshot; ++ ++struct Qcow2Cache; ++typedef struct Qcow2Cache Qcow2Cache; ++ ++typedef struct Qcow2CryptoHeaderExtension { ++ uint64_t offset; ++ uint64_t length; ++} QEMU_PACKED Qcow2CryptoHeaderExtension; ++ ++typedef struct Qcow2UnknownHeaderExtension { ++ uint32_t magic; ++ uint32_t len; ++ QLIST_ENTRY(Qcow2UnknownHeaderExtension) next; ++ uint8_t data[]; ++} Qcow2UnknownHeaderExtension; ++ ++enum { ++ QCOW2_FEAT_TYPE_INCOMPATIBLE = 0, ++ QCOW2_FEAT_TYPE_COMPATIBLE = 1, ++ QCOW2_FEAT_TYPE_AUTOCLEAR = 2, ++}; ++ ++/* Incompatible feature bits */ ++enum { ++ QCOW2_INCOMPAT_DIRTY_BITNR = 0, ++ QCOW2_INCOMPAT_CORRUPT_BITNR = 1, ++ QCOW2_INCOMPAT_DATA_FILE_BITNR = 2, ++ QCOW2_INCOMPAT_COMPRESSION_BITNR = 3, ++ QCOW2_INCOMPAT_EXTL2_BITNR = 4, ++ QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, ++ QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, ++ QCOW2_INCOMPAT_DATA_FILE = 1 << QCOW2_INCOMPAT_DATA_FILE_BITNR, ++ QCOW2_INCOMPAT_COMPRESSION = 1 << QCOW2_INCOMPAT_COMPRESSION_BITNR, ++ QCOW2_INCOMPAT_EXTL2 = 1 << QCOW2_INCOMPAT_EXTL2_BITNR, ++ ++ QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY ++ | QCOW2_INCOMPAT_CORRUPT ++ | QCOW2_INCOMPAT_DATA_FILE ++ | QCOW2_INCOMPAT_COMPRESSION ++ | QCOW2_INCOMPAT_EXTL2, ++}; ++ ++/* Compatible feature bits */ ++enum { ++ QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR = 0, ++ QCOW2_COMPAT_LAZY_REFCOUNTS = 1 << QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, ++ ++ QCOW2_COMPAT_FEAT_MASK = QCOW2_COMPAT_LAZY_REFCOUNTS, ++}; ++ ++/* Autoclear feature bits */ ++enum { ++ QCOW2_AUTOCLEAR_BITMAPS_BITNR = 0, ++ QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR = 1, ++ QCOW2_AUTOCLEAR_BITMAPS = 1 << QCOW2_AUTOCLEAR_BITMAPS_BITNR, ++ QCOW2_AUTOCLEAR_DATA_FILE_RAW = 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR, ++ ++ QCOW2_AUTOCLEAR_MASK = QCOW2_AUTOCLEAR_BITMAPS ++ | QCOW2_AUTOCLEAR_DATA_FILE_RAW, ++}; ++ ++enum qcow2_discard_type { ++ QCOW2_DISCARD_NEVER = 0, ++ QCOW2_DISCARD_ALWAYS, ++ QCOW2_DISCARD_REQUEST, ++ QCOW2_DISCARD_SNAPSHOT, ++ QCOW2_DISCARD_OTHER, ++ QCOW2_DISCARD_MAX ++}; ++ ++typedef struct Qcow2Feature { ++ uint8_t type; ++ uint8_t bit; ++ char name[46]; ++} QEMU_PACKED Qcow2Feature; ++ ++typedef struct Qcow2DiscardRegion { ++ BlockDriverState *bs; ++ uint64_t offset; ++ uint64_t bytes; ++ QTAILQ_ENTRY(Qcow2DiscardRegion) next; ++} Qcow2DiscardRegion; ++ ++typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array, ++ uint64_t index); ++typedef void Qcow2SetRefcountFunc(void *refcount_array, ++ uint64_t index, uint64_t value); ++ ++typedef struct Qcow2BitmapHeaderExt { ++ uint32_t nb_bitmaps; ++ uint32_t reserved32; ++ uint64_t bitmap_directory_size; ++ uint64_t bitmap_directory_offset; ++} QEMU_PACKED Qcow2BitmapHeaderExt; ++ ++#define QCOW2_MAX_THREADS 4 ++ ++typedef struct BDRVQcow2State { ++ int cluster_bits; ++ int cluster_size; ++ int l2_slice_size; ++ int subcluster_bits; ++ int subcluster_size; ++ int subclusters_per_cluster; ++ int l2_bits; ++ int l2_size; ++ int l1_size; ++ int l1_vm_state_index; ++ int refcount_block_bits; ++ int refcount_block_size; ++ int csize_shift; ++ int csize_mask; ++ uint64_t cluster_offset_mask; ++ uint64_t l1_table_offset; ++ uint64_t *l1_table; ++ ++ Qcow2Cache *l2_table_cache; ++ Qcow2Cache *refcount_block_cache; ++ QEMUTimer *cache_clean_timer; ++ unsigned cache_clean_interval; ++ ++ QLIST_HEAD(, QCowL2Meta) cluster_allocs; ++ ++ uint64_t *refcount_table; ++ uint64_t refcount_table_offset; ++ uint32_t refcount_table_size; ++ uint32_t max_refcount_table_index; /* Last used entry in refcount_table */ ++ uint64_t free_cluster_index; ++ uint64_t free_byte_offset; ++ ++ CoMutex lock; ++ ++ Qcow2CryptoHeaderExtension crypto_header; /* QCow2 header extension */ ++ QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ ++ QCryptoBlock *crypto; /* Disk encryption format driver */ ++ bool crypt_physical_offset; /* Whether to use virtual or physical offset ++ for encryption initialization vector tweak */ ++ uint32_t crypt_method_header; ++ uint64_t snapshots_offset; ++ int snapshots_size; ++ unsigned int nb_snapshots; ++ QCowSnapshot *snapshots; ++ ++ uint32_t nb_bitmaps; ++ uint64_t bitmap_directory_size; ++ uint64_t bitmap_directory_offset; ++ ++ int flags; ++ int qcow_version; ++ bool use_lazy_refcounts; ++ int refcount_order; ++ int refcount_bits; ++ uint64_t refcount_max; ++ ++ Qcow2GetRefcountFunc *get_refcount; ++ Qcow2SetRefcountFunc *set_refcount; ++ ++ bool discard_passthrough[QCOW2_DISCARD_MAX]; ++ ++ bool discard_no_unref; ++ ++ int overlap_check; /* bitmask of Qcow2MetadataOverlap values */ ++ bool signaled_corruption; ++ ++ uint64_t incompatible_features; ++ uint64_t compatible_features; ++ uint64_t autoclear_features; ++ ++ size_t unknown_header_fields_size; ++ void *unknown_header_fields; ++ QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; ++ QTAILQ_HEAD (, Qcow2DiscardRegion) discards; ++ bool cache_discards; ++ ++ /* Backing file path and format as stored in the image (this is not the ++ * effective path/format, which may be the result of a runtime option ++ * override) */ ++ char *image_backing_file; ++ char *image_backing_format; ++ char *image_data_file; ++ ++ CoQueue thread_task_queue; ++ int nb_threads; ++ ++ BdrvChild *data_file; ++ ++ bool metadata_preallocation_checked; ++ bool metadata_preallocation; ++ /* ++ * Compression type used for the image. Default: 0 - ZLIB ++ * The image compression type is set on image creation. ++ * For now, the only way to change the compression type ++ * is to convert the image with the desired compression type set. ++ */ ++ Qcow2CompressionType compression_type; ++} BDRVQcow2State; ++ ++typedef struct Qcow2COWRegion { ++ /** ++ * Offset of the COW region in bytes from the start of the first cluster ++ * touched by the request. ++ */ ++ unsigned offset; ++ ++ /** Number of bytes to copy */ ++ unsigned nb_bytes; ++} Qcow2COWRegion; ++ ++/** ++ * Describes an in-flight (part of a) write request that writes to clusters ++ * that need to have their L2 table entries updated (because they are ++ * newly allocated or need changes in their L2 bitmaps) ++ */ ++typedef struct QCowL2Meta ++{ ++ /** Guest offset of the first updated cluster */ ++ uint64_t offset; ++ ++ /** Host offset of the first updated cluster */ ++ uint64_t alloc_offset; ++ ++ /** Number of updated clusters */ ++ int nb_clusters; ++ ++ /** Do not free the old clusters */ ++ bool keep_old_clusters; ++ ++ /** ++ * Requests that overlap with this allocation and wait to be restarted ++ * when the allocating request has completed. ++ */ ++ CoQueue dependent_requests; ++ ++ /** ++ * The COW Region immediately before the area the guest actually ++ * writes to. This (part of the) write request starts at ++ * cow_start.offset + cow_start.nb_bytes. ++ */ ++ Qcow2COWRegion cow_start; ++ ++ /** ++ * The COW Region immediately after the area the guest actually ++ * writes to. This (part of the) write request ends at cow_end.offset ++ * (which must always be set even when cow_end.nb_bytes is 0). ++ */ ++ Qcow2COWRegion cow_end; ++ ++ /* ++ * Indicates that COW regions are already handled and do not require ++ * any more processing. ++ */ ++ bool skip_cow; ++ ++ /** ++ * Indicates that this is not a normal write request but a preallocation. ++ * If the image has extended L2 entries this means that no new individual ++ * subclusters will be marked as allocated in the L2 bitmap (but any ++ * existing contents of that bitmap will be kept). ++ */ ++ bool prealloc; ++ ++ /** ++ * The I/O vector with the data from the actual guest write request. ++ * If non-NULL, this is meant to be merged together with the data ++ * from @cow_start and @cow_end into one single write operation. ++ */ ++ QEMUIOVector *data_qiov; ++ size_t data_qiov_offset; ++ ++ /** Pointer to next L2Meta of the same write request */ ++ struct QCowL2Meta *next; ++ ++ QLIST_ENTRY(QCowL2Meta) next_in_flight; ++} QCowL2Meta; ++ ++/* ++ * In images with standard L2 entries all clusters are treated as if ++ * they had one subcluster so QCow2ClusterType and QCow2SubclusterType ++ * can be mapped to each other and have the exact same meaning ++ * (QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC cannot happen in these images). ++ * ++ * In images with extended L2 entries QCow2ClusterType refers to the ++ * complete cluster and QCow2SubclusterType to each of the individual ++ * subclusters, so there are several possible combinations: ++ * ++ * |--------------+---------------------------| ++ * | Cluster type | Possible subcluster types | ++ * |--------------+---------------------------| ++ * | UNALLOCATED | UNALLOCATED_PLAIN | ++ * | | ZERO_PLAIN | ++ * |--------------+---------------------------| ++ * | NORMAL | UNALLOCATED_ALLOC | ++ * | | ZERO_ALLOC | ++ * | | NORMAL | ++ * |--------------+---------------------------| ++ * | COMPRESSED | COMPRESSED | ++ * |--------------+---------------------------| ++ * ++ * QCOW2_SUBCLUSTER_INVALID means that the L2 entry is incorrect and ++ * the image should be marked corrupt. ++ */ ++ ++typedef enum QCow2ClusterType { ++ QCOW2_CLUSTER_UNALLOCATED, ++ QCOW2_CLUSTER_ZERO_PLAIN, ++ QCOW2_CLUSTER_ZERO_ALLOC, ++ QCOW2_CLUSTER_NORMAL, ++ QCOW2_CLUSTER_COMPRESSED, ++} QCow2ClusterType; ++ ++typedef enum QCow2SubclusterType { ++ QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN, ++ QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC, ++ QCOW2_SUBCLUSTER_ZERO_PLAIN, ++ QCOW2_SUBCLUSTER_ZERO_ALLOC, ++ QCOW2_SUBCLUSTER_NORMAL, ++ QCOW2_SUBCLUSTER_COMPRESSED, ++ QCOW2_SUBCLUSTER_INVALID, ++} QCow2SubclusterType; ++ ++typedef enum QCow2MetadataOverlap { ++ QCOW2_OL_MAIN_HEADER_BITNR = 0, ++ QCOW2_OL_ACTIVE_L1_BITNR = 1, ++ QCOW2_OL_ACTIVE_L2_BITNR = 2, ++ QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, ++ QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, ++ QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, ++ QCOW2_OL_INACTIVE_L1_BITNR = 6, ++ QCOW2_OL_INACTIVE_L2_BITNR = 7, ++ QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8, ++ ++ QCOW2_OL_MAX_BITNR = 9, ++ ++ QCOW2_OL_NONE = 0, ++ QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR), ++ QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR), ++ QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR), ++ QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), ++ QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), ++ QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), ++ QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR), ++ /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv ++ * reads. */ ++ QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), ++ QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR), ++} QCow2MetadataOverlap; ++ ++/* Perform all overlap checks which can be done in constant time */ ++#define QCOW2_OL_CONSTANT \ ++ (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \ ++ QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY) ++ ++/* Perform all overlap checks which don't require disk access */ ++#define QCOW2_OL_CACHED \ ++ (QCOW2_OL_CONSTANT | QCOW2_OL_ACTIVE_L2 | QCOW2_OL_REFCOUNT_BLOCK | \ ++ QCOW2_OL_INACTIVE_L1) ++ ++/* Perform all overlap checks */ ++#define QCOW2_OL_ALL \ ++ (QCOW2_OL_CACHED | QCOW2_OL_INACTIVE_L2) ++ ++#define L1E_OFFSET_MASK 0x00fffffffffffe00ULL ++#define L1E_RESERVED_MASK 0x7f000000000001ffULL ++#define L2E_OFFSET_MASK 0x00fffffffffffe00ULL ++#define L2E_STD_RESERVED_MASK 0x3f000000000001feULL ++ ++#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL ++#define REFT_RESERVED_MASK 0x1ffULL ++ ++#define INV_OFFSET (-1ULL) ++ ++static inline bool has_subclusters(BDRVQcow2State *s) ++{ ++ return s->incompatible_features & QCOW2_INCOMPAT_EXTL2; ++} ++ ++static inline size_t l2_entry_size(BDRVQcow2State *s) ++{ ++ return has_subclusters(s) ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; ++} ++ ++static inline uint64_t get_l2_entry(BDRVQcow2State *s, uint64_t *l2_slice, ++ int idx) ++{ ++ idx *= l2_entry_size(s) / sizeof(uint64_t); ++ return be64_to_cpu(l2_slice[idx]); ++} ++ ++static inline uint64_t get_l2_bitmap(BDRVQcow2State *s, uint64_t *l2_slice, ++ int idx) ++{ ++ if (has_subclusters(s)) { ++ idx *= l2_entry_size(s) / sizeof(uint64_t); ++ return be64_to_cpu(l2_slice[idx + 1]); ++ } else { ++ return 0; /* For convenience only; this value has no meaning. */ ++ } ++} ++ ++static inline void set_l2_entry(BDRVQcow2State *s, uint64_t *l2_slice, ++ int idx, uint64_t entry) ++{ ++ idx *= l2_entry_size(s) / sizeof(uint64_t); ++ l2_slice[idx] = cpu_to_be64(entry); ++} ++ ++static inline void set_l2_bitmap(BDRVQcow2State *s, uint64_t *l2_slice, ++ int idx, uint64_t bitmap) ++{ ++ assert(has_subclusters(s)); ++ idx *= l2_entry_size(s) / sizeof(uint64_t); ++ l2_slice[idx + 1] = cpu_to_be64(bitmap); ++} ++ ++static inline bool GRAPH_RDLOCK has_data_file(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ return (s->data_file != bs->file); ++} ++ ++static inline bool data_file_is_raw(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ return !!(s->autoclear_features & QCOW2_AUTOCLEAR_DATA_FILE_RAW); ++} ++ ++static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset) ++{ ++ return offset & ~(s->cluster_size - 1); ++} ++ ++static inline int64_t offset_into_cluster(BDRVQcow2State *s, int64_t offset) ++{ ++ return offset & (s->cluster_size - 1); ++} ++ ++static inline int64_t offset_into_subcluster(BDRVQcow2State *s, int64_t offset) ++{ ++ return offset & (s->subcluster_size - 1); ++} ++ ++static inline uint64_t size_to_clusters(BDRVQcow2State *s, uint64_t size) ++{ ++ return (size + (s->cluster_size - 1)) >> s->cluster_bits; ++} ++ ++static inline uint64_t size_to_subclusters(BDRVQcow2State *s, uint64_t size) ++{ ++ return (size + (s->subcluster_size - 1)) >> s->subcluster_bits; ++} ++ ++static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size) ++{ ++ int shift = s->cluster_bits + s->l2_bits; ++ return (size + (1ULL << shift) - 1) >> shift; ++} ++ ++static inline int offset_to_l1_index(BDRVQcow2State *s, uint64_t offset) ++{ ++ return offset >> (s->l2_bits + s->cluster_bits); ++} ++ ++static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset) ++{ ++ return (offset >> s->cluster_bits) & (s->l2_size - 1); ++} ++ ++static inline int offset_to_l2_slice_index(BDRVQcow2State *s, int64_t offset) ++{ ++ return (offset >> s->cluster_bits) & (s->l2_slice_size - 1); ++} ++ ++static inline int offset_to_sc_index(BDRVQcow2State *s, int64_t offset) ++{ ++ return (offset >> s->subcluster_bits) & (s->subclusters_per_cluster - 1); ++} ++ ++static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s) ++{ ++ return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); ++} ++ ++static inline QCow2ClusterType GRAPH_RDLOCK ++qcow2_get_cluster_type(BlockDriverState *bs, uint64_t l2_entry) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (l2_entry & QCOW_OFLAG_COMPRESSED) { ++ return QCOW2_CLUSTER_COMPRESSED; ++ } else if ((l2_entry & QCOW_OFLAG_ZERO) && !has_subclusters(s)) { ++ if (l2_entry & L2E_OFFSET_MASK) { ++ return QCOW2_CLUSTER_ZERO_ALLOC; ++ } ++ return QCOW2_CLUSTER_ZERO_PLAIN; ++ } else if (!(l2_entry & L2E_OFFSET_MASK)) { ++ /* Offset 0 generally means unallocated, but it is ambiguous with ++ * external data files because 0 is a valid offset there. However, all ++ * clusters in external data files always have refcount 1, so we can ++ * rely on QCOW_OFLAG_COPIED to disambiguate. */ ++ if (has_data_file(bs) && (l2_entry & QCOW_OFLAG_COPIED)) { ++ return QCOW2_CLUSTER_NORMAL; ++ } else { ++ return QCOW2_CLUSTER_UNALLOCATED; ++ } ++ } else { ++ return QCOW2_CLUSTER_NORMAL; ++ } ++} ++ ++/* ++ * In an image without subsclusters @l2_bitmap is ignored and ++ * @sc_index must be 0. ++ * Return QCOW2_SUBCLUSTER_INVALID if an invalid l2 entry is detected ++ * (this checks the whole entry and bitmap, not only the bits related ++ * to subcluster @sc_index). ++ */ ++static inline GRAPH_RDLOCK ++QCow2SubclusterType qcow2_get_subcluster_type(BlockDriverState *bs, ++ uint64_t l2_entry, ++ uint64_t l2_bitmap, ++ unsigned sc_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCow2ClusterType type = qcow2_get_cluster_type(bs, l2_entry); ++ assert(sc_index < s->subclusters_per_cluster); ++ ++ if (has_subclusters(s)) { ++ switch (type) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ return QCOW2_SUBCLUSTER_COMPRESSED; ++ case QCOW2_CLUSTER_NORMAL: ++ if ((l2_bitmap >> 32) & l2_bitmap) { ++ return QCOW2_SUBCLUSTER_INVALID; ++ } else if (l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index)) { ++ return QCOW2_SUBCLUSTER_ZERO_ALLOC; ++ } else if (l2_bitmap & QCOW_OFLAG_SUB_ALLOC(sc_index)) { ++ return QCOW2_SUBCLUSTER_NORMAL; ++ } else { ++ return QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC; ++ } ++ case QCOW2_CLUSTER_UNALLOCATED: ++ if (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC) { ++ return QCOW2_SUBCLUSTER_INVALID; ++ } else if (l2_bitmap & QCOW_OFLAG_SUB_ZERO(sc_index)) { ++ return QCOW2_SUBCLUSTER_ZERO_PLAIN; ++ } else { ++ return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; ++ } ++ default: ++ g_assert_not_reached(); ++ } ++ } else { ++ switch (type) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ return QCOW2_SUBCLUSTER_COMPRESSED; ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ return QCOW2_SUBCLUSTER_ZERO_PLAIN; ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ return QCOW2_SUBCLUSTER_ZERO_ALLOC; ++ case QCOW2_CLUSTER_NORMAL: ++ return QCOW2_SUBCLUSTER_NORMAL; ++ case QCOW2_CLUSTER_UNALLOCATED: ++ return QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++} ++ ++static inline bool qcow2_cluster_is_allocated(QCow2ClusterType type) ++{ ++ return (type == QCOW2_CLUSTER_COMPRESSED || type == QCOW2_CLUSTER_NORMAL || ++ type == QCOW2_CLUSTER_ZERO_ALLOC); ++} ++ ++/* Check whether refcounts are eager or lazy */ ++static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s) ++{ ++ return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY); ++} ++ ++static inline uint64_t l2meta_cow_start(QCowL2Meta *m) ++{ ++ return m->offset + m->cow_start.offset; ++} ++ ++static inline uint64_t l2meta_cow_end(QCowL2Meta *m) ++{ ++ return m->offset + m->cow_end.offset + m->cow_end.nb_bytes; ++} ++ ++static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2) ++{ ++ return r1 > r2 ? r1 - r2 : r2 - r1; ++} ++ ++static inline ++uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset) ++{ ++ return offset >> (s->refcount_block_bits + s->cluster_bits); ++} ++ ++/* qcow2.c functions */ ++int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, ++ int refcount_order, bool generous_increase, ++ uint64_t *refblock_count); ++ ++int GRAPH_RDLOCK qcow2_mark_dirty(BlockDriverState *bs); ++int GRAPH_RDLOCK qcow2_mark_corrupt(BlockDriverState *bs); ++int GRAPH_RDLOCK qcow2_update_header(BlockDriverState *bs); ++ ++void GRAPH_RDLOCK ++qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, ++ int64_t size, const char *message_format, ...) ++ G_GNUC_PRINTF(5, 6); ++ ++int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, ++ uint64_t entries, size_t entry_len, ++ int64_t max_size_bytes, const char *table_name, ++ Error **errp); ++ ++/* qcow2-refcount.c functions */ ++int coroutine_fn GRAPH_RDLOCK qcow2_refcount_init(BlockDriverState *bs); ++void qcow2_refcount_close(BlockDriverState *bs); ++ ++int GRAPH_RDLOCK qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, ++ uint64_t *refcount); ++ ++int GRAPH_RDLOCK ++qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, ++ uint64_t addend, bool decrease, ++ enum qcow2_discard_type type); ++ ++int64_t GRAPH_RDLOCK ++qcow2_refcount_area(BlockDriverState *bs, uint64_t offset, ++ uint64_t additional_clusters, bool exact_size, ++ int new_refblock_index, ++ uint64_t new_refblock_offset); ++ ++int64_t GRAPH_RDLOCK ++qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); ++ ++int64_t GRAPH_RDLOCK coroutine_fn ++qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, ++ int64_t nb_clusters); ++ ++int64_t coroutine_fn GRAPH_RDLOCK qcow2_alloc_bytes(BlockDriverState *bs, int size); ++void GRAPH_RDLOCK qcow2_free_clusters(BlockDriverState *bs, ++ int64_t offset, int64_t size, ++ enum qcow2_discard_type type); ++void GRAPH_RDLOCK ++qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, ++ enum qcow2_discard_type type); ++ ++int GRAPH_RDLOCK ++qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, ++ int l1_size, int addend); ++ ++int GRAPH_RDLOCK qcow2_flush_caches(BlockDriverState *bs); ++int GRAPH_RDLOCK qcow2_write_caches(BlockDriverState *bs); ++int coroutine_fn qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ BdrvCheckMode fix); ++ ++void GRAPH_RDLOCK qcow2_process_discards(BlockDriverState *bs, int ret); ++ ++int GRAPH_RDLOCK ++qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, ++ int64_t size); ++int GRAPH_RDLOCK ++qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, ++ int64_t size, bool data_file); ++ ++int coroutine_fn qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size, ++ int64_t offset, int64_t size); ++ ++int GRAPH_RDLOCK ++qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque, Error **errp); ++int coroutine_fn GRAPH_RDLOCK qcow2_shrink_reftable(BlockDriverState *bs); ++ ++int64_t coroutine_fn GRAPH_RDLOCK ++qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_detect_metadata_preallocation(BlockDriverState *bs); ++ ++/* qcow2-cluster.c functions */ ++int GRAPH_RDLOCK ++qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, bool exact_size); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); ++ ++int GRAPH_RDLOCK 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); ++ ++int GRAPH_RDLOCK ++qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, ++ unsigned int *bytes, uint64_t *host_offset, ++ QCow2SubclusterType *subcluster_type); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, ++ unsigned int *bytes, uint64_t *host_offset, ++ QCowL2Meta **m); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, ++ int compressed_size, uint64_t *host_offset); ++void GRAPH_RDLOCK ++qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry, ++ uint64_t *coffset, int *csize); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); ++ ++void coroutine_fn GRAPH_RDLOCK ++qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); ++ ++int GRAPH_RDLOCK ++qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, uint64_t bytes, ++ enum qcow2_discard_type type, bool full_discard); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, uint64_t bytes, ++ int flags); ++ ++int GRAPH_RDLOCK ++qcow2_expand_zero_clusters(BlockDriverState *bs, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque); ++ ++/* qcow2-snapshot.c functions */ ++int GRAPH_RDLOCK ++qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); ++ ++int GRAPH_RDLOCK ++qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); ++ ++int GRAPH_RDLOCK ++qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, ++ const char *name, Error **errp); ++ ++int GRAPH_RDLOCK ++qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); ++ ++int GRAPH_RDLOCK ++qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_id, ++ const char *name, Error **errp); ++ ++void qcow2_free_snapshots(BlockDriverState *bs); ++int coroutine_fn GRAPH_RDLOCK ++qcow2_read_snapshots(BlockDriverState *bs, Error **errp); ++int GRAPH_RDLOCK qcow2_write_snapshots(BlockDriverState *bs); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_check_read_snapshot_table(BlockDriverState *bs, BdrvCheckResult *result, ++ BdrvCheckMode fix); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_check_fix_snapshot_table(BlockDriverState *bs, BdrvCheckResult *result, ++ BdrvCheckMode fix); ++ ++/* qcow2-cache.c functions */ ++Qcow2Cache * GRAPH_RDLOCK ++qcow2_cache_create(BlockDriverState *bs, int num_tables, unsigned table_size); ++ ++int qcow2_cache_destroy(Qcow2Cache *c); ++ ++void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); ++int GRAPH_RDLOCK qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); ++int GRAPH_RDLOCK qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c); ++int GRAPH_RDLOCK qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, ++ Qcow2Cache *dependency); ++void qcow2_cache_depends_on_flush(Qcow2Cache *c); ++ ++void qcow2_cache_clean_unused(Qcow2Cache *c); ++int GRAPH_RDLOCK qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c); ++ ++int GRAPH_RDLOCK ++qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table); ++ ++int GRAPH_RDLOCK ++qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table); ++ ++void qcow2_cache_put(Qcow2Cache *c, void **table); ++void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset); ++void qcow2_cache_discard(Qcow2Cache *c, void *table); ++ ++/* qcow2-bitmap.c functions */ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size); ++ ++bool coroutine_fn GRAPH_RDLOCK ++qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated, ++ Error **errp); ++ ++bool GRAPH_RDLOCK ++qcow2_get_bitmap_info_list(BlockDriverState *bs, ++ Qcow2BitmapInfoList **info_list, Error **errp); ++ ++int GRAPH_RDLOCK qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); ++int GRAPH_RDLOCK qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); ++ ++bool GRAPH_RDLOCK ++qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, bool release_stored, ++ Error **errp); ++ ++bool coroutine_fn GRAPH_RDLOCK ++qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp); ++ ++int coroutine_fn GRAPH_RDLOCK ++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); ++ ++ssize_t coroutine_fn ++qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size); ++ssize_t coroutine_fn ++qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size); ++int coroutine_fn ++qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len); ++int coroutine_fn ++qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len); ++ ++#endif +diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h +new file mode 100644 +index 00000000..7a3f2e65 +--- /dev/null ++++ b/include/qemu/atomic.h +@@ -0,0 +1,284 @@ ++/* ++ * Simple interface for atomic operations. ++ * ++ * Copyright (C) 2013 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ * See docs/devel/atomics.rst for discussion about the guarantees each ++ * atomic primitive is meant to provide. ++ */ ++ ++#ifndef QEMU_ATOMIC_H ++#define QEMU_ATOMIC_H ++ ++#include "compiler.h" ++ ++/* Compiler barrier */ ++#define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) ++ ++#ifndef __ATOMIC_RELAXED ++#error "Expecting C11 atomic ops" ++#endif ++ ++/* Manual memory barriers ++ * ++ *__atomic_thread_fence does not include a compiler barrier; instead, ++ * the barrier is part of __atomic_load/__atomic_store's "volatile-like" ++ * semantics. If smp_wmb() is a no-op, absence of the barrier means that ++ * the compiler is free to reorder stores on each side of the barrier. ++ * Add one here, and similarly in smp_rmb() and smp_read_barrier_depends(). ++ */ ++ ++#define smp_mb() ({ barrier(); __atomic_thread_fence(__ATOMIC_SEQ_CST); }) ++#define smp_mb_release() ({ barrier(); __atomic_thread_fence(__ATOMIC_RELEASE); }) ++#define smp_mb_acquire() ({ barrier(); __atomic_thread_fence(__ATOMIC_ACQUIRE); }) ++ ++/* Most compilers currently treat consume and acquire the same, but really ++ * no processors except Alpha need a barrier here. Leave it in if ++ * using Thread Sanitizer to avoid warnings, otherwise optimize it away. ++ */ ++#ifdef QEMU_SANITIZE_THREAD ++#define smp_read_barrier_depends() ({ barrier(); __atomic_thread_fence(__ATOMIC_CONSUME); }) ++#elif defined(__alpha__) ++#define smp_read_barrier_depends() asm volatile("mb":::"memory") ++#else ++#define smp_read_barrier_depends() barrier() ++#endif ++ ++/* ++ * A signal barrier forces all pending local memory ops to be observed before ++ * a SIGSEGV is delivered to the *same* thread. In practice this is exactly ++ * the same as barrier(), but since we have the correct builtin, use it. ++ */ ++#define signal_barrier() __atomic_signal_fence(__ATOMIC_SEQ_CST) ++ ++/* Sanity check that the size of an atomic operation isn't "overly large". ++ * Despite the fact that e.g. i686 has 64-bit atomic operations, we do not ++ * want to use them because we ought not need them, and this lets us do a ++ * bit of sanity checking that other 32-bit hosts might build. ++ * ++ * That said, we have a problem on 64-bit ILP32 hosts in that in order to ++ * sync with TCG_OVERSIZED_GUEST, this must match TCG_TARGET_REG_BITS. ++ * We'd prefer not want to pull in everything else TCG related, so handle ++ * those few cases by hand. ++ * ++ * Note that x32 is fully detected with __x86_64__ + _ILP32, and that for ++ * Sparc we always force the use of sparcv9 in configure. MIPS n32 (ILP32) & ++ * n64 (LP64) ABIs are both detected using __mips64. ++ */ ++#if defined(__x86_64__) || defined(__sparc__) || defined(__mips64) ++# define ATOMIC_REG_SIZE 8 ++#else ++# define ATOMIC_REG_SIZE sizeof(void *) ++#endif ++ ++/* Weak atomic operations prevent the compiler moving other ++ * loads/stores past the atomic operation load/store. However there is ++ * no explicit memory barrier for the processor. ++ * ++ * The C11 memory model says that variables that are accessed from ++ * different threads should at least be done with __ATOMIC_RELAXED ++ * primitives or the result is undefined. Generally this has little to ++ * no effect on the generated code but not using the atomic primitives ++ * will get flagged by sanitizers as a violation. ++ */ ++#define qatomic_read__nocheck(ptr) \ ++ __atomic_load_n(ptr, __ATOMIC_RELAXED) ++ ++#define qatomic_read(ptr) \ ++ ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ qatomic_read__nocheck(ptr); \ ++ }) ++ ++#define qatomic_set__nocheck(ptr, i) \ ++ __atomic_store_n(ptr, i, __ATOMIC_RELAXED) ++ ++#define qatomic_set(ptr, i) do { \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ qatomic_set__nocheck(ptr, i); \ ++} while(0) ++ ++/* See above: most compilers currently treat consume and acquire the ++ * same, but this slows down qatomic_rcu_read unnecessarily. ++ */ ++#ifdef QEMU_SANITIZE_THREAD ++#define qatomic_rcu_read__nocheck(ptr, valptr) \ ++ __atomic_load(ptr, valptr, __ATOMIC_CONSUME); ++#else ++#define qatomic_rcu_read__nocheck(ptr, valptr) \ ++ __atomic_load(ptr, valptr, __ATOMIC_RELAXED); \ ++ smp_read_barrier_depends(); ++#endif ++ ++/* ++ * Preprocessor sorcery ahead: use a different identifier for the ++ * local variable in each expansion, so we can nest macro calls ++ * without shadowing variables. ++ */ ++#define qatomic_rcu_read_internal(ptr, _val) \ ++ ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ typeof_strip_qual(*ptr) _val; \ ++ qatomic_rcu_read__nocheck(ptr, &_val); \ ++ _val; \ ++ }) ++#define qatomic_rcu_read(ptr) \ ++ qatomic_rcu_read_internal((ptr), MAKE_IDENTIFIER(_val)) ++ ++#define qatomic_rcu_set(ptr, i) do { \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ ++} while(0) ++ ++#define qatomic_load_acquire(ptr) \ ++ ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ typeof_strip_qual(*ptr) _val; \ ++ __atomic_load(ptr, &_val, __ATOMIC_ACQUIRE); \ ++ _val; \ ++ }) ++ ++#define qatomic_store_release(ptr, i) do { \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ __atomic_store_n(ptr, i, __ATOMIC_RELEASE); \ ++} while(0) ++ ++ ++/* All the remaining operations are fully sequentially consistent */ ++ ++#define qatomic_xchg__nocheck(ptr, i) ({ \ ++ __atomic_exchange_n(ptr, (i), __ATOMIC_SEQ_CST); \ ++}) ++ ++#define qatomic_xchg(ptr, i) ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ qatomic_xchg__nocheck(ptr, i); \ ++}) ++ ++/* Returns the old value of '*ptr' (whether the cmpxchg failed or not) */ ++#define qatomic_cmpxchg__nocheck(ptr, old, new) ({ \ ++ typeof_strip_qual(*ptr) _old = (old); \ ++ (void)__atomic_compare_exchange_n(ptr, &_old, new, false, \ ++ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ ++ _old; \ ++}) ++ ++#define qatomic_cmpxchg(ptr, old, new) ({ \ ++ qemu_build_assert(sizeof(*ptr) <= ATOMIC_REG_SIZE); \ ++ qatomic_cmpxchg__nocheck(ptr, old, new); \ ++}) ++ ++/* Provide shorter names for GCC atomic builtins, return old value */ ++#define qatomic_fetch_inc(ptr) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_dec(ptr) __atomic_fetch_sub(ptr, 1, __ATOMIC_SEQ_CST) ++ ++#define qatomic_fetch_add(ptr, n) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_sub(ptr, n) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_and(ptr, n) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_or(ptr, n) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_fetch_xor(ptr, n) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST) ++ ++#define qatomic_inc_fetch(ptr) __atomic_add_fetch(ptr, 1, __ATOMIC_SEQ_CST) ++#define qatomic_dec_fetch(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST) ++#define qatomic_add_fetch(ptr, n) __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_sub_fetch(ptr, n) __atomic_sub_fetch(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_and_fetch(ptr, n) __atomic_and_fetch(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_or_fetch(ptr, n) __atomic_or_fetch(ptr, n, __ATOMIC_SEQ_CST) ++#define qatomic_xor_fetch(ptr, n) __atomic_xor_fetch(ptr, n, __ATOMIC_SEQ_CST) ++ ++/* And even shorter names that return void. */ ++#define qatomic_inc(ptr) \ ++ ((void) __atomic_fetch_add(ptr, 1, __ATOMIC_SEQ_CST)) ++#define qatomic_dec(ptr) \ ++ ((void) __atomic_fetch_sub(ptr, 1, __ATOMIC_SEQ_CST)) ++#define qatomic_add(ptr, n) \ ++ ((void) __atomic_fetch_add(ptr, n, __ATOMIC_SEQ_CST)) ++#define qatomic_sub(ptr, n) \ ++ ((void) __atomic_fetch_sub(ptr, n, __ATOMIC_SEQ_CST)) ++#define qatomic_and(ptr, n) \ ++ ((void) __atomic_fetch_and(ptr, n, __ATOMIC_SEQ_CST)) ++#define qatomic_or(ptr, n) \ ++ ((void) __atomic_fetch_or(ptr, n, __ATOMIC_SEQ_CST)) ++#define qatomic_xor(ptr, n) \ ++ ((void) __atomic_fetch_xor(ptr, n, __ATOMIC_SEQ_CST)) ++ ++#define smp_wmb() smp_mb_release() ++#define smp_rmb() smp_mb_acquire() ++ ++/* ++ * SEQ_CST is weaker than the older __sync_* builtins and Linux ++ * kernel read-modify-write atomics. Provide a macro to obtain ++ * the same semantics. ++ */ ++#if !defined(QEMU_SANITIZE_THREAD) && \ ++ (defined(__i386__) || defined(__x86_64__) || defined(__s390x__)) ++# define smp_mb__before_rmw() signal_barrier() ++# define smp_mb__after_rmw() signal_barrier() ++#else ++# define smp_mb__before_rmw() smp_mb() ++# define smp_mb__after_rmw() smp_mb() ++#endif ++ ++/* ++ * On some architectures, qatomic_set_mb is more efficient than a store ++ * plus a fence. ++ */ ++ ++#if !defined(QEMU_SANITIZE_THREAD) && \ ++ (defined(__i386__) || defined(__x86_64__) || defined(__s390x__)) ++# define qatomic_set_mb(ptr, i) \ ++ ({ (void)qatomic_xchg(ptr, i); smp_mb__after_rmw(); }) ++#else ++# define qatomic_set_mb(ptr, i) \ ++ ({ qatomic_store_release(ptr, i); smp_mb(); }) ++#endif ++ ++#define qatomic_fetch_inc_nonzero(ptr) ({ \ ++ typeof_strip_qual(*ptr) _oldn = qatomic_read(ptr); \ ++ while (_oldn && qatomic_cmpxchg(ptr, _oldn, _oldn + 1) != _oldn) { \ ++ _oldn = qatomic_read(ptr); \ ++ } \ ++ _oldn; \ ++}) ++ ++/* ++ * Abstractions to access atomically (i.e. "once") i64/u64 variables. ++ * ++ * The i386 abi is odd in that by default members are only aligned to ++ * 4 bytes, which means that 8-byte types can wind up mis-aligned. ++ * Clang will then warn about this, and emit a call into libatomic. ++ * ++ * Use of these types in structures when they will be used with atomic ++ * operations can avoid this. ++ */ ++typedef int64_t aligned_int64_t __attribute__((aligned(8))); ++typedef uint64_t aligned_uint64_t __attribute__((aligned(8))); ++ ++#ifdef CONFIG_ATOMIC64 ++/* Use __nocheck because sizeof(void *) might be < sizeof(u64) */ ++#define qatomic_read_i64(P) \ ++ _Generic(*(P), int64_t: qatomic_read__nocheck(P)) ++#define qatomic_read_u64(P) \ ++ _Generic(*(P), uint64_t: qatomic_read__nocheck(P)) ++#define qatomic_set_i64(P, V) \ ++ _Generic(*(P), int64_t: qatomic_set__nocheck(P, V)) ++#define qatomic_set_u64(P, V) \ ++ _Generic(*(P), uint64_t: qatomic_set__nocheck(P, V)) ++ ++static inline void qatomic64_init(void) ++{ ++} ++#else /* !CONFIG_ATOMIC64 */ ++int64_t qatomic_read_i64(const int64_t *ptr); ++uint64_t qatomic_read_u64(const uint64_t *ptr); ++void qatomic_set_i64(int64_t *ptr, int64_t val); ++void qatomic_set_u64(uint64_t *ptr, uint64_t val); ++void qatomic64_init(void); ++#endif /* !CONFIG_ATOMIC64 */ ++ ++#endif /* QEMU_ATOMIC_H */ +diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h +new file mode 100644 +index 00000000..1cf28844 +--- /dev/null ++++ b/include/qemu/bitmap.h +@@ -0,0 +1,281 @@ ++/* ++ * Bitmap Module ++ * ++ * Copyright (C) 2010 Corentin Chary ++ * ++ * Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BITMAP_H ++#define BITMAP_H ++ ++ ++#include "qemu/bitops.h" ++ ++/* ++ * The available bitmap operations and their rough meaning in the ++ * case that the bitmap is a single unsigned long are thus: ++ * ++ * Note that nbits should be always a compile time evaluable constant. ++ * Otherwise many inlines will generate horrible code. ++ * ++ * bitmap_zero(dst, nbits) *dst = 0UL ++ * bitmap_fill(dst, nbits) *dst = ~0UL ++ * bitmap_copy(dst, src, nbits) *dst = *src ++ * bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2 ++ * bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2 ++ * bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2 ++ * bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2) ++ * bitmap_complement(dst, src, nbits) *dst = ~(*src) ++ * bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal? ++ * bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap? ++ * bitmap_empty(src, nbits) Are all bits zero in *src? ++ * bitmap_full(src, nbits) Are all bits set in *src? ++ * bitmap_set(dst, pos, nbits) Set specified bit area ++ * bitmap_set_atomic(dst, pos, nbits) Set specified bit area with atomic ops ++ * bitmap_clear(dst, pos, nbits) Clear specified bit area ++ * bitmap_test_and_clear_atomic(dst, pos, nbits) Test and clear area ++ * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area ++ * bitmap_to_le(dst, src, nbits) Convert bitmap to little endian ++ * bitmap_from_le(dst, src, nbits) Convert bitmap from little endian ++ * bitmap_copy_with_src_offset(dst, src, offset, nbits) ++ * *dst = *src (with an offset into src) ++ * bitmap_copy_with_dst_offset(dst, src, offset, nbits) ++ * *dst = *src (with an offset into dst) ++ */ ++ ++/* ++ * Also the following operations apply to bitmaps. ++ * ++ * set_bit(bit, addr) *addr |= bit ++ * clear_bit(bit, addr) *addr &= ~bit ++ * change_bit(bit, addr) *addr ^= bit ++ * test_bit(bit, addr) Is bit set in *addr? ++ * test_and_set_bit(bit, addr) Set bit and return old value ++ * test_and_clear_bit(bit, addr) Clear bit and return old value ++ * test_and_change_bit(bit, addr) Change bit and return old value ++ * find_first_zero_bit(addr, nbits) Position first zero bit in *addr ++ * find_first_bit(addr, nbits) Position first set bit in *addr ++ * find_next_zero_bit(addr, nbits, bit) Position next zero bit in *addr >= bit ++ * find_next_bit(addr, nbits, bit) Position next set bit in *addr >= bit ++ */ ++ ++#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) ++#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1))) ++ ++#define DECLARE_BITMAP(name,bits) \ ++ unsigned long name[BITS_TO_LONGS(bits)] ++ ++#define small_nbits(nbits) \ ++ ((nbits) <= BITS_PER_LONG) ++ ++int slow_bitmap_empty(const unsigned long *bitmap, long bits); ++int slow_bitmap_full(const unsigned long *bitmap, long bits); ++int slow_bitmap_equal(const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, ++ long bits); ++int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++int slow_bitmap_intersects(const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits); ++long slow_bitmap_count_one(const unsigned long *bitmap, long nbits); ++ ++static inline unsigned long *bitmap_try_new(long nbits) ++{ ++ long nelem = BITS_TO_LONGS(nbits); ++ return g_try_new0(unsigned long, nelem); ++} ++ ++static inline unsigned long *bitmap_new(long nbits) ++{ ++ long nelem = BITS_TO_LONGS(nbits); ++ return g_new0(unsigned long, nelem); ++} ++ ++static inline void bitmap_zero(unsigned long *dst, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = 0UL; ++ } else { ++ long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); ++ memset(dst, 0, len); ++ } ++} ++ ++static inline void bitmap_fill(unsigned long *dst, long nbits) ++{ ++ size_t nlongs = BITS_TO_LONGS(nbits); ++ if (!small_nbits(nbits)) { ++ long len = (nlongs - 1) * sizeof(unsigned long); ++ memset(dst, 0xff, len); ++ } ++ dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits); ++} ++ ++static inline void bitmap_copy(unsigned long *dst, const unsigned long *src, ++ long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = *src; ++ } else { ++ long len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); ++ memcpy(dst, src, len); ++ } ++} ++ ++static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return (*dst = *src1 & *src2) != 0; ++ } ++ return slow_bitmap_and(dst, src1, src2, nbits); ++} ++ ++static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = *src1 | *src2; ++ } else { ++ slow_bitmap_or(dst, src1, src2, nbits); ++ } ++} ++ ++static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = *src1 ^ *src2; ++ } else { ++ slow_bitmap_xor(dst, src1, src2, nbits); ++ } ++} ++ ++static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return (*dst = *src1 & ~(*src2)) != 0; ++ } ++ return slow_bitmap_andnot(dst, src1, src2, nbits); ++} ++ ++static inline void bitmap_complement(unsigned long *dst, ++ const unsigned long *src, ++ long nbits) ++{ ++ if (small_nbits(nbits)) { ++ *dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits); ++ } else { ++ slow_bitmap_complement(dst, src, nbits); ++ } ++} ++ ++static inline int bitmap_equal(const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits)); ++ } else { ++ return slow_bitmap_equal(src1, src2, nbits); ++ } ++} ++ ++static inline int bitmap_empty(const unsigned long *src, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return ! (*src & BITMAP_LAST_WORD_MASK(nbits)); ++ } else { ++ return slow_bitmap_empty(src, nbits); ++ } ++} ++ ++static inline int bitmap_full(const unsigned long *src, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits)); ++ } else { ++ return slow_bitmap_full(src, nbits); ++ } ++} ++ ++static inline int bitmap_intersects(const unsigned long *src1, ++ const unsigned long *src2, long nbits) ++{ ++ if (small_nbits(nbits)) { ++ return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0; ++ } else { ++ return slow_bitmap_intersects(src1, src2, nbits); ++ } ++} ++ ++static inline long bitmap_count_one(const unsigned long *bitmap, long nbits) ++{ ++ if (unlikely(!nbits)) { ++ return 0; ++ } ++ ++ if (small_nbits(nbits)) { ++ return ctpopl(*bitmap & BITMAP_LAST_WORD_MASK(nbits)); ++ } else { ++ return slow_bitmap_count_one(bitmap, nbits); ++ } ++} ++ ++static inline long bitmap_count_one_with_offset(const unsigned long *bitmap, ++ long offset, long nbits) ++{ ++ long aligned_offset = QEMU_ALIGN_DOWN(offset, BITS_PER_LONG); ++ long redundant_bits = offset - aligned_offset; ++ long bits_to_count = nbits + redundant_bits; ++ const unsigned long *bitmap_start = bitmap + ++ aligned_offset / BITS_PER_LONG; ++ ++ return bitmap_count_one(bitmap_start, bits_to_count) - ++ bitmap_count_one(bitmap_start, redundant_bits); ++} ++ ++void bitmap_set(unsigned long *map, long i, long len); ++void bitmap_set_atomic(unsigned long *map, long i, long len); ++void bitmap_clear(unsigned long *map, long start, long nr); ++bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr); ++bool bitmap_test_and_clear(unsigned long *map, long start, long nr); ++void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, ++ long nr); ++unsigned long bitmap_find_next_zero_area(unsigned long *map, ++ unsigned long size, ++ unsigned long start, ++ unsigned long nr, ++ unsigned long align_mask); ++ ++static inline unsigned long *bitmap_zero_extend(unsigned long *old, ++ long old_nbits, long new_nbits) ++{ ++ long new_nelem = BITS_TO_LONGS(new_nbits); ++ unsigned long *ptr = g_renew(unsigned long, old, new_nelem); ++ bitmap_clear(ptr, old_nbits, new_nbits - old_nbits); ++ return ptr; ++} ++ ++void bitmap_to_le(unsigned long *dst, const unsigned long *src, ++ long nbits); ++void bitmap_from_le(unsigned long *dst, const unsigned long *src, ++ long nbits); ++ ++void bitmap_copy_with_src_offset(unsigned long *dst, const unsigned long *src, ++ unsigned long offset, unsigned long nbits); ++void bitmap_copy_with_dst_offset(unsigned long *dst, const unsigned long *src, ++ unsigned long shift, unsigned long nbits); ++ ++#endif /* BITMAP_H */ +diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h +new file mode 100644 +index 00000000..2c0a2fe7 +--- /dev/null ++++ b/include/qemu/bitops.h +@@ -0,0 +1,634 @@ ++/* ++ * Bitops Module ++ * ++ * Copyright (C) 2010 Corentin Chary ++ * ++ * Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BITOPS_H ++#define BITOPS_H ++ ++ ++#include "host-utils.h" ++#include "atomic.h" ++ ++#define BITS_PER_BYTE CHAR_BIT ++#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) ++ ++#define BIT(nr) (1UL << (nr)) ++#define BIT_ULL(nr) (1ULL << (nr)) ++#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) ++#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) ++#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) ++ ++#define MAKE_64BIT_MASK(shift, length) \ ++ (((~0ULL) >> (64 - (length))) << (shift)) ++ ++/** ++ * set_bit - Set a bit in memory ++ * @nr: the bit to set ++ * @addr: the address to start counting from ++ */ ++static inline void set_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ *p |= mask; ++} ++ ++/** ++ * set_bit_atomic - Set a bit in memory atomically ++ * @nr: the bit to set ++ * @addr: the address to start counting from ++ */ ++static inline void set_bit_atomic(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ qatomic_or(p, mask); ++} ++ ++/** ++ * clear_bit - Clears a bit in memory ++ * @nr: Bit to clear ++ * @addr: Address to start counting from ++ */ ++static inline void clear_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ *p &= ~mask; ++} ++ ++/** ++ * clear_bit_atomic - Clears a bit in memory atomically ++ * @nr: Bit to clear ++ * @addr: Address to start counting from ++ */ ++static inline void clear_bit_atomic(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ return qatomic_and(p, ~mask); ++} ++ ++/** ++ * change_bit - Toggle a bit in memory ++ * @nr: Bit to change ++ * @addr: Address to start counting from ++ */ ++static inline void change_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ ++ *p ^= mask; ++} ++ ++/** ++ * test_and_set_bit - Set a bit and return its old value ++ * @nr: Bit to set ++ * @addr: Address to count from ++ */ ++static inline int test_and_set_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ unsigned long old = *p; ++ ++ *p = old | mask; ++ return (old & mask) != 0; ++} ++ ++/** ++ * test_and_clear_bit - Clear a bit and return its old value ++ * @nr: Bit to clear ++ * @addr: Address to count from ++ */ ++static inline int test_and_clear_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ unsigned long old = *p; ++ ++ *p = old & ~mask; ++ return (old & mask) != 0; ++} ++ ++/** ++ * test_and_change_bit - Change a bit and return its old value ++ * @nr: Bit to change ++ * @addr: Address to count from ++ */ ++static inline int test_and_change_bit(long nr, unsigned long *addr) ++{ ++ unsigned long mask = BIT_MASK(nr); ++ unsigned long *p = addr + BIT_WORD(nr); ++ unsigned long old = *p; ++ ++ *p = old ^ mask; ++ return (old & mask) != 0; ++} ++ ++/** ++ * test_bit - Determine whether a bit is set ++ * @nr: bit number to test ++ * @addr: Address to start counting from ++ */ ++static inline int test_bit(long nr, const unsigned long *addr) ++{ ++ return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); ++} ++ ++/** ++ * find_last_bit - find the last set bit in a memory region ++ * @addr: The address to start the search at ++ * @size: The maximum size to search ++ * ++ * Returns the bit number of the last set bit, ++ * or @size if there is no set bit in the bitmap. ++ */ ++unsigned long find_last_bit(const unsigned long *addr, ++ unsigned long size); ++ ++/** ++ * find_next_bit - find the next set bit in a memory region ++ * @addr: The address to base the search on ++ * @offset: The bitnumber to start searching at ++ * @size: The bitmap size in bits ++ * ++ * Returns the bit number of the next set bit, ++ * or @size if there are no further set bits in the bitmap. ++ */ ++unsigned long find_next_bit(const unsigned long *addr, ++ unsigned long size, ++ unsigned long offset); ++ ++/** ++ * find_next_zero_bit - find the next cleared bit in a memory region ++ * @addr: The address to base the search on ++ * @offset: The bitnumber to start searching at ++ * @size: The bitmap size in bits ++ * ++ * Returns the bit number of the next cleared bit, ++ * or @size if there are no further clear bits in the bitmap. ++ */ ++ ++unsigned long find_next_zero_bit(const unsigned long *addr, ++ unsigned long size, ++ unsigned long offset); ++ ++/** ++ * find_first_bit - find the first set bit in a memory region ++ * @addr: The address to start the search at ++ * @size: The maximum size to search ++ * ++ * Returns the bit number of the first set bit, ++ * or @size if there is no set bit in the bitmap. ++ */ ++static inline unsigned long find_first_bit(const unsigned long *addr, ++ unsigned long size) ++{ ++ unsigned long result, tmp; ++ ++ for (result = 0; result < size; result += BITS_PER_LONG) { ++ tmp = *addr++; ++ if (tmp) { ++ result += ctzl(tmp); ++ return result < size ? result : size; ++ } ++ } ++ /* Not found */ ++ return size; ++} ++ ++/** ++ * find_first_zero_bit - find the first cleared bit in a memory region ++ * @addr: The address to start the search at ++ * @size: The maximum size to search ++ * ++ * Returns the bit number of the first cleared bit, ++ * or @size if there is no clear bit in the bitmap. ++ */ ++static inline unsigned long find_first_zero_bit(const unsigned long *addr, ++ unsigned long size) ++{ ++ return find_next_zero_bit(addr, size, 0); ++} ++ ++/** ++ * rol8 - rotate an 8-bit value left ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint8_t rol8(uint8_t word, unsigned int shift) ++{ ++ return (word << (shift & 7)) | (word >> (-shift & 7)); ++} ++ ++/** ++ * ror8 - rotate an 8-bit value right ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint8_t ror8(uint8_t word, unsigned int shift) ++{ ++ return (word >> (shift & 7)) | (word << (-shift & 7)); ++} ++ ++/** ++ * rol16 - rotate a 16-bit value left ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint16_t rol16(uint16_t word, unsigned int shift) ++{ ++ return (word << (shift & 15)) | (word >> (-shift & 15)); ++} ++ ++/** ++ * ror16 - rotate a 16-bit value right ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint16_t ror16(uint16_t word, unsigned int shift) ++{ ++ return (word >> (shift & 15)) | (word << (-shift & 15)); ++} ++ ++/** ++ * rol32 - rotate a 32-bit value left ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint32_t rol32(uint32_t word, unsigned int shift) ++{ ++ return (word << (shift & 31)) | (word >> (-shift & 31)); ++} ++ ++/** ++ * ror32 - rotate a 32-bit value right ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint32_t ror32(uint32_t word, unsigned int shift) ++{ ++ return (word >> (shift & 31)) | (word << (-shift & 31)); ++} ++ ++/** ++ * rol64 - rotate a 64-bit value left ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint64_t rol64(uint64_t word, unsigned int shift) ++{ ++ return (word << (shift & 63)) | (word >> (-shift & 63)); ++} ++ ++/** ++ * ror64 - rotate a 64-bit value right ++ * @word: value to rotate ++ * @shift: bits to roll ++ */ ++static inline uint64_t ror64(uint64_t word, unsigned int shift) ++{ ++ return (word >> (shift & 63)) | (word << (-shift & 63)); ++} ++ ++/** ++ * hswap32 - swap 16-bit halfwords within a 32-bit value ++ * @h: value to swap ++ */ ++static inline uint32_t hswap32(uint32_t h) ++{ ++ return rol32(h, 16); ++} ++ ++/** ++ * hswap64 - swap 16-bit halfwords within a 64-bit value ++ * @h: value to swap ++ */ ++static inline uint64_t hswap64(uint64_t h) ++{ ++ uint64_t m = 0x0000ffff0000ffffull; ++ h = rol64(h, 32); ++ return ((h & m) << 16) | ((h >> 16) & m); ++} ++ ++/** ++ * wswap64 - swap 32-bit words within a 64-bit value ++ * @h: value to swap ++ */ ++static inline uint64_t wswap64(uint64_t h) ++{ ++ return rol64(h, 32); ++} ++ ++/** ++ * extract32: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 32 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it. The bit field must ++ * lie entirely within the 32 bit word. It is valid to request that ++ * all 32 bits are returned (ie @length 32 and @start 0). ++ * ++ * Returns: the value of the bit field extracted from the input value. ++ */ ++static inline uint32_t extract32(uint32_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 32 - start); ++ return (value >> start) & (~0U >> (32 - length)); ++} ++ ++/** ++ * extract8: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 8 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it. The bit field must ++ * lie entirely within the 8 bit word. It is valid to request that ++ * all 8 bits are returned (ie @length 8 and @start 0). ++ * ++ * Returns: the value of the bit field extracted from the input value. ++ */ ++static inline uint8_t extract8(uint8_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 8 - start); ++ return extract32(value, start, length); ++} ++ ++/** ++ * extract16: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 16 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it. The bit field must ++ * lie entirely within the 16 bit word. It is valid to request that ++ * all 16 bits are returned (ie @length 16 and @start 0). ++ * ++ * Returns: the value of the bit field extracted from the input value. ++ */ ++static inline uint16_t extract16(uint16_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 16 - start); ++ return extract32(value, start, length); ++} ++ ++/** ++ * extract64: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 64 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it. The bit field must ++ * lie entirely within the 64 bit word. It is valid to request that ++ * all 64 bits are returned (ie @length 64 and @start 0). ++ * ++ * Returns: the value of the bit field extracted from the input value. ++ */ ++static inline uint64_t extract64(uint64_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 64 - start); ++ return (value >> start) & (~0ULL >> (64 - length)); ++} ++ ++/** ++ * sextract32: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 32 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it, sign extended to ++ * an int32_t (ie with the most significant bit of the field propagated ++ * to all the upper bits of the return value). The bit field must lie ++ * entirely within the 32 bit word. It is valid to request that ++ * all 32 bits are returned (ie @length 32 and @start 0). ++ * ++ * Returns: the sign extended value of the bit field extracted from the ++ * input value. ++ */ ++static inline int32_t sextract32(uint32_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 32 - start); ++ /* Note that this implementation relies on right shift of signed ++ * integers being an arithmetic shift. ++ */ ++ return ((int32_t)(value << (32 - length - start))) >> (32 - length); ++} ++ ++/** ++ * sextract64: ++ * @value: the value to extract the bit field from ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * ++ * Extract from the 64 bit input @value the bit field specified by the ++ * @start and @length parameters, and return it, sign extended to ++ * an int64_t (ie with the most significant bit of the field propagated ++ * to all the upper bits of the return value). The bit field must lie ++ * entirely within the 64 bit word. It is valid to request that ++ * all 64 bits are returned (ie @length 64 and @start 0). ++ * ++ * Returns: the sign extended value of the bit field extracted from the ++ * input value. ++ */ ++static inline int64_t sextract64(uint64_t value, int start, int length) ++{ ++ assert(start >= 0 && length > 0 && length <= 64 - start); ++ /* Note that this implementation relies on right shift of signed ++ * integers being an arithmetic shift. ++ */ ++ return ((int64_t)(value << (64 - length - start))) >> (64 - length); ++} ++ ++/** ++ * deposit32: ++ * @value: initial value to insert bit field into ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * @fieldval: the value to insert into the bit field ++ * ++ * Deposit @fieldval into the 32 bit @value at the bit field specified ++ * by the @start and @length parameters, and return the modified ++ * @value. Bits of @value outside the bit field are not modified. ++ * Bits of @fieldval above the least significant @length bits are ++ * ignored. The bit field must lie entirely within the 32 bit word. ++ * It is valid to request that all 32 bits are modified (ie @length ++ * 32 and @start 0). ++ * ++ * Returns: the modified @value. ++ */ ++static inline uint32_t deposit32(uint32_t value, int start, int length, ++ uint32_t fieldval) ++{ ++ uint32_t mask; ++ assert(start >= 0 && length > 0 && length <= 32 - start); ++ mask = (~0U >> (32 - length)) << start; ++ return (value & ~mask) | ((fieldval << start) & mask); ++} ++ ++/** ++ * deposit64: ++ * @value: initial value to insert bit field into ++ * @start: the lowest bit in the bit field (numbered from 0) ++ * @length: the length of the bit field ++ * @fieldval: the value to insert into the bit field ++ * ++ * Deposit @fieldval into the 64 bit @value at the bit field specified ++ * by the @start and @length parameters, and return the modified ++ * @value. Bits of @value outside the bit field are not modified. ++ * Bits of @fieldval above the least significant @length bits are ++ * ignored. The bit field must lie entirely within the 64 bit word. ++ * It is valid to request that all 64 bits are modified (ie @length ++ * 64 and @start 0). ++ * ++ * Returns: the modified @value. ++ */ ++static inline uint64_t deposit64(uint64_t value, int start, int length, ++ uint64_t fieldval) ++{ ++ uint64_t mask; ++ assert(start >= 0 && length > 0 && length <= 64 - start); ++ mask = (~0ULL >> (64 - length)) << start; ++ return (value & ~mask) | ((fieldval << start) & mask); ++} ++ ++/** ++ * half_shuffle32: ++ * @x: 32-bit value (of which only the bottom 16 bits are of interest) ++ * ++ * Given an input value:: ++ * ++ * xxxx xxxx xxxx xxxx ABCD EFGH IJKL MNOP ++ * ++ * return the value where the bottom 16 bits are spread out into ++ * the odd bits in the word, and the even bits are zeroed:: ++ * ++ * 0A0B 0C0D 0E0F 0G0H 0I0J 0K0L 0M0N 0O0P ++ * ++ * Any bits set in the top half of the input are ignored. ++ * ++ * Returns: the shuffled bits. ++ */ ++static inline uint32_t half_shuffle32(uint32_t x) ++{ ++ /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". ++ * It ignores any bits set in the top half of the input. ++ */ ++ x = ((x & 0xFF00) << 8) | (x & 0x00FF); ++ x = ((x << 4) | x) & 0x0F0F0F0F; ++ x = ((x << 2) | x) & 0x33333333; ++ x = ((x << 1) | x) & 0x55555555; ++ return x; ++} ++ ++/** ++ * half_shuffle64: ++ * @x: 64-bit value (of which only the bottom 32 bits are of interest) ++ * ++ * Given an input value:: ++ * ++ * xxxx xxxx xxxx .... xxxx xxxx ABCD EFGH IJKL MNOP QRST UVWX YZab cdef ++ * ++ * return the value where the bottom 32 bits are spread out into ++ * the odd bits in the word, and the even bits are zeroed:: ++ * ++ * 0A0B 0C0D 0E0F 0G0H 0I0J 0K0L 0M0N .... 0U0V 0W0X 0Y0Z 0a0b 0c0d 0e0f ++ * ++ * Any bits set in the top half of the input are ignored. ++ * ++ * Returns: the shuffled bits. ++ */ ++static inline uint64_t half_shuffle64(uint64_t x) ++{ ++ /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". ++ * It ignores any bits set in the top half of the input. ++ */ ++ x = ((x & 0xFFFF0000ULL) << 16) | (x & 0xFFFF); ++ x = ((x << 8) | x) & 0x00FF00FF00FF00FFULL; ++ x = ((x << 4) | x) & 0x0F0F0F0F0F0F0F0FULL; ++ x = ((x << 2) | x) & 0x3333333333333333ULL; ++ x = ((x << 1) | x) & 0x5555555555555555ULL; ++ return x; ++} ++ ++/** ++ * half_unshuffle32: ++ * @x: 32-bit value (of which only the odd bits are of interest) ++ * ++ * Given an input value:: ++ * ++ * xAxB xCxD xExF xGxH xIxJ xKxL xMxN xOxP ++ * ++ * return the value where all the odd bits are compressed down ++ * into the low half of the word, and the high half is zeroed:: ++ * ++ * 0000 0000 0000 0000 ABCD EFGH IJKL MNOP ++ * ++ * Any even bits set in the input are ignored. ++ * ++ * Returns: the unshuffled bits. ++ */ ++static inline uint32_t half_unshuffle32(uint32_t x) ++{ ++ /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". ++ * where it is called an inverse half shuffle. ++ */ ++ x &= 0x55555555; ++ x = ((x >> 1) | x) & 0x33333333; ++ x = ((x >> 2) | x) & 0x0F0F0F0F; ++ x = ((x >> 4) | x) & 0x00FF00FF; ++ x = ((x >> 8) | x) & 0x0000FFFF; ++ return x; ++} ++ ++/** ++ * half_unshuffle64: ++ * @x: 64-bit value (of which only the odd bits are of interest) ++ * ++ * Given an input value:: ++ * ++ * xAxB xCxD xExF xGxH xIxJ xKxL xMxN .... xUxV xWxX xYxZ xaxb xcxd xexf ++ * ++ * return the value where all the odd bits are compressed down ++ * into the low half of the word, and the high half is zeroed:: ++ * ++ * 0000 0000 0000 .... 0000 0000 ABCD EFGH IJKL MNOP QRST UVWX YZab cdef ++ * ++ * Any even bits set in the input are ignored. ++ * ++ * Returns: the unshuffled bits. ++ */ ++static inline uint64_t half_unshuffle64(uint64_t x) ++{ ++ /* This algorithm is from _Hacker's Delight_ section 7-2 "Shuffling Bits". ++ * where it is called an inverse half shuffle. ++ */ ++ x &= 0x5555555555555555ULL; ++ x = ((x >> 1) | x) & 0x3333333333333333ULL; ++ x = ((x >> 2) | x) & 0x0F0F0F0F0F0F0F0FULL; ++ x = ((x >> 4) | x) & 0x00FF00FF00FF00FFULL; ++ x = ((x >> 8) | x) & 0x0000FFFF0000FFFFULL; ++ x = ((x >> 16) | x) & 0x00000000FFFFFFFFULL; ++ return x; ++} ++ ++#endif +diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h +new file mode 100644 +index 00000000..ad22910a +--- /dev/null ++++ b/include/qemu/bswap.h +@@ -0,0 +1,436 @@ ++#ifndef BSWAP_H ++#define BSWAP_H ++ ++#undef bswap16 ++#define bswap16(_x) __builtin_bswap16(_x) ++#undef bswap32 ++#define bswap32(_x) __builtin_bswap32(_x) ++#undef bswap64 ++#define bswap64(_x) __builtin_bswap64(_x) ++ ++static inline uint32_t bswap24(uint32_t x) ++{ ++ return (((x & 0x000000ffU) << 16) | ++ ((x & 0x0000ff00U) << 0) | ++ ((x & 0x00ff0000U) >> 16)); ++} ++ ++static inline void bswap16s(uint16_t *s) ++{ ++ *s = __builtin_bswap16(*s); ++} ++ ++static inline void bswap24s(uint32_t *s) ++{ ++ *s = bswap24(*s & 0x00ffffffU); ++} ++ ++static inline void bswap32s(uint32_t *s) ++{ ++ *s = __builtin_bswap32(*s); ++} ++ ++static inline void bswap64s(uint64_t *s) ++{ ++ *s = __builtin_bswap64(*s); ++} ++ ++#if HOST_BIG_ENDIAN ++#define be_bswap(v, size) (v) ++#define le_bswap(v, size) glue(__builtin_bswap, size)(v) ++#define be_bswap24(v) (v) ++#define le_bswap24(v) bswap24(v) ++#define be_bswaps(v, size) ++#define le_bswaps(p, size) \ ++ do { *p = glue(__builtin_bswap, size)(*p); } while (0) ++#else ++#define le_bswap(v, size) (v) ++#define be_bswap24(v) bswap24(v) ++#define le_bswap24(v) (v) ++#define be_bswap(v, size) glue(__builtin_bswap, size)(v) ++#define le_bswaps(v, size) ++#define be_bswaps(p, size) \ ++ do { *p = glue(__builtin_bswap, size)(*p); } while (0) ++#endif ++ ++/** ++ * Endianness conversion functions between host cpu and specified endianness. ++ * (We list the complete set of prototypes produced by the macros below ++ * to assist people who search the headers to find their definitions.) ++ * ++ * uint16_t le16_to_cpu(uint16_t v); ++ * uint32_t le32_to_cpu(uint32_t v); ++ * uint64_t le64_to_cpu(uint64_t v); ++ * uint16_t be16_to_cpu(uint16_t v); ++ * uint32_t be32_to_cpu(uint32_t v); ++ * uint64_t be64_to_cpu(uint64_t v); ++ * ++ * Convert the value @v from the specified format to the native ++ * endianness of the host CPU by byteswapping if necessary, and ++ * return the converted value. ++ * ++ * uint16_t cpu_to_le16(uint16_t v); ++ * uint32_t cpu_to_le32(uint32_t v); ++ * uint64_t cpu_to_le64(uint64_t v); ++ * uint16_t cpu_to_be16(uint16_t v); ++ * uint32_t cpu_to_be32(uint32_t v); ++ * uint64_t cpu_to_be64(uint64_t v); ++ * ++ * Convert the value @v from the native endianness of the host CPU to ++ * the specified format by byteswapping if necessary, and return ++ * the converted value. ++ * ++ * void le16_to_cpus(uint16_t *v); ++ * void le32_to_cpus(uint32_t *v); ++ * void le64_to_cpus(uint64_t *v); ++ * void be16_to_cpus(uint16_t *v); ++ * void be32_to_cpus(uint32_t *v); ++ * void be64_to_cpus(uint64_t *v); ++ * ++ * Do an in-place conversion of the value pointed to by @v from the ++ * specified format to the native endianness of the host CPU. ++ * ++ * void cpu_to_le16s(uint16_t *v); ++ * void cpu_to_le32s(uint32_t *v); ++ * void cpu_to_le64s(uint64_t *v); ++ * void cpu_to_be16s(uint16_t *v); ++ * void cpu_to_be32s(uint32_t *v); ++ * void cpu_to_be64s(uint64_t *v); ++ * ++ * Do an in-place conversion of the value pointed to by @v from the ++ * native endianness of the host CPU to the specified format. ++ * ++ * Both X_to_cpu() and cpu_to_X() perform the same operation; you ++ * should use whichever one is better documenting of the function your ++ * code is performing. ++ * ++ * Do not use these functions for conversion of values which are in guest ++ * memory, since the data may not be sufficiently aligned for the host CPU's ++ * load and store instructions. Instead you should use the ld*_p() and ++ * st*_p() functions, which perform loads and stores of data of any ++ * required size and endianness and handle possible misalignment. ++ */ ++ ++#define CPU_CONVERT(endian, size, type)\ ++static inline type endian ## size ## _to_cpu(type v)\ ++{\ ++ return glue(endian, _bswap)(v, size);\ ++}\ ++\ ++static inline type cpu_to_ ## endian ## size(type v)\ ++{\ ++ return glue(endian, _bswap)(v, size);\ ++}\ ++\ ++static inline void endian ## size ## _to_cpus(type *p)\ ++{\ ++ glue(endian, _bswaps)(p, size);\ ++}\ ++\ ++static inline void cpu_to_ ## endian ## size ## s(type *p)\ ++{\ ++ glue(endian, _bswaps)(p, size);\ ++} ++ ++CPU_CONVERT(be, 16, uint16_t) ++CPU_CONVERT(be, 32, uint32_t) ++CPU_CONVERT(be, 64, uint64_t) ++ ++CPU_CONVERT(le, 16, uint16_t) ++CPU_CONVERT(le, 32, uint32_t) ++CPU_CONVERT(le, 64, uint64_t) ++ ++/* ++ * Same as cpu_to_le{16,32,64}, except that gcc will figure the result is ++ * a compile-time constant if you pass in a constant. So this can be ++ * used to initialize static variables. ++ */ ++#if HOST_BIG_ENDIAN ++# define const_le64(_x) \ ++ ((((_x) & 0x00000000000000ffULL) << 56) | \ ++ (((_x) & 0x000000000000ff00ULL) << 40) | \ ++ (((_x) & 0x0000000000ff0000ULL) << 24) | \ ++ (((_x) & 0x00000000ff000000ULL) << 8) | \ ++ (((_x) & 0x000000ff00000000ULL) >> 8) | \ ++ (((_x) & 0x0000ff0000000000ULL) >> 24) | \ ++ (((_x) & 0x00ff000000000000ULL) >> 40) | \ ++ (((_x) & 0xff00000000000000ULL) >> 56)) ++# define const_le32(_x) \ ++ ((((_x) & 0x000000ffU) << 24) | \ ++ (((_x) & 0x0000ff00U) << 8) | \ ++ (((_x) & 0x00ff0000U) >> 8) | \ ++ (((_x) & 0xff000000U) >> 24)) ++# define const_le16(_x) \ ++ ((((_x) & 0x00ff) << 8) | \ ++ (((_x) & 0xff00) >> 8)) ++#else ++# define const_le64(_x) (_x) ++# define const_le32(_x) (_x) ++# define const_le16(_x) (_x) ++#endif ++ ++/* unaligned/endian-independent pointer access */ ++ ++/* ++ * the generic syntax is: ++ * ++ * load: ld{type}{sign}{size}_{endian}_p(ptr) ++ * ++ * store: st{type}{size}_{endian}_p(ptr, val) ++ * ++ * Note there are small differences with the softmmu access API! ++ * ++ * type is: ++ * (empty): integer access ++ * f : float access ++ * ++ * sign is: ++ * (empty): for 32 or 64 bit sizes (including floats and doubles) ++ * u : unsigned ++ * s : signed ++ * ++ * size is: ++ * b: 8 bits ++ * w: 16 bits ++ * 24: 24 bits ++ * l: 32 bits ++ * q: 64 bits ++ * ++ * endian is: ++ * he : host endian ++ * be : big endian ++ * le : little endian ++ * te : target endian ++ * (except for byte accesses, which have no endian infix). ++ * ++ * The target endian accessors are obviously only available to source ++ * files which are built per-target; they are defined in cpu-all.h. ++ * ++ * In all cases these functions take a host pointer. ++ * For accessors that take a guest address rather than a ++ * host address, see the cpu_{ld,st}_* accessors defined in ++ * cpu_ldst.h. ++ * ++ * For cases where the size to be used is not fixed at compile time, ++ * there are ++ * stn_{endian}_p(ptr, sz, val) ++ * which stores @val to @ptr as an @endian-order number @sz bytes in size ++ * and ++ * ldn_{endian}_p(ptr, sz) ++ * which loads @sz bytes from @ptr as an unsigned @endian-order number ++ * and returns it in a uint64_t. ++ */ ++ ++static inline int ldub_p(const void *ptr) ++{ ++ return *(uint8_t *)ptr; ++} ++ ++static inline int ldsb_p(const void *ptr) ++{ ++ return *(int8_t *)ptr; ++} ++ ++static inline void stb_p(void *ptr, uint8_t v) ++{ ++ *(uint8_t *)ptr = v; ++} ++ ++/* ++ * Any compiler worth its salt will turn these memcpy into native unaligned ++ * operations. Thus we don't need to play games with packed attributes, or ++ * inline byte-by-byte stores. ++ * Some compilation environments (eg some fortify-source implementations) ++ * may intercept memcpy() in a way that defeats the compiler optimization, ++ * though, so we use __builtin_memcpy() to give ourselves the best chance ++ * of good performance. ++ */ ++ ++static inline int lduw_he_p(const void *ptr) ++{ ++ uint16_t r; ++ __builtin_memcpy(&r, ptr, sizeof(r)); ++ return r; ++} ++ ++static inline int ldsw_he_p(const void *ptr) ++{ ++ int16_t r; ++ __builtin_memcpy(&r, ptr, sizeof(r)); ++ return r; ++} ++ ++static inline void stw_he_p(void *ptr, uint16_t v) ++{ ++ __builtin_memcpy(ptr, &v, sizeof(v)); ++} ++ ++static inline void st24_he_p(void *ptr, uint32_t v) ++{ ++ __builtin_memcpy(ptr, &v, 3); ++} ++ ++static inline int ldl_he_p(const void *ptr) ++{ ++ int32_t r; ++ __builtin_memcpy(&r, ptr, sizeof(r)); ++ return r; ++} ++ ++static inline void stl_he_p(void *ptr, uint32_t v) ++{ ++ __builtin_memcpy(ptr, &v, sizeof(v)); ++} ++ ++static inline uint64_t ldq_he_p(const void *ptr) ++{ ++ uint64_t r; ++ __builtin_memcpy(&r, ptr, sizeof(r)); ++ return r; ++} ++ ++static inline void stq_he_p(void *ptr, uint64_t v) ++{ ++ __builtin_memcpy(ptr, &v, sizeof(v)); ++} ++ ++static inline int lduw_le_p(const void *ptr) ++{ ++ return (uint16_t)le_bswap(lduw_he_p(ptr), 16); ++} ++ ++static inline int ldsw_le_p(const void *ptr) ++{ ++ return (int16_t)le_bswap(lduw_he_p(ptr), 16); ++} ++ ++static inline int ldl_le_p(const void *ptr) ++{ ++ return le_bswap(ldl_he_p(ptr), 32); ++} ++ ++static inline uint64_t ldq_le_p(const void *ptr) ++{ ++ return le_bswap(ldq_he_p(ptr), 64); ++} ++ ++static inline void stw_le_p(void *ptr, uint16_t v) ++{ ++ stw_he_p(ptr, le_bswap(v, 16)); ++} ++ ++static inline void st24_le_p(void *ptr, uint32_t v) ++{ ++ st24_he_p(ptr, le_bswap24(v)); ++} ++ ++static inline void stl_le_p(void *ptr, uint32_t v) ++{ ++ stl_he_p(ptr, le_bswap(v, 32)); ++} ++ ++static inline void stq_le_p(void *ptr, uint64_t v) ++{ ++ stq_he_p(ptr, le_bswap(v, 64)); ++} ++ ++static inline int lduw_be_p(const void *ptr) ++{ ++ return (uint16_t)be_bswap(lduw_he_p(ptr), 16); ++} ++ ++static inline int ldsw_be_p(const void *ptr) ++{ ++ return (int16_t)be_bswap(lduw_he_p(ptr), 16); ++} ++ ++static inline int ldl_be_p(const void *ptr) ++{ ++ return be_bswap(ldl_he_p(ptr), 32); ++} ++ ++static inline uint64_t ldq_be_p(const void *ptr) ++{ ++ return be_bswap(ldq_he_p(ptr), 64); ++} ++ ++static inline void stw_be_p(void *ptr, uint16_t v) ++{ ++ stw_he_p(ptr, be_bswap(v, 16)); ++} ++ ++static inline void st24_be_p(void *ptr, uint32_t v) ++{ ++ st24_he_p(ptr, be_bswap24(v)); ++} ++ ++static inline void stl_be_p(void *ptr, uint32_t v) ++{ ++ stl_he_p(ptr, be_bswap(v, 32)); ++} ++ ++static inline void stq_be_p(void *ptr, uint64_t v) ++{ ++ stq_he_p(ptr, be_bswap(v, 64)); ++} ++ ++static inline unsigned long leul_to_cpu(unsigned long v) ++{ ++#if HOST_LONG_BITS == 32 ++ return le_bswap(v, 32); ++#elif HOST_LONG_BITS == 64 ++ return le_bswap(v, 64); ++#else ++# error Unknown sizeof long ++#endif ++} ++ ++/* Store v to p as a sz byte value in host order */ ++#define DO_STN_LDN_P(END) \ ++ static inline void stn_## END ## _p(void *ptr, int sz, uint64_t v) \ ++ { \ ++ switch (sz) { \ ++ case 1: \ ++ stb_p(ptr, v); \ ++ break; \ ++ case 2: \ ++ stw_ ## END ## _p(ptr, v); \ ++ break; \ ++ case 4: \ ++ stl_ ## END ## _p(ptr, v); \ ++ break; \ ++ case 8: \ ++ stq_ ## END ## _p(ptr, v); \ ++ break; \ ++ default: \ ++ g_assert_not_reached(); \ ++ } \ ++ } \ ++ static inline uint64_t ldn_## END ## _p(const void *ptr, int sz) \ ++ { \ ++ switch (sz) { \ ++ case 1: \ ++ return ldub_p(ptr); \ ++ case 2: \ ++ return lduw_ ## END ## _p(ptr); \ ++ case 4: \ ++ return (uint32_t)ldl_ ## END ## _p(ptr); \ ++ case 8: \ ++ return ldq_ ## END ## _p(ptr); \ ++ default: \ ++ g_assert_not_reached(); \ ++ } \ ++ } ++ ++DO_STN_LDN_P(he) ++DO_STN_LDN_P(le) ++DO_STN_LDN_P(be) ++ ++#undef DO_STN_LDN_P ++ ++#undef le_bswap ++#undef be_bswap ++#undef le_bswaps ++#undef be_bswaps ++ ++#endif /* BSWAP_H */ +diff --git a/include/qemu/clang-tsa.h b/include/qemu/clang-tsa.h +new file mode 100644 +index 00000000..ba06fb8c +--- /dev/null ++++ b/include/qemu/clang-tsa.h +@@ -0,0 +1,114 @@ ++#ifndef CLANG_TSA_H ++#define CLANG_TSA_H ++ ++/* ++ * Copyright 2018 Jarkko Hietaniemi ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining ++ * a copy of this software and associated documentation files (the "Software"), ++ * to deal in the Software without restriction, including without ++ * limitation the rights to use, copy, modify, merge, publish, ++ * distribute, sublicense, and/or sell copies of the Software, and to ++ * permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ++ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ++ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ++ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ++ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ */ ++ ++/* http://clang.llvm.org/docs/ThreadSafetyAnalysis.html ++ * ++ * TSA is available since clang 3.6-ish. ++ */ ++#ifdef __clang__ ++# define TSA(x) __attribute__((x)) ++#else ++# define TSA(x) /* No TSA, make TSA attributes no-ops. */ ++#endif ++ ++/* TSA_CAPABILITY() is used to annotate typedefs: ++ * ++ * typedef pthread_mutex_t TSA_CAPABILITY("mutex") tsa_mutex; ++ */ ++#define TSA_CAPABILITY(x) TSA(capability(x)) ++ ++/* TSA_GUARDED_BY() is used to annotate global variables, ++ * the data is guarded: ++ * ++ * Foo foo TSA_GUARDED_BY(mutex); ++ */ ++#define TSA_GUARDED_BY(x) TSA(guarded_by(x)) ++ ++/* TSA_PT_GUARDED_BY() is used to annotate global pointers, the data ++ * behind the pointer is guarded. ++ * ++ * Foo* ptr TSA_PT_GUARDED_BY(mutex); ++ */ ++#define TSA_PT_GUARDED_BY(x) TSA(pt_guarded_by(x)) ++ ++/* The TSA_REQUIRES() is used to annotate functions: the caller of the ++ * function MUST hold the resource, the function will NOT release it. ++ * ++ * More than one mutex may be specified, comma-separated. ++ * ++ * void Foo(void) TSA_REQUIRES(mutex); ++ */ ++#define TSA_REQUIRES(...) TSA(requires_capability(__VA_ARGS__)) ++#define TSA_REQUIRES_SHARED(...) TSA(requires_shared_capability(__VA_ARGS__)) ++ ++/* TSA_EXCLUDES() is used to annotate functions: the caller of the ++ * function MUST NOT hold resource, the function first acquires the ++ * resource, and then releases it. ++ * ++ * More than one mutex may be specified, comma-separated. ++ * ++ * void Foo(void) TSA_EXCLUDES(mutex); ++ */ ++#define TSA_EXCLUDES(...) TSA(locks_excluded(__VA_ARGS__)) ++ ++/* TSA_ACQUIRE() is used to annotate functions: the caller of the ++ * function MUST NOT hold the resource, the function will acquire the ++ * resource, but NOT release it. ++ * ++ * More than one mutex may be specified, comma-separated. ++ * ++ * void Foo(void) TSA_ACQUIRE(mutex); ++ */ ++#define TSA_ACQUIRE(...) TSA(acquire_capability(__VA_ARGS__)) ++#define TSA_ACQUIRE_SHARED(...) TSA(acquire_shared_capability(__VA_ARGS__)) ++ ++/* TSA_RELEASE() is used to annotate functions: the caller of the ++ * function MUST hold the resource, but the function will then release it. ++ * ++ * More than one mutex may be specified, comma-separated. ++ * ++ * void Foo(void) TSA_RELEASE(mutex); ++ */ ++#define TSA_RELEASE(...) TSA(release_capability(__VA_ARGS__)) ++#define TSA_RELEASE_SHARED(...) TSA(release_shared_capability(__VA_ARGS__)) ++ ++/* TSA_NO_TSA is used to annotate functions. Use only when you need to. ++ * ++ * void Foo(void) TSA_NO_TSA; ++ */ ++#define TSA_NO_TSA TSA(no_thread_safety_analysis) ++ ++/* ++ * TSA_ASSERT() is used to annotate functions: This function will assert that ++ * the lock is held. When it returns, the caller of the function is assumed to ++ * already hold the resource. ++ * ++ * More than one mutex may be specified, comma-separated. ++ */ ++#define TSA_ASSERT(...) TSA(assert_capability(__VA_ARGS__)) ++#define TSA_ASSERT_SHARED(...) TSA(assert_shared_capability(__VA_ARGS__)) ++ ++#endif /* #ifndef CLANG_TSA_H */ +diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h +new file mode 100644 +index 00000000..c06954cc +--- /dev/null ++++ b/include/qemu/compiler.h +@@ -0,0 +1,276 @@ ++/* compiler.h: macros to abstract away compiler specifics ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef COMPILER_H ++#define COMPILER_H ++ ++#define HOST_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) ++ ++/* HOST_LONG_BITS is the size of a native pointer in bits. */ ++#define HOST_LONG_BITS (__SIZEOF_POINTER__ * 8) ++ ++#if defined __clang_analyzer__ || defined __COVERITY__ ++#define QEMU_STATIC_ANALYSIS 1 ++#endif ++ ++#ifdef __cplusplus ++#define QEMU_EXTERN_C extern "C" ++#else ++#define QEMU_EXTERN_C extern ++#endif ++ ++#if defined(_WIN32) && (defined(__x86_64__) || defined(__i386__)) ++# define QEMU_PACKED __attribute__((gcc_struct, packed)) ++#else ++# define QEMU_PACKED __attribute__((packed)) ++#endif ++ ++#define QEMU_ALIGNED(X) __attribute__((aligned(X))) ++ ++#ifndef glue ++#define xglue(x, y) x ## y ++#define glue(x, y) xglue(x, y) ++#define stringify(s) tostring(s) ++#define tostring(s) #s ++#endif ++ ++/* Expands into an identifier stemN, where N is another number each time */ ++#define MAKE_IDENTIFIER(stem) glue(stem, __COUNTER__) ++ ++#ifndef likely ++#define likely(x) __builtin_expect(!!(x), 1) ++#define unlikely(x) __builtin_expect(!!(x), 0) ++#endif ++ ++#ifndef container_of ++#define container_of(ptr, type, member) ({ \ ++ const typeof(((type *) 0)->member) *__mptr = (ptr); \ ++ (type *) ((char *) __mptr - offsetof(type, member));}) ++#endif ++ ++#define sizeof_field(type, field) sizeof(((type *)0)->field) ++ ++/* ++ * Calculate the number of bytes up to and including the given 'field' of ++ * 'container'. ++ */ ++#define endof(container, field) \ ++ (offsetof(container, field) + sizeof_field(container, field)) ++ ++/* Convert from a base type to a parent type, with compile time checking. */ ++#define DO_UPCAST(type, field, dev) ( __extension__ ( { \ ++ char __attribute__((unused)) offset_must_be_zero[ \ ++ -offsetof(type, field)]; \ ++ container_of(dev, type, field);})) ++ ++#define typeof_field(type, field) typeof(((type *)0)->field) ++#define type_check(t1,t2) ((t1*)0 - (t2*)0) ++ ++#define QEMU_BUILD_BUG_ON_STRUCT(x) \ ++ struct { \ ++ int:(x) ? -1 : 1; \ ++ } ++ ++#define QEMU_BUILD_BUG_MSG(x, msg) _Static_assert(!(x), msg) ++ ++#define QEMU_BUILD_BUG_ON(x) QEMU_BUILD_BUG_MSG(x, "not expecting: " #x) ++ ++#define QEMU_BUILD_BUG_ON_ZERO(x) (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - \ ++ sizeof(QEMU_BUILD_BUG_ON_STRUCT(x))) ++ ++#if !defined(__clang__) && defined(_WIN32) ++/* ++ * Map __printf__ to __gnu_printf__ because we want standard format strings even ++ * when MinGW or GLib include files use __printf__. ++ */ ++# define __printf__ __gnu_printf__ ++#endif ++ ++#ifndef __has_warning ++#define __has_warning(x) 0 /* compatibility with non-clang compilers */ ++#endif ++ ++#ifndef __has_feature ++#define __has_feature(x) 0 /* compatibility with non-clang compilers */ ++#endif ++ ++#ifndef __has_builtin ++#define __has_builtin(x) 0 /* compatibility with non-clang compilers */ ++#endif ++ ++#if __has_builtin(__builtin_assume_aligned) || !defined(__clang__) ++#define HAS_ASSUME_ALIGNED ++#endif ++ ++#ifndef __has_attribute ++#define __has_attribute(x) 0 /* compatibility with older GCC */ ++#endif ++ ++#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) ++# define QEMU_SANITIZE_ADDRESS 1 ++#endif ++ ++#if defined(__SANITIZE_THREAD__) || __has_feature(thread_sanitizer) ++# define QEMU_SANITIZE_THREAD 1 ++#endif ++ ++/* ++ * GCC doesn't provide __has_attribute() until GCC 5, but we know all the GCC ++ * versions we support have the "flatten" attribute. Clang may not have the ++ * "flatten" attribute but always has __has_attribute() to check for it. ++ */ ++#if __has_attribute(flatten) || !defined(__clang__) ++# define QEMU_FLATTEN __attribute__((flatten)) ++#else ++# define QEMU_FLATTEN ++#endif ++ ++/* ++ * If __attribute__((error)) is present, use it to produce an error at ++ * compile time. Otherwise, one must wait for the linker to diagnose ++ * the missing symbol. ++ */ ++#if __has_attribute(error) ++# define QEMU_ERROR(X) __attribute__((error(X))) ++#else ++# define QEMU_ERROR(X) ++#endif ++ ++/* ++ * The nonstring variable attribute specifies that an object or member ++ * declaration with type array of char or pointer to char is intended ++ * to store character arrays that do not necessarily contain a terminating ++ * NUL character. This is useful in detecting uses of such arrays or pointers ++ * with functions that expect NUL-terminated strings, and to avoid warnings ++ * when such an array or pointer is used as an argument to a bounded string ++ * manipulation function such as strncpy. ++ */ ++#if __has_attribute(nonstring) ++# define QEMU_NONSTRING __attribute__((nonstring)) ++#else ++# define QEMU_NONSTRING ++#endif ++ ++/* ++ * Forced inlining may be desired to encourage constant propagation ++ * of function parameters. However, it can also make debugging harder, ++ * so disable it for a non-optimizing build. ++ */ ++#if defined(__OPTIMIZE__) ++#define QEMU_ALWAYS_INLINE __attribute__((always_inline)) ++#else ++#define QEMU_ALWAYS_INLINE ++#endif ++ ++/** ++ * In most cases, normal "fallthrough" comments are good enough for ++ * switch-case statements, but sometimes the compiler has problems ++ * with those. In that case you can use QEMU_FALLTHROUGH instead. ++ */ ++#if __has_attribute(fallthrough) ++# define QEMU_FALLTHROUGH __attribute__((fallthrough)) ++#else ++# define QEMU_FALLTHROUGH do {} while (0) /* fallthrough */ ++#endif ++ ++#ifdef CONFIG_CFI ++/* ++ * If CFI is enabled, use an attribute to disable cfi-icall on the following ++ * function ++ */ ++#define QEMU_DISABLE_CFI __attribute__((no_sanitize("cfi-icall"))) ++#else ++/* If CFI is not enabled, use an empty define to not change the behavior */ ++#define QEMU_DISABLE_CFI ++#endif ++ ++/* ++ * Apple clang version 14 has a bug in its __builtin_subcll(); define ++ * BUILTIN_SUBCLL_BROKEN for the offending versions so we can avoid it. ++ * When a version of Apple clang which has this bug fixed is released ++ * we can add an upper bound to this check. ++ * See https://gitlab.com/qemu-project/qemu/-/issues/1631 ++ * and https://gitlab.com/qemu-project/qemu/-/issues/1659 for details. ++ * The bug never made it into any upstream LLVM releases, only Apple ones. ++ */ ++#if defined(__apple_build_version__) && __clang_major__ >= 14 ++#define BUILTIN_SUBCLL_BROKEN ++#endif ++ ++#if __has_attribute(annotate) ++#define QEMU_ANNOTATE(x) __attribute__((annotate(x))) ++#else ++#define QEMU_ANNOTATE(x) ++#endif ++ ++#if __has_attribute(used) ++# define QEMU_USED __attribute__((used)) ++#else ++# define QEMU_USED ++#endif ++ ++/* ++ * Ugly CPP trick that is like "defined FOO", but also works in C ++ * code. Useful to replace #ifdef with "if" statements; assumes ++ * the symbol was defined with Meson's "config.set()", so it is empty ++ * if defined. ++ */ ++#define IS_ENABLED(x) IS_EMPTY(x) ++ ++#define IS_EMPTY_JUNK_ junk, ++#define IS_EMPTY(value) IS_EMPTY_(IS_EMPTY_JUNK_##value) ++ ++/* Expands to either SECOND_ARG(junk, 1, 0) or SECOND_ARG(IS_EMPTY_JUNK_CONFIG_FOO 1, 0) */ ++#define SECOND_ARG(first, second, ...) second ++#define IS_EMPTY_(junk_maybecomma) SECOND_ARG(junk_maybecomma 1, 0) ++ ++#ifndef __cplusplus ++/* ++ * Useful in macros that need to declare temporary variables. For example, ++ * the variable that receives the old value of an atomically-accessed ++ * variable must be non-qualified, because atomic builtins return values ++ * through a pointer-type argument as in __atomic_load(&var, &old, MODEL). ++ * ++ * This macro has to handle types smaller than int manually, because of ++ * implicit promotion. int and larger types, as well as pointers, can be ++ * converted to a non-qualified type just by applying a binary operator. ++ */ ++#define typeof_strip_qual(expr) \ ++ typeof( \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), bool) || \ ++ __builtin_types_compatible_p(typeof(expr), const bool) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile bool) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile bool), \ ++ (bool)1, \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), signed char) || \ ++ __builtin_types_compatible_p(typeof(expr), const signed char) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile signed char) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile signed char), \ ++ (signed char)1, \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), unsigned char) || \ ++ __builtin_types_compatible_p(typeof(expr), const unsigned char) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile unsigned char) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile unsigned char), \ ++ (unsigned char)1, \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), signed short) || \ ++ __builtin_types_compatible_p(typeof(expr), const signed short) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile signed short) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile signed short), \ ++ (signed short)1, \ ++ __builtin_choose_expr( \ ++ __builtin_types_compatible_p(typeof(expr), unsigned short) || \ ++ __builtin_types_compatible_p(typeof(expr), const unsigned short) || \ ++ __builtin_types_compatible_p(typeof(expr), volatile unsigned short) || \ ++ __builtin_types_compatible_p(typeof(expr), const volatile unsigned short), \ ++ (unsigned short)1, \ ++ (expr)+0)))))) ++#endif ++ ++#endif /* COMPILER_H */ +diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h +new file mode 100644 +index 00000000..51b310fa +--- /dev/null ++++ b/include/qemu/config-file.h +@@ -0,0 +1,31 @@ ++#ifndef QEMU_CONFIG_FILE_H ++#define QEMU_CONFIG_FILE_H ++ ++typedef void QEMUConfigCB(const char *group, QDict *qdict, void *opaque, Error **errp); ++ ++void qemu_load_module_for_opts(const char *group); ++QemuOptsList *qemu_find_opts(const char *group); ++QemuOptsList *qemu_find_opts_err(const char *group, Error **errp); ++QemuOpts *qemu_find_opts_singleton(const char *group); ++ ++extern QemuOptsList *vm_config_groups[]; ++extern QemuOptsList *drive_config_groups[]; ++ ++void qemu_add_opts(QemuOptsList *list); ++void qemu_add_drive_opts(QemuOptsList *list); ++int qemu_global_option(const char *str); ++ ++int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, ++ Error **errp); ++ ++/* A default callback for qemu_read_config_file(). */ ++void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp); ++ ++int qemu_read_config_file(const char *filename, QEMUConfigCB *f, Error **errp); ++ ++/* Parse QDict options as a replacement for a config file (allowing multiple ++ enumerated (0..(n-1)) configuration "sections") */ ++bool qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, ++ Error **errp); ++ ++#endif /* QEMU_CONFIG_FILE_H */ +diff --git a/include/qemu/coroutine-core.h b/include/qemu/coroutine-core.h +new file mode 100644 +index 00000000..503bad6e +--- /dev/null ++++ b/include/qemu/coroutine-core.h +@@ -0,0 +1,154 @@ ++/* ++ * QEMU coroutine implementation ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Stefan Hajnoczi ++ * Kevin Wolf ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_COROUTINE_CORE_H ++#define QEMU_COROUTINE_CORE_H ++ ++/** ++ * Coroutines are a mechanism for stack switching and can be used for ++ * cooperative userspace threading. These functions provide a simple but ++ * useful flavor of coroutines that is suitable for writing sequential code, ++ * rather than callbacks, for operations that need to give up control while ++ * waiting for events to complete. ++ * ++ * These functions are re-entrant and may be used outside the BQL. ++ * ++ * Functions that execute in coroutine context cannot be called ++ * directly from normal functions. Use @coroutine_fn to mark such ++ * functions. For example: ++ * ++ * static void coroutine_fn foo(void) { ++ * .... ++ * } ++ * ++ * In the future it would be nice to have the compiler or a static ++ * checker catch misuse of such functions. This annotation might make ++ * it possible and in the meantime it serves as documentation. ++ */ ++ ++/** ++ * Mark a function that executes in coroutine context ++ * ++ * ++ * Functions that execute in coroutine context cannot be called ++ * directly from normal functions. Use @coroutine_fn to mark such ++ * functions. For example: ++ * ++ * static void coroutine_fn foo(void) { ++ * .... ++ * } ++ * ++ * In the future it would be nice to have the compiler or a static ++ * checker catch misuse of such functions. This annotation might make ++ * it possible and in the meantime it serves as documentation. ++ */ ++ ++typedef struct Coroutine Coroutine; ++typedef struct CoMutex CoMutex; ++ ++/** ++ * Coroutine entry point ++ * ++ * When the coroutine is entered for the first time, opaque is passed in as an ++ * argument. ++ * ++ * When this function returns, the coroutine is destroyed automatically and ++ * execution continues in the caller who last entered the coroutine. ++ */ ++typedef void coroutine_fn CoroutineEntry(void *opaque); ++ ++/** ++ * Create a new coroutine ++ * ++ * Use qemu_coroutine_enter() to actually transfer control to the coroutine. ++ * The opaque argument is passed as the argument to the entry point. ++ */ ++Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque); ++ ++/** ++ * Transfer control to a coroutine ++ */ ++void qemu_coroutine_enter(Coroutine *coroutine); ++ ++/** ++ * Transfer control to a coroutine if it's not active (i.e. part of the call ++ * stack of the running coroutine). Otherwise, do nothing. ++ */ ++void qemu_coroutine_enter_if_inactive(Coroutine *co); ++ ++/** ++ * Transfer control to a coroutine and associate it with ctx ++ */ ++void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co); ++ ++/** ++ * Transfer control back to a coroutine's caller ++ * ++ * This function does not return until the coroutine is re-entered using ++ * qemu_coroutine_enter(). ++ */ ++void coroutine_fn qemu_coroutine_yield(void); ++ ++/** ++ * Get the AioContext of the given coroutine ++ */ ++AioContext *qemu_coroutine_get_aio_context(Coroutine *co); ++ ++/** ++ * Get the currently executing coroutine ++ */ ++Coroutine *qemu_coroutine_self(void); ++ ++/** ++ * Return whether or not currently inside a coroutine ++ * ++ * This can be used to write functions that work both when in coroutine context ++ * and when not in coroutine context. Note that such functions cannot use the ++ * coroutine_fn annotation since they work outside coroutine context. ++ */ ++bool qemu_in_coroutine(void); ++ ++/** ++ * Return true if the coroutine is currently entered ++ * ++ * A coroutine is "entered" if it has not yielded from the current ++ * qemu_coroutine_enter() call used to run it. This does not mean that the ++ * coroutine is currently executing code since it may have transferred control ++ * to another coroutine using qemu_coroutine_enter(). ++ * ++ * When several coroutines enter each other there may be no way to know which ++ * ones have already been entered. In such situations this function can be ++ * used to avoid recursively entering coroutines. ++ */ ++bool qemu_coroutine_entered(Coroutine *co); ++ ++/** ++ * Initialises a CoMutex. This must be called before any other operation is used ++ * on the CoMutex. ++ */ ++void qemu_co_mutex_init(CoMutex *mutex); ++ ++/** ++ * Locks the mutex. If the lock cannot be taken immediately, control is ++ * transferred to the caller of the current coroutine. ++ */ ++void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex); ++ ++/** ++ * Unlocks the mutex and schedules the next coroutine that was waiting for this ++ * lock to be run. ++ */ ++void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex); ++ ++#endif +diff --git a/include/qemu/coroutine-tls.h b/include/qemu/coroutine-tls.h +new file mode 100644 +index 00000000..1558a826 +--- /dev/null ++++ b/include/qemu/coroutine-tls.h +@@ -0,0 +1,165 @@ ++/* ++ * QEMU Thread Local Storage for coroutines ++ * ++ * Copyright Red Hat ++ * ++ * SPDX-License-Identifier: LGPL-2.1-or-later ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ * It is forbidden to access Thread Local Storage in coroutines because ++ * compiler optimizations may cause values to be cached across coroutine ++ * re-entry. Coroutines can run in more than one thread through the course of ++ * their life, leading bugs when stale TLS values from the wrong thread are ++ * used as a result of compiler optimization. ++ * ++ * An example is: ++ * ++ * ..code-block:: c ++ * :caption: A coroutine that may see the wrong TLS value ++ * ++ * static __thread AioContext *current_aio_context; ++ * ... ++ * static void coroutine_fn foo(void) ++ * { ++ * aio_notify(current_aio_context); ++ * qemu_coroutine_yield(); ++ * aio_notify(current_aio_context); // <-- may be stale after yielding! ++ * } ++ * ++ * This header provides macros for safely defining variables in Thread Local ++ * Storage: ++ * ++ * ..code-block:: c ++ * :caption: A coroutine that safely uses TLS ++ * ++ * QEMU_DEFINE_STATIC_CO_TLS(AioContext *, current_aio_context) ++ * ... ++ * static void coroutine_fn foo(void) ++ * { ++ * aio_notify(get_current_aio_context()); ++ * qemu_coroutine_yield(); ++ * aio_notify(get_current_aio_context()); // <-- safe ++ * } ++ */ ++ ++#ifndef QEMU_COROUTINE_TLS_H ++#define QEMU_COROUTINE_TLS_H ++ ++/* ++ * To stop the compiler from caching TLS values we define accessor functions ++ * with __attribute__((noinline)) plus asm volatile("") to prevent ++ * optimizations that override noinline. ++ * ++ * The compiler can still analyze noinline code and make optimizations based on ++ * that knowledge, so an inline asm output operand is used to prevent ++ * optimizations that make assumptions about the address of the TLS variable. ++ * ++ * This is fragile and ultimately needs to be solved by a mechanism that is ++ * guaranteed to work by the compiler (e.g. stackless coroutines), but for now ++ * we use this approach to prevent issues. ++ */ ++ ++/** ++ * QEMU_DECLARE_CO_TLS: ++ * @type: the variable's C type ++ * @var: the variable name ++ * ++ * Declare an extern variable in Thread Local Storage from a header file: ++ * ++ * .. code-block:: c ++ * :caption: Declaring an extern variable in Thread Local Storage ++ * ++ * QEMU_DECLARE_CO_TLS(int, my_count) ++ * ... ++ * int c = get_my_count(); ++ * set_my_count(c + 1); ++ * *get_ptr_my_count() = 0; ++ * ++ * This is a coroutine-safe replacement for the __thread keyword and is ++ * equivalent to the following code: ++ * ++ * .. code-block:: c ++ * :caption: Declaring a TLS variable using __thread ++ * ++ * extern __thread int my_count; ++ * ... ++ * int c = my_count; ++ * my_count = c + 1; ++ * *(&my_count) = 0; ++ */ ++#define QEMU_DECLARE_CO_TLS(type, var) \ ++ __attribute__((noinline)) type get_##var(void); \ ++ __attribute__((noinline)) void set_##var(type v); \ ++ __attribute__((noinline)) type *get_ptr_##var(void); ++ ++/** ++ * QEMU_DEFINE_CO_TLS: ++ * @type: the variable's C type ++ * @var: the variable name ++ * ++ * Define a variable in Thread Local Storage that was previously declared from ++ * a header file with QEMU_DECLARE_CO_TLS(): ++ * ++ * .. code-block:: c ++ * :caption: Defining a variable in Thread Local Storage ++ * ++ * QEMU_DEFINE_CO_TLS(int, my_count) ++ * ++ * This is a coroutine-safe replacement for the __thread keyword and is ++ * equivalent to the following code: ++ * ++ * .. code-block:: c ++ * :caption: Defining a TLS variable using __thread ++ * ++ * __thread int my_count; ++ */ ++#define QEMU_DEFINE_CO_TLS(type, var) \ ++ static __thread type co_tls_##var; \ ++ type get_##var(void) { asm volatile(""); return co_tls_##var; } \ ++ void set_##var(type v) { asm volatile(""); co_tls_##var = v; } \ ++ type *get_ptr_##var(void) \ ++ { type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; } ++ ++/** ++ * QEMU_DEFINE_STATIC_CO_TLS: ++ * @type: the variable's C type ++ * @var: the variable name ++ * ++ * Define a static variable in Thread Local Storage: ++ * ++ * .. code-block:: c ++ * :caption: Defining a static variable in Thread Local Storage ++ * ++ * QEMU_DEFINE_STATIC_CO_TLS(int, my_count) ++ * ... ++ * int c = get_my_count(); ++ * set_my_count(c + 1); ++ * *get_ptr_my_count() = 0; ++ * ++ * This is a coroutine-safe replacement for the __thread keyword and is ++ * equivalent to the following code: ++ * ++ * .. code-block:: c ++ * :caption: Defining a static TLS variable using __thread ++ * ++ * static __thread int my_count; ++ * ... ++ * int c = my_count; ++ * my_count = c + 1; ++ * *(&my_count) = 0; ++ */ ++#define QEMU_DEFINE_STATIC_CO_TLS(type, var) \ ++ static __thread type co_tls_##var; \ ++ static __attribute__((noinline, unused)) \ ++ type get_##var(void) \ ++ { asm volatile(""); return co_tls_##var; } \ ++ static __attribute__((noinline, unused)) \ ++ void set_##var(type v) \ ++ { asm volatile(""); co_tls_##var = v; } \ ++ static __attribute__((noinline, unused)) \ ++ type *get_ptr_##var(void) \ ++ { type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; } ++ ++#endif /* QEMU_COROUTINE_TLS_H */ +diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h +new file mode 100644 +index 00000000..ff308453 +--- /dev/null ++++ b/include/qemu/coroutine.h +@@ -0,0 +1,311 @@ ++/* ++ * QEMU coroutine implementation ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Stefan Hajnoczi ++ * Kevin Wolf ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_COROUTINE_H ++#define QEMU_COROUTINE_H ++ ++#include "qemu/coroutine-core.h" ++#include "qemu/queue.h" ++#include "qemu/timer.h" ++ ++/** ++ * Coroutines are a mechanism for stack switching and can be used for ++ * cooperative userspace threading. These functions provide a simple but ++ * useful flavor of coroutines that is suitable for writing sequential code, ++ * rather than callbacks, for operations that need to give up control while ++ * waiting for events to complete. ++ * ++ * These functions are re-entrant and may be used outside the BQL. ++ * ++ * Functions that execute in coroutine context cannot be called ++ * directly from normal functions. Use @coroutine_fn to mark such ++ * functions. For example: ++ * ++ * static void coroutine_fn foo(void) { ++ * .... ++ * } ++ * ++ * In the future it would be nice to have the compiler or a static ++ * checker catch misuse of such functions. This annotation might make ++ * it possible and in the meantime it serves as documentation. ++ */ ++ ++/** ++ * Provides a mutex that can be used to synchronise coroutines ++ */ ++struct CoWaitRecord; ++struct CoMutex { ++ /* Count of pending lockers; 0 for a free mutex, 1 for an ++ * uncontended mutex. ++ */ ++ unsigned locked; ++ ++ /* Context that is holding the lock. Useful to avoid spinning ++ * when two coroutines on the same AioContext try to get the lock. :) ++ */ ++ AioContext *ctx; ++ ++ /* A queue of waiters. Elements are added atomically in front of ++ * from_push. to_pop is only populated, and popped from, by whoever ++ * is in charge of the next wakeup. This can be an unlocker or, ++ * through the handoff protocol, a locker that is about to go to sleep. ++ */ ++ QSLIST_HEAD(, CoWaitRecord) from_push, to_pop; ++ ++ unsigned handoff, sequence; ++ ++ Coroutine *holder; ++}; ++ ++/** ++ * Assert that the current coroutine holds @mutex. ++ */ ++static inline coroutine_fn void qemu_co_mutex_assert_locked(CoMutex *mutex) ++{ ++ /* ++ * mutex->holder doesn't need any synchronisation if the assertion holds ++ * true because the mutex protects it. If it doesn't hold true, we still ++ * don't mind if another thread takes or releases mutex behind our back, ++ * because the condition will be false no matter whether we read NULL or ++ * the pointer for any other coroutine. ++ */ ++ assert(qatomic_read(&mutex->locked) && ++ mutex->holder == qemu_coroutine_self()); ++} ++ ++#include "qemu/lockable.h" ++ ++/** ++ * CoQueues are a mechanism to queue coroutines in order to continue executing ++ * them later. They are similar to condition variables, but they need help ++ * from an external mutex in order to maintain thread-safety. ++ */ ++typedef struct CoQueue { ++ QSIMPLEQ_HEAD(, Coroutine) entries; ++} CoQueue; ++ ++/** ++ * Initialise a CoQueue. This must be called before any other operation is used ++ * on the CoQueue. ++ */ ++void qemu_co_queue_init(CoQueue *queue); ++ ++typedef enum { ++ /* ++ * Enqueue at front instead of back. Use this to re-queue a request when ++ * its wait condition is not satisfied after being woken up. ++ */ ++ CO_QUEUE_WAIT_FRONT = 0x1, ++} CoQueueWaitFlags; ++ ++/** ++ * Adds the current coroutine to the CoQueue and transfers control to the ++ * caller of the coroutine. The mutex is unlocked during the wait and ++ * locked again afterwards. ++ */ ++#define qemu_co_queue_wait(queue, lock) \ ++ qemu_co_queue_wait_impl(queue, QEMU_MAKE_LOCKABLE(lock), 0) ++#define qemu_co_queue_wait_flags(queue, lock, flags) \ ++ qemu_co_queue_wait_impl(queue, QEMU_MAKE_LOCKABLE(lock), (flags)) ++void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock, ++ CoQueueWaitFlags flags); ++ ++/** ++ * Removes the next coroutine from the CoQueue, and queue it to run after ++ * the currently-running coroutine yields. ++ * Returns true if a coroutine was removed, false if the queue is empty. ++ * Used from coroutine context, use qemu_co_enter_next outside. ++ */ ++bool coroutine_fn qemu_co_queue_next(CoQueue *queue); ++ ++/** ++ * Empties the CoQueue and queues the coroutine to run after ++ * the currently-running coroutine yields. ++ * Used from coroutine context, use qemu_co_enter_all outside. ++ */ ++void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue); ++ ++/** ++ * Removes the next coroutine from the CoQueue, and wake it up. Unlike ++ * qemu_co_queue_next, this function releases the lock during aio_co_wake ++ * because it is meant to be used outside coroutine context; in that case, the ++ * coroutine is entered immediately, before qemu_co_enter_next returns. ++ * ++ * If used in coroutine context, qemu_co_enter_next is equivalent to ++ * qemu_co_queue_next. ++ */ ++#define qemu_co_enter_next(queue, lock) \ ++ qemu_co_enter_next_impl(queue, QEMU_MAKE_LOCKABLE(lock)) ++bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock); ++ ++/** ++ * Empties the CoQueue, waking the waiting coroutine one at a time. Unlike ++ * qemu_co_queue_all, this function releases the lock during aio_co_wake ++ * because it is meant to be used outside coroutine context; in that case, the ++ * coroutine is entered immediately, before qemu_co_enter_all returns. ++ * ++ * If used in coroutine context, qemu_co_enter_all is equivalent to ++ * qemu_co_queue_all. ++ */ ++#define qemu_co_enter_all(queue, lock) \ ++ qemu_co_enter_all_impl(queue, QEMU_MAKE_LOCKABLE(lock)) ++void qemu_co_enter_all_impl(CoQueue *queue, QemuLockable *lock); ++ ++/** ++ * Checks if the CoQueue is empty. ++ */ ++bool qemu_co_queue_empty(CoQueue *queue); ++ ++ ++typedef struct CoRwTicket CoRwTicket; ++typedef struct CoRwlock { ++ CoMutex mutex; ++ ++ /* Number of readers, or -1 if owned for writing. */ ++ int owners; ++ ++ /* Waiting coroutines. */ ++ QSIMPLEQ_HEAD(, CoRwTicket) tickets; ++} CoRwlock; ++ ++/** ++ * Initialises a CoRwlock. This must be called before any other operation ++ * is used on the CoRwlock ++ */ ++void qemu_co_rwlock_init(CoRwlock *lock); ++ ++/** ++ * Read locks the CoRwlock. If the lock cannot be taken immediately because ++ * of a parallel writer, control is transferred to the caller of the current ++ * coroutine. ++ */ ++void coroutine_fn qemu_co_rwlock_rdlock(CoRwlock *lock); ++ ++/** ++ * Write Locks the CoRwlock from a reader. This is a bit more efficient than ++ * @qemu_co_rwlock_unlock followed by a separate @qemu_co_rwlock_wrlock. ++ * Note that if the lock cannot be upgraded immediately, control is transferred ++ * to the caller of the current coroutine; another writer might run while ++ * @qemu_co_rwlock_upgrade blocks. ++ */ ++void coroutine_fn qemu_co_rwlock_upgrade(CoRwlock *lock); ++ ++/** ++ * Downgrades a write-side critical section to a reader. Downgrading with ++ * @qemu_co_rwlock_downgrade never blocks, unlike @qemu_co_rwlock_unlock ++ * followed by @qemu_co_rwlock_rdlock. This makes it more efficient, but ++ * may also sometimes be necessary for correctness. ++ */ ++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 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 coroutine_fn qemu_co_rwlock_unlock(CoRwlock *lock); ++ ++typedef struct QemuCoSleep { ++ Coroutine *to_wake; ++} QemuCoSleep; ++ ++/** ++ * Yield the coroutine for a given duration. Initializes @w so that, ++ * during this yield, it can be passed to qemu_co_sleep_wake() to ++ * terminate the sleep. ++ */ ++void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, ++ QEMUClockType type, int64_t ns); ++ ++/** ++ * Yield the coroutine until the next call to qemu_co_sleep_wake. ++ */ ++void coroutine_fn qemu_co_sleep(QemuCoSleep *w); ++ ++static inline void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) ++{ ++ QemuCoSleep w = { 0 }; ++ qemu_co_sleep_ns_wakeable(&w, type, ns); ++} ++ ++typedef void CleanupFunc(void *opaque); ++/** ++ * Run entry in a coroutine and start timer. Wait for entry to finish or for ++ * timer to elapse, what happen first. If entry finished, return 0, if timer ++ * elapsed earlier, return -ETIMEDOUT. ++ * ++ * Be careful, entry execution is not canceled, user should handle it somehow. ++ * If @clean is provided, it's called after coroutine finish if timeout ++ * happened. ++ */ ++int coroutine_fn qemu_co_timeout(CoroutineEntry *entry, void *opaque, ++ uint64_t timeout_ns, CleanupFunc clean); ++ ++/** ++ * Wake a coroutine if it is sleeping in qemu_co_sleep_ns. The timer will be ++ * deleted. @sleep_state must be the variable whose address was given to ++ * qemu_co_sleep_ns() and should be checked to be non-NULL before calling ++ * qemu_co_sleep_wake(). ++ */ ++void qemu_co_sleep_wake(QemuCoSleep *w); ++ ++/** ++ * Yield until a file descriptor becomes readable ++ * ++ * Note that this function clobbers the handlers for the file descriptor. ++ */ ++void coroutine_fn yield_until_fd_readable(int fd); ++ ++/** ++ * Increase coroutine pool size ++ */ ++void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size); ++ ++/** ++ * Decrease coroutine pool size ++ */ ++void qemu_coroutine_dec_pool_size(unsigned int additional_pool_size); ++ ++/** ++ * Sends a (part of) iovec down a socket, yielding when the socket is full, or ++ * Receives data into a (part of) iovec from a socket, ++ * yielding when there is no data in the socket. ++ * The same interface as qemu_sendv_recvv(), with added yielding. ++ * XXX should mark these as coroutine_fn ++ */ ++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) \ ++ qemu_co_sendv_recvv(sockfd, iov, iov_cnt, offset, bytes, true) ++ ++/** ++ * The same as above, but with just a single buffer ++ */ ++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) \ ++ qemu_co_send_recv(sockfd, buf, bytes, true) ++ ++#endif /* QEMU_COROUTINE_H */ +diff --git a/include/qemu/coroutine_int.h b/include/qemu/coroutine_int.h +new file mode 100644 +index 00000000..1da14855 +--- /dev/null ++++ b/include/qemu/coroutine_int.h +@@ -0,0 +1,77 @@ ++/* ++ * Coroutine internals ++ * ++ * Copyright (c) 2011 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_COROUTINE_INT_H ++#define QEMU_COROUTINE_INT_H ++ ++#include "qemu/queue.h" ++#include "qemu/coroutine.h" ++ ++#ifdef CONFIG_SAFESTACK ++/* Pointer to the unsafe stack, defined by the compiler */ ++extern __thread void *__safestack_unsafe_stack_ptr; ++#endif ++ ++#define COROUTINE_STACK_SIZE (1 << 20) ++ ++typedef enum { ++ COROUTINE_YIELD = 1, ++ COROUTINE_TERMINATE = 2, ++ COROUTINE_ENTER = 3, ++} CoroutineAction; ++ ++struct Coroutine { ++ CoroutineEntry *entry; ++ void *entry_arg; ++ Coroutine *caller; ++ ++ /* Only used when the coroutine has terminated. */ ++ QSLIST_ENTRY(Coroutine) pool_next; ++ ++ size_t locks_held; ++ ++ /* Only used when the coroutine has yielded. */ ++ AioContext *ctx; ++ ++ /* Used to catch and abort on illegal co-routine entry. ++ * Will contain the name of the function that had first ++ * scheduled the coroutine. */ ++ const char *scheduled; ++ ++ QSIMPLEQ_ENTRY(Coroutine) co_queue_next; ++ ++ /* Coroutines that should be woken up when we yield or terminate. ++ * Only used when the coroutine is running. ++ */ ++ QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup; ++ ++ QSLIST_ENTRY(Coroutine) co_scheduled_next; ++}; ++ ++Coroutine *qemu_coroutine_new(void); ++void qemu_coroutine_delete(Coroutine *co); ++CoroutineAction qemu_coroutine_switch(Coroutine *from, Coroutine *to, ++ CoroutineAction action); ++ ++#endif +diff --git a/include/qemu/ctype.h b/include/qemu/ctype.h +new file mode 100644 +index 00000000..3691f098 +--- /dev/null ++++ b/include/qemu/ctype.h +@@ -0,0 +1,27 @@ ++/* ++ * QEMU TCG support ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_CTYPE_H ++#define QEMU_CTYPE_H ++ ++#define qemu_isalnum(c) isalnum((unsigned char)(c)) ++#define qemu_isalpha(c) isalpha((unsigned char)(c)) ++#define qemu_iscntrl(c) iscntrl((unsigned char)(c)) ++#define qemu_isdigit(c) isdigit((unsigned char)(c)) ++#define qemu_isgraph(c) isgraph((unsigned char)(c)) ++#define qemu_islower(c) islower((unsigned char)(c)) ++#define qemu_isprint(c) isprint((unsigned char)(c)) ++#define qemu_ispunct(c) ispunct((unsigned char)(c)) ++#define qemu_isspace(c) isspace((unsigned char)(c)) ++#define qemu_isupper(c) isupper((unsigned char)(c)) ++#define qemu_isxdigit(c) isxdigit((unsigned char)(c)) ++#define qemu_tolower(c) tolower((unsigned char)(c)) ++#define qemu_toupper(c) toupper((unsigned char)(c)) ++#define qemu_isascii(c) isascii((unsigned char)(c)) ++#define qemu_toascii(c) toascii((unsigned char)(c)) ++ ++#endif +diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h +new file mode 100644 +index 00000000..da15547b +--- /dev/null ++++ b/include/qemu/cutils.h +@@ -0,0 +1,308 @@ ++#ifndef QEMU_CUTILS_H ++#define QEMU_CUTILS_H ++ ++/* ++ * si_prefix: ++ * @exp10: exponent of 10, a multiple of 3 between -18 and 18 inclusive. ++ * ++ * Return a SI prefix (n, u, m, K, M, etc.) corresponding ++ * to the given exponent of 10. ++ */ ++const char *si_prefix(unsigned int exp10); ++ ++/* ++ * iec_binary_prefix: ++ * @exp2: exponent of 2, a multiple of 10 between 0 and 60 inclusive. ++ * ++ * Return an IEC binary prefix (Ki, Mi, etc.) corresponding ++ * to the given exponent of 2. ++ */ ++const char *iec_binary_prefix(unsigned int exp2); ++ ++/** ++ * pstrcpy: ++ * @buf: buffer to copy string into ++ * @buf_size: size of @buf in bytes ++ * @str: string to copy ++ * ++ * Copy @str into @buf, including the trailing NUL, but do not ++ * write more than @buf_size bytes. The resulting buffer is ++ * always NUL terminated (even if the source string was too long). ++ * If @buf_size is zero or negative then no bytes are copied. ++ * ++ * This function is similar to strncpy(), but avoids two of that ++ * function's problems: ++ * * if @str fits in the buffer, pstrcpy() does not zero-fill the ++ * remaining space at the end of @buf ++ * * if @str is too long, pstrcpy() will copy the first @buf_size-1 ++ * bytes and then add a NUL ++ */ ++void pstrcpy(char *buf, int buf_size, const char *str); ++/** ++ * strpadcpy: ++ * @buf: buffer to copy string into ++ * @buf_size: size of @buf in bytes ++ * @str: string to copy ++ * @pad: character to pad the remainder of @buf with ++ * ++ * Copy @str into @buf (but *not* its trailing NUL!), and then pad the ++ * rest of the buffer with the @pad character. If @str is too large ++ * for the buffer then it is truncated, so that @buf contains the ++ * first @buf_size characters of @str, with no terminator. ++ */ ++void strpadcpy(char *buf, int buf_size, const char *str, char pad); ++/** ++ * pstrcat: ++ * @buf: buffer containing existing string ++ * @buf_size: size of @buf in bytes ++ * @s: string to concatenate to @buf ++ * ++ * Append a copy of @s to the string already in @buf, but do not ++ * allow the buffer to overflow. If the existing contents of @buf ++ * plus @str would total more than @buf_size bytes, then write ++ * as much of @str as will fit followed by a NUL terminator. ++ * ++ * @buf must already contain a NUL-terminated string, or the ++ * behaviour is undefined. ++ * ++ * Returns: @buf. ++ */ ++char *pstrcat(char *buf, int buf_size, const char *s); ++/** ++ * strstart: ++ * @str: string to test ++ * @val: prefix string to look for ++ * @ptr: NULL, or pointer to be written to indicate start of ++ * the remainder of the string ++ * ++ * Test whether @str starts with the prefix @val. ++ * If it does (including the degenerate case where @str and @val ++ * are equal) then return true. If @ptr is not NULL then a ++ * pointer to the first character following the prefix is written ++ * to it. If @val is not a prefix of @str then return false (and ++ * @ptr is not written to). ++ * ++ * Returns: true if @str starts with prefix @val, false otherwise. ++ */ ++int strstart(const char *str, const char *val, const char **ptr); ++/** ++ * stristart: ++ * @str: string to test ++ * @val: prefix string to look for ++ * @ptr: NULL, or pointer to be written to indicate start of ++ * the remainder of the string ++ * ++ * Test whether @str starts with the case-insensitive prefix @val. ++ * This function behaves identically to strstart(), except that the ++ * comparison is made after calling qemu_toupper() on each pair of ++ * characters. ++ * ++ * Returns: true if @str starts with case-insensitive prefix @val, ++ * false otherwise. ++ */ ++int stristart(const char *str, const char *val, const char **ptr); ++/** ++ * qemu_strnlen: ++ * @s: string ++ * @max_len: maximum number of bytes in @s to scan ++ * ++ * Return the length of the string @s, like strlen(), but do not ++ * examine more than @max_len bytes of the memory pointed to by @s. ++ * If no NUL terminator is found within @max_len bytes, then return ++ * @max_len instead. ++ * ++ * This function has the same behaviour as the POSIX strnlen() ++ * function. ++ * ++ * Returns: length of @s in bytes, or @max_len, whichever is smaller. ++ */ ++int qemu_strnlen(const char *s, int max_len); ++/** ++ * qemu_strsep: ++ * @input: pointer to string to parse ++ * @delim: string containing delimiter characters to search for ++ * ++ * Locate the first occurrence of any character in @delim within ++ * the string referenced by @input, and replace it with a NUL. ++ * The location of the next character after the delimiter character ++ * is stored into @input. ++ * If the end of the string was reached without finding a delimiter ++ * character, then NULL is stored into @input. ++ * If @input points to a NULL pointer on entry, return NULL. ++ * The return value is always the original value of *@input (and ++ * so now points to a NUL-terminated string corresponding to the ++ * part of the input up to the first delimiter). ++ * ++ * This function has the same behaviour as the BSD strsep() function. ++ * ++ * Returns: the pointer originally in @input. ++ */ ++char *qemu_strsep(char **input, const char *delim); ++#ifdef HAVE_STRCHRNUL ++static inline const char *qemu_strchrnul(const char *s, int c) ++{ ++ return strchrnul(s, c); ++} ++#else ++const char *qemu_strchrnul(const char *s, int c); ++#endif ++time_t mktimegm(struct tm *tm); ++int qemu_parse_fd(const char *param); ++int qemu_strtoi(const char *nptr, const char **endptr, int base, ++ int *result); ++int qemu_strtoui(const char *nptr, const char **endptr, int base, ++ unsigned int *result); ++int qemu_strtol(const char *nptr, const char **endptr, int base, ++ long *result); ++int qemu_strtoul(const char *nptr, const char **endptr, int base, ++ unsigned long *result); ++int qemu_strtoi64(const char *nptr, const char **endptr, int base, ++ int64_t *result); ++int qemu_strtou64(const char *nptr, const char **endptr, int base, ++ uint64_t *result); ++int qemu_strtod(const char *nptr, const char **endptr, double *result); ++int qemu_strtod_finite(const char *nptr, const char **endptr, double *result); ++ ++int parse_uint(const char *s, const char **endptr, int base, uint64_t *value); ++int parse_uint_full(const char *s, int base, uint64_t *value); ++ ++int qemu_strtosz(const char *nptr, const char **end, uint64_t *result); ++int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result); ++int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result); ++ ++char *size_to_str(uint64_t val); ++ ++/** ++ * freq_to_str: ++ * @freq_hz: frequency to stringify ++ * ++ * Return human readable string for frequency @freq_hz. ++ * Use SI units like KHz, MHz, and so forth. ++ * ++ * The caller is responsible for releasing the value returned ++ * with g_free() after use. ++ */ ++char *freq_to_str(uint64_t freq_hz); ++ ++/* used to print char* safely */ ++#define STR_OR_NULL(str) ((str) ? (str) : "null") ++ ++/* ++ * Check if a buffer is all zeroes. ++ */ ++ ++bool buffer_is_zero_ool(const void *vbuf, size_t len); ++bool buffer_is_zero_ge256(const void *vbuf, size_t len); ++bool test_buffer_is_zero_next_accel(void); ++ ++static inline bool buffer_is_zero_sample3(const char *buf, size_t len) ++{ ++ /* ++ * For any reasonably sized buffer, these three samples come from ++ * three different cachelines. In qemu-img usage, we find that ++ * each byte eliminates more than half of all buffer testing. ++ * It is therefore critical to performance that the byte tests ++ * short-circuit, so that we do not pull in additional cache lines. ++ * Do not "optimize" this to !(a | b | c). ++ */ ++ return !buf[0] && !buf[len - 1] && !buf[len / 2]; ++} ++ ++#ifdef __OPTIMIZE__ ++static inline bool buffer_is_zero(const void *buf, size_t len) ++{ ++ return (__builtin_constant_p(len) && len >= 256 ++ ? buffer_is_zero_sample3(buf, len) && ++ buffer_is_zero_ge256(buf, len) ++ : buffer_is_zero_ool(buf, len)); ++} ++#else ++#define buffer_is_zero buffer_is_zero_ool ++#endif ++ ++/* ++ * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) ++ * Input is limited to 14-bit numbers ++ */ ++ ++int uleb128_encode_small(uint8_t *out, uint32_t n); ++int uleb128_decode_small(const uint8_t *in, uint32_t *n); ++ ++/** ++ * qemu_pstrcmp0: ++ * @str1: a non-NULL pointer to a C string (*str1 can be NULL) ++ * @str2: a non-NULL pointer to a C string (*str2 can be NULL) ++ * ++ * Compares *str1 and *str2 with g_strcmp0(). ++ * ++ * Returns: an integer less than, equal to, or greater than zero, if ++ * *str1 is <, == or > than *str2. ++ */ ++int qemu_pstrcmp0(const char **str1, const char **str2); ++ ++/* Find program directory, and save it for later usage with ++ * qemu_get_exec_dir(). ++ * Try OS specific API first, if not working, parse from argv0. */ ++void qemu_init_exec_dir(const char *argv0); ++ ++/* Get the saved exec dir. */ ++const char *qemu_get_exec_dir(void); ++ ++/** ++ * get_relocated_path: ++ * @dir: the directory (typically a `CONFIG_*DIR` variable) to be relocated. ++ * ++ * Returns a path for @dir that uses the directory of the running executable ++ * as the prefix. ++ * ++ * When a directory named `qemu-bundle` exists in the directory of the running ++ * executable, the path to the directory will be prepended to @dir. For ++ * example, if the directory of the running executable is `/qemu/build` @dir ++ * is `/usr/share/qemu`, the result will be ++ * `/qemu/build/qemu-bundle/usr/share/qemu`. The directory is expected to exist ++ * in the build tree. ++ * ++ * Otherwise, the directory of the running executable will be used as the ++ * prefix and it appends the relative path from `bindir` to @dir. For example, ++ * if the directory of the running executable is `/opt/qemu/bin`, `bindir` is ++ * `/usr/bin` and @dir is `/usr/share/qemu`, the result will be ++ * `/opt/qemu/bin/../share/qemu`. ++ * ++ * The returned string should be freed by the caller. ++ */ ++char *get_relocated_path(const char *dir); ++ ++static inline const char *yes_no(bool b) ++{ ++ return b ? "yes" : "no"; ++} ++ ++/* ++ * helper to parse debug environment variables ++ */ ++int parse_debug_env(const char *name, int max, int initial); ++ ++/** ++ * qemu_hexdump_line: ++ * @str: GString into which to append ++ * @buf: buffer to dump ++ * @len: number of bytes to dump ++ * @unit_len: add a space between every @unit_len bytes ++ * @block_len: add an extra space between every @block_len bytes ++ * ++ * Append @len bytes of @buf as hexadecimal into @str. ++ * Add spaces between every @unit_len and @block_len bytes. ++ * If @str is NULL, allocate a new string and return it; ++ * otherwise return @str. ++ */ ++GString *qemu_hexdump_line(GString *str, const void *buf, size_t len, ++ size_t unit_len, size_t block_len); ++ ++/* ++ * Hexdump a buffer to a file. An optional string prefix is added to every line ++ */ ++ ++void qemu_hexdump(FILE *fp, const char *prefix, ++ const void *bufptr, size_t size); ++ ++#endif +diff --git a/include/qemu/defer-call.h b/include/qemu/defer-call.h +new file mode 100644 +index 00000000..e2c1d245 +--- /dev/null ++++ b/include/qemu/defer-call.h +@@ -0,0 +1,16 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Deferred calls ++ * ++ * Copyright Red Hat. ++ */ ++ ++#ifndef QEMU_DEFER_CALL_H ++#define QEMU_DEFER_CALL_H ++ ++/* See documentation in util/defer-call.c */ ++void defer_call_begin(void); ++void defer_call_end(void); ++void defer_call(void (*fn)(void *), void *opaque); ++ ++#endif /* QEMU_DEFER_CALL_H */ +diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h +new file mode 100644 +index 00000000..3ae2357f +--- /dev/null ++++ b/include/qemu/error-report.h +@@ -0,0 +1,77 @@ ++/* ++ * Error reporting ++ * ++ * Copyright (C) 2010 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_ERROR_REPORT_H ++#define QEMU_ERROR_REPORT_H ++ ++typedef struct Location { ++ /* all members are private to qemu-error.c */ ++ enum { LOC_NONE, LOC_CMDLINE, LOC_FILE } kind; ++ int num; ++ const void *ptr; ++ struct Location *prev; ++} Location; ++ ++Location *loc_push_restore(Location *loc); ++Location *loc_push_none(Location *loc); ++Location *loc_pop(Location *loc); ++Location *loc_save(Location *loc); ++void loc_restore(Location *loc); ++void loc_set_none(void); ++void loc_set_cmdline(char **argv, int idx, int cnt); ++void loc_set_file(const char *fname, int lno); ++ ++int error_vprintf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++int error_printf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++ ++void error_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++void warn_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++void info_vreport(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++ ++void error_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++void warn_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++void info_report(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++ ++bool error_report_once_cond(bool *printed, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++bool warn_report_once_cond(bool *printed, const char *fmt, ...) ++ G_GNUC_PRINTF(2, 3); ++ ++void error_init(const char *argv0); ++ ++/* ++ * Similar to error_report(), except it prints the message just once. ++ * Return true when it prints, false otherwise. ++ */ ++#define error_report_once(fmt, ...) \ ++ ({ \ ++ static bool print_once_; \ ++ error_report_once_cond(&print_once_, \ ++ fmt, ##__VA_ARGS__); \ ++ }) ++ ++/* ++ * Similar to warn_report(), except it prints the message just once. ++ * Return true when it prints, false otherwise. ++ */ ++#define warn_report_once(fmt, ...) \ ++ ({ \ ++ static bool print_once_; \ ++ warn_report_once_cond(&print_once_, \ ++ fmt, ##__VA_ARGS__); \ ++ }) ++ ++extern bool message_with_timestamp; ++extern bool error_with_guestname; ++extern const char *error_guest_name; ++ ++#endif +diff --git a/include/qemu/event_notifier.h b/include/qemu/event_notifier.h +new file mode 100644 +index 00000000..8a4ff308 +--- /dev/null ++++ b/include/qemu/event_notifier.h +@@ -0,0 +1,46 @@ ++/* ++ * event notifier support ++ * ++ * Copyright Red Hat, Inc. 2010 ++ * ++ * Authors: ++ * Michael S. Tsirkin ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_EVENT_NOTIFIER_H ++#define QEMU_EVENT_NOTIFIER_H ++ ++ ++#ifdef _WIN32 ++#include ++#endif ++ ++struct EventNotifier { ++#ifdef _WIN32 ++ HANDLE event; ++#else ++ int rfd; ++ int wfd; ++ bool initialized; ++#endif ++}; ++ ++typedef void EventNotifierHandler(EventNotifier *); ++ ++int event_notifier_init(EventNotifier *, int active); ++void event_notifier_cleanup(EventNotifier *); ++int event_notifier_set(EventNotifier *); ++int event_notifier_test_and_clear(EventNotifier *); ++ ++#ifdef CONFIG_POSIX ++void event_notifier_init_fd(EventNotifier *, int fd); ++int event_notifier_get_fd(const EventNotifier *); ++int event_notifier_get_wfd(const EventNotifier *); ++#else ++HANDLE event_notifier_get_handle(EventNotifier *); ++#endif ++ ++#endif +diff --git a/include/qemu/futex.h b/include/qemu/futex.h +new file mode 100644 +index 00000000..91ae8896 +--- /dev/null ++++ b/include/qemu/futex.h +@@ -0,0 +1,41 @@ ++/* ++ * Wrappers around Linux futex syscall ++ * ++ * Copyright Red Hat, Inc. 2017 ++ * ++ * Author: ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_FUTEX_H ++#define QEMU_FUTEX_H ++ ++#include ++#include ++ ++#define qemu_futex(...) syscall(__NR_futex, __VA_ARGS__) ++ ++static inline void qemu_futex_wake(void *f, int n) ++{ ++ qemu_futex(f, FUTEX_WAKE, n, NULL, NULL, 0); ++} ++ ++static inline void qemu_futex_wait(void *f, unsigned val) ++{ ++ while (qemu_futex(f, FUTEX_WAIT, (int) val, NULL, NULL, 0)) { ++ switch (errno) { ++ case EWOULDBLOCK: ++ return; ++ case EINTR: ++ break; /* get out of switch and retry */ ++ default: ++ abort(); ++ } ++ } ++} ++ ++#endif /* QEMU_FUTEX_H */ +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +new file mode 100644 +index 00000000..8136e336 +--- /dev/null ++++ b/include/qemu/hbitmap.h +@@ -0,0 +1,353 @@ ++/* ++ * Hierarchical Bitmap Data Type ++ * ++ * Copyright Red Hat, Inc., 2012 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef HBITMAP_H ++#define HBITMAP_H ++ ++#include "bitops.h" ++#include "host-utils.h" ++ ++typedef struct HBitmap HBitmap; ++typedef struct HBitmapIter HBitmapIter; ++ ++#define BITS_PER_LEVEL (BITS_PER_LONG == 32 ? 5 : 6) ++ ++/* For 32-bit, the largest that fits in a 4 GiB address space. ++ * For 64-bit, the number of sectors in 1 PiB. Good luck, in ++ * either case... :) ++ */ ++#define HBITMAP_LOG_MAX_SIZE (BITS_PER_LONG == 32 ? 34 : 41) ++ ++/* We need to place a sentinel in level 0 to speed up iteration. Thus, ++ * we do this instead of HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL. The ++ * difference is that it allocates an extra level when HBITMAP_LOG_MAX_SIZE ++ * is an exact multiple of BITS_PER_LEVEL. ++ */ ++#define HBITMAP_LEVELS ((HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL) + 1) ++ ++struct HBitmapIter { ++ const HBitmap *hb; ++ ++ /* Copied from hb for access in the inline functions (hb is opaque). */ ++ int granularity; ++ ++ /* Entry offset into the last-level array of longs. */ ++ size_t pos; ++ ++ /* The currently-active path in the tree. Each item of cur[i] stores ++ * the bits (i.e. the subtrees) yet to be processed under that node. ++ */ ++ unsigned long cur[HBITMAP_LEVELS]; ++}; ++ ++/** ++ * hbitmap_alloc: ++ * @size: Number of bits in the bitmap. ++ * @granularity: Granularity of the bitmap. Aligned groups of 2^@granularity ++ * bits will be represented by a single bit. Each operation on a ++ * range of bits first rounds the bits to determine which group they land ++ * in, and then affect the entire set; iteration will only visit the first ++ * bit of each group. ++ * ++ * Allocate a new HBitmap. ++ */ ++HBitmap *hbitmap_alloc(uint64_t size, int granularity); ++ ++/** ++ * hbitmap_truncate: ++ * @hb: The bitmap to change the size of. ++ * @size: The number of elements to change the bitmap to accommodate. ++ * ++ * truncate or grow an existing bitmap to accommodate a new number of elements. ++ * This may invalidate existing HBitmapIterators. ++ */ ++void hbitmap_truncate(HBitmap *hb, uint64_t size); ++ ++/** ++ * hbitmap_merge: ++ * ++ * Store result of merging @a and @b into @result. ++ * @result is allowed to be equal to @a or @b. ++ * All bitmaps must have same size. ++ */ ++void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result); ++ ++/** ++ * hbitmap_empty: ++ * @hb: HBitmap to operate on. ++ * ++ * Return whether the bitmap is empty. ++ */ ++bool hbitmap_empty(const HBitmap *hb); ++ ++/** ++ * hbitmap_granularity: ++ * @hb: HBitmap to operate on. ++ * ++ * Return the granularity of the HBitmap. ++ */ ++int hbitmap_granularity(const HBitmap *hb); ++ ++/** ++ * hbitmap_count: ++ * @hb: HBitmap to operate on. ++ * ++ * Return the number of bits set in the HBitmap. ++ */ ++uint64_t hbitmap_count(const HBitmap *hb); ++ ++/** ++ * hbitmap_set: ++ * @hb: HBitmap to operate on. ++ * @start: First bit to set (0-based). ++ * @count: Number of bits to set. ++ * ++ * Set a consecutive range of bits in an HBitmap. ++ */ ++void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count); ++ ++/** ++ * hbitmap_reset: ++ * @hb: HBitmap to operate on. ++ * @start: First bit to reset (0-based). ++ * @count: Number of bits to reset. ++ * ++ * Reset a consecutive range of bits in an HBitmap. ++ * @start and @count must be aligned to bitmap granularity. The only exception ++ * is resetting the tail of the bitmap: @count may be equal to hb->orig_size - ++ * @start, in this case @count may be not aligned. The sum of @start + @count is ++ * allowed to be greater than hb->orig_size, but only if @start < hb->orig_size ++ * and @start + @count = ALIGN_UP(hb->orig_size, granularity). ++ */ ++void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count); ++ ++/** ++ * hbitmap_reset_all: ++ * @hb: HBitmap to operate on. ++ * ++ * Reset all bits in an HBitmap. ++ */ ++void hbitmap_reset_all(HBitmap *hb); ++ ++/** ++ * hbitmap_get: ++ * @hb: HBitmap to operate on. ++ * @item: Bit to query (0-based). ++ * ++ * Return whether the @item-th bit in an HBitmap is set. ++ */ ++bool hbitmap_get(const HBitmap *hb, uint64_t item); ++ ++/** ++ * hbitmap_is_serializable: ++ * @hb: HBitmap which should be (de-)serialized. ++ * ++ * Returns whether the bitmap can actually be (de-)serialized. Other ++ * (de-)serialization functions may only be invoked if this function returns ++ * true. ++ * ++ * Calling (de-)serialization functions does not affect a bitmap's ++ * (de-)serializability. ++ */ ++bool hbitmap_is_serializable(const HBitmap *hb); ++ ++/** ++ * hbitmap_serialization_align: ++ * @hb: HBitmap to operate on. ++ * ++ * Required alignment of serialization chunks, used by other serialization ++ * functions. For every chunk: ++ * 1. Chunk start should be aligned to this granularity. ++ * 2. Chunk size should be aligned too, except for last chunk (for which ++ * start + count == hb->size) ++ */ ++uint64_t hbitmap_serialization_align(const HBitmap *hb); ++ ++/** ++ * hbitmap_serialization_size: ++ * @hb: HBitmap to operate on. ++ * @start: Starting bit ++ * @count: Number of bits ++ * ++ * Return number of bytes hbitmap_(de)serialize_part needs ++ */ ++uint64_t hbitmap_serialization_size(const HBitmap *hb, ++ uint64_t start, uint64_t count); ++ ++/** ++ * hbitmap_serialize_part ++ * @hb: HBitmap to operate on. ++ * @buf: Buffer to store serialized bitmap. ++ * @start: First bit to store. ++ * @count: Number of bits to store. ++ * ++ * Stores HBitmap data corresponding to given region. The format of saved data ++ * is linear sequence of bits, so it can be used by hbitmap_deserialize_part ++ * independently of endianness and size of HBitmap level array elements ++ */ ++void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf, ++ uint64_t start, uint64_t count); ++ ++/** ++ * hbitmap_deserialize_part ++ * @hb: HBitmap to operate on. ++ * @buf: Buffer to restore bitmap data from. ++ * @start: First bit to restore. ++ * @count: Number of bits to restore. ++ * @finish: Whether to call hbitmap_deserialize_finish automatically. ++ * ++ * Restores HBitmap data corresponding to given region. The format is the same ++ * as for hbitmap_serialize_part. ++ * ++ * If @finish is false, caller must call hbitmap_serialize_finish before using ++ * the bitmap. ++ */ ++void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf, ++ uint64_t start, uint64_t count, ++ bool finish); ++ ++/** ++ * hbitmap_deserialize_zeroes ++ * @hb: HBitmap to operate on. ++ * @start: First bit to restore. ++ * @count: Number of bits to restore. ++ * @finish: Whether to call hbitmap_deserialize_finish automatically. ++ * ++ * Fills the bitmap with zeroes. ++ * ++ * If @finish is false, caller must call hbitmap_serialize_finish before using ++ * the bitmap. ++ */ ++void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count, ++ bool finish); ++ ++/** ++ * hbitmap_deserialize_ones ++ * @hb: HBitmap to operate on. ++ * @start: First bit to restore. ++ * @count: Number of bits to restore. ++ * @finish: Whether to call hbitmap_deserialize_finish automatically. ++ * ++ * Fills the bitmap with ones. ++ * ++ * If @finish is false, caller must call hbitmap_serialize_finish before using ++ * the bitmap. ++ */ ++void hbitmap_deserialize_ones(HBitmap *hb, uint64_t start, uint64_t count, ++ bool finish); ++ ++/** ++ * hbitmap_deserialize_finish ++ * @hb: HBitmap to operate on. ++ * ++ * Repair HBitmap after calling hbitmap_deserialize_data. Actually, all HBitmap ++ * layers are restored here. ++ */ ++void hbitmap_deserialize_finish(HBitmap *hb); ++ ++/** ++ * hbitmap_sha256: ++ * @bitmap: HBitmap to operate on. ++ * ++ * Returns SHA256 hash of the last level. ++ */ ++char *hbitmap_sha256(const HBitmap *bitmap, Error **errp); ++ ++/** ++ * hbitmap_free: ++ * @hb: HBitmap to operate on. ++ * ++ * Free an HBitmap and all of its associated memory. ++ */ ++void hbitmap_free(HBitmap *hb); ++ ++/** ++ * hbitmap_iter_init: ++ * @hbi: HBitmapIter to initialize. ++ * @hb: HBitmap to iterate on. ++ * @first: First bit to visit (0-based, must be strictly less than the ++ * size of the bitmap). ++ * ++ * Set up @hbi to iterate on the HBitmap @hb. hbitmap_iter_next will return ++ * the lowest-numbered bit that is set in @hb, starting at @first. ++ * ++ * Concurrent setting of bits is acceptable, and will at worst cause the ++ * iteration to miss some of those bits. ++ * ++ * The concurrent resetting of bits is OK. ++ */ ++void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); ++ ++/* ++ * hbitmap_next_dirty: ++ * ++ * Find next dirty bit within selected range. If not found, return -1. ++ * ++ * @hb: The HBitmap to operate on ++ * @start: The bit to start from. ++ * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole ++ * bitmap is looked through. You can use INT64_MAX as @count to search up to ++ * the bitmap end. ++ */ ++int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count); ++ ++/* hbitmap_next_zero: ++ * ++ * Find next not dirty bit within selected range. If not found, return -1. ++ * ++ * @hb: The HBitmap to operate on ++ * @start: The bit to start from. ++ * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole ++ * bitmap is looked through. You can use INT64_MAX as @count to search up to ++ * the bitmap end. ++ */ ++int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count); ++ ++/* hbitmap_next_dirty_area: ++ * @hb: The HBitmap to operate on ++ * @start: the offset to start from ++ * @end: end of requested area ++ * @max_dirty_count: limit for out parameter dirty_count ++ * @dirty_start: on success: start of found area ++ * @dirty_count: on success: length of found area ++ * ++ * If dirty area found within [@start, @end), returns true and sets ++ * @dirty_start and @dirty_count appropriately. @dirty_count will not exceed ++ * @max_dirty_count. ++ * If dirty area was not found, returns false and leaves @dirty_start and ++ * @dirty_count unchanged. ++ */ ++bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, ++ int64_t max_dirty_count, ++ int64_t *dirty_start, int64_t *dirty_count); ++ ++/* ++ * hbitmap_status: ++ * @hb: The HBitmap to operate on ++ * @start: The bit to start from ++ * @count: Number of bits to proceed ++ * @pnum: Out-parameter. How many bits has same value starting from @start ++ * ++ * Returns true if bitmap is dirty at @start, false otherwise. ++ */ ++bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count, ++ int64_t *pnum); ++ ++/** ++ * hbitmap_iter_next: ++ * @hbi: HBitmapIter to operate on. ++ * ++ * Return the next bit that is set in @hbi's associated HBitmap, ++ * or -1 if all remaining bits are zero. ++ */ ++int64_t hbitmap_iter_next(HBitmapIter *hbi); ++ ++#endif +diff --git a/include/qemu/help_option.h b/include/qemu/help_option.h +new file mode 100644 +index 00000000..ca6389a1 +--- /dev/null ++++ b/include/qemu/help_option.h +@@ -0,0 +1,33 @@ ++#ifndef QEMU_HELP_OPTION_H ++#define QEMU_HELP_OPTION_H ++ ++/** ++ * is_help_option: ++ * @s: string to test ++ * ++ * Check whether @s is one of the standard strings which indicate ++ * that the user is asking for a list of the valid values for a ++ * command option like -cpu or -M. The current accepted strings ++ * are 'help' and '?'. '?' is deprecated (it is a shell wildcard ++ * which makes it annoying to use in a reliable way) but provided ++ * for backwards compatibility. ++ * ++ * Returns: true if @s is a request for a list. ++ */ ++static inline bool is_help_option(const char *s) ++{ ++ return !strcmp(s, "?") || !strcmp(s, "help"); ++} ++ ++static inline int starts_with_help_option(const char *s) ++{ ++ if (*s == '?') { ++ return 1; ++ } ++ if (g_str_has_prefix(s, "help")) { ++ return 4; ++ } ++ return 0; ++} ++ ++#endif +diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h +new file mode 100644 +index 00000000..ead97d35 +--- /dev/null ++++ b/include/qemu/host-utils.h +@@ -0,0 +1,861 @@ ++/* ++ * Utility compute operations used by translated code. ++ * ++ * Copyright (c) 2007 Thiemo Seufer ++ * Copyright (c) 2007 Jocelyn Mayer ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++/* Portions of this work are licensed under the terms of the GNU GPL, ++ * version 2 or later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef HOST_UTILS_H ++#define HOST_UTILS_H ++ ++#include "qemu/bswap.h" ++#include "qemu/int128.h" ++ ++#ifdef CONFIG_INT128 ++static inline void mulu64(uint64_t *plow, uint64_t *phigh, ++ uint64_t a, uint64_t b) ++{ ++ __uint128_t r = (__uint128_t)a * b; ++ *plow = r; ++ *phigh = r >> 64; ++} ++ ++static inline void muls64(uint64_t *plow, uint64_t *phigh, ++ int64_t a, int64_t b) ++{ ++ __int128_t r = (__int128_t)a * b; ++ *plow = r; ++ *phigh = r >> 64; ++} ++ ++/* compute with 96 bit intermediate result: (a*b)/c */ ++static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) ++{ ++ return (__int128_t)a * b / c; ++} ++ ++static inline uint64_t muldiv64_round_up(uint64_t a, uint32_t b, uint32_t c) ++{ ++ return ((__int128_t)a * b + c - 1) / c; ++} ++ ++static inline uint64_t divu128(uint64_t *plow, uint64_t *phigh, ++ uint64_t divisor) ++{ ++ __uint128_t dividend = ((__uint128_t)*phigh << 64) | *plow; ++ __uint128_t result = dividend / divisor; ++ ++ *plow = result; ++ *phigh = result >> 64; ++ return dividend % divisor; ++} ++ ++static inline int64_t divs128(uint64_t *plow, int64_t *phigh, ++ int64_t divisor) ++{ ++ __int128_t dividend = ((__int128_t)*phigh << 64) | *plow; ++ __int128_t result = dividend / divisor; ++ ++ *plow = result; ++ *phigh = result >> 64; ++ return dividend % divisor; ++} ++#else ++void muls64(uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b); ++void mulu64(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b); ++uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor); ++int64_t divs128(uint64_t *plow, int64_t *phigh, int64_t divisor); ++ ++static inline uint64_t muldiv64_rounding(uint64_t a, uint32_t b, uint32_t c, ++ bool round_up) ++{ ++ union { ++ uint64_t ll; ++ struct { ++#if HOST_BIG_ENDIAN ++ uint32_t high, low; ++#else ++ uint32_t low, high; ++#endif ++ } l; ++ } u, res; ++ uint64_t rl, rh; ++ ++ u.ll = a; ++ rl = (uint64_t)u.l.low * (uint64_t)b; ++ if (round_up) { ++ rl += c - 1; ++ } ++ rh = (uint64_t)u.l.high * (uint64_t)b; ++ rh += (rl >> 32); ++ res.l.high = rh / c; ++ res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c; ++ return res.ll; ++} ++ ++static inline uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) ++{ ++ return muldiv64_rounding(a, b, c, false); ++} ++ ++static inline uint64_t muldiv64_round_up(uint64_t a, uint32_t b, uint32_t c) ++{ ++ return muldiv64_rounding(a, b, c, true); ++} ++#endif ++ ++/** ++ * clz8 - count leading zeros in a 8-bit value. ++ * @val: The value to search ++ * ++ * Returns 8 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ * ++ * Note that the GCC builtin will upcast its argument to an `unsigned int` ++ * so this function subtracts off the number of prepended zeroes. ++ */ ++static inline int clz8(uint8_t val) ++{ ++ return val ? __builtin_clz(val) - 24 : 8; ++} ++ ++/** ++ * clz16 - count leading zeros in a 16-bit value. ++ * @val: The value to search ++ * ++ * Returns 16 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ * ++ * Note that the GCC builtin will upcast its argument to an `unsigned int` ++ * so this function subtracts off the number of prepended zeroes. ++ */ ++static inline int clz16(uint16_t val) ++{ ++ return val ? __builtin_clz(val) - 16 : 16; ++} ++ ++/** ++ * clz32 - count leading zeros in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns 32 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int clz32(uint32_t val) ++{ ++ return val ? __builtin_clz(val) : 32; ++} ++ ++/** ++ * clo32 - count leading ones in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns 32 if the value is -1. ++ */ ++static inline int clo32(uint32_t val) ++{ ++ return clz32(~val); ++} ++ ++/** ++ * clz64 - count leading zeros in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns 64 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int clz64(uint64_t val) ++{ ++ return val ? __builtin_clzll(val) : 64; ++} ++ ++/** ++ * clo64 - count leading ones in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns 64 if the value is -1. ++ */ ++static inline int clo64(uint64_t val) ++{ ++ return clz64(~val); ++} ++ ++/** ++ * ctz8 - count trailing zeros in a 8-bit value. ++ * @val: The value to search ++ * ++ * Returns 8 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int ctz8(uint8_t val) ++{ ++ return val ? __builtin_ctz(val) : 8; ++} ++ ++/** ++ * ctz16 - count trailing zeros in a 16-bit value. ++ * @val: The value to search ++ * ++ * Returns 16 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int ctz16(uint16_t val) ++{ ++ return val ? __builtin_ctz(val) : 16; ++} ++ ++/** ++ * ctz32 - count trailing zeros in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns 32 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int ctz32(uint32_t val) ++{ ++ return val ? __builtin_ctz(val) : 32; ++} ++ ++/** ++ * cto32 - count trailing ones in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns 32 if the value is -1. ++ */ ++static inline int cto32(uint32_t val) ++{ ++ return ctz32(~val); ++} ++ ++/** ++ * ctz64 - count trailing zeros in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns 64 if the value is zero. Note that the GCC builtin is ++ * undefined if the value is zero. ++ */ ++static inline int ctz64(uint64_t val) ++{ ++ return val ? __builtin_ctzll(val) : 64; ++} ++ ++/** ++ * cto64 - count trailing ones in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns 64 if the value is -1. ++ */ ++static inline int cto64(uint64_t val) ++{ ++ return ctz64(~val); ++} ++ ++/** ++ * clrsb32 - count leading redundant sign bits in a 32-bit value. ++ * @val: The value to search ++ * ++ * Returns the number of bits following the sign bit that are equal to it. ++ * No special cases; output range is [0-31]. ++ */ ++static inline int clrsb32(uint32_t val) ++{ ++#if __has_builtin(__builtin_clrsb) || !defined(__clang__) ++ return __builtin_clrsb(val); ++#else ++ return clz32(val ^ ((int32_t)val >> 1)) - 1; ++#endif ++} ++ ++/** ++ * clrsb64 - count leading redundant sign bits in a 64-bit value. ++ * @val: The value to search ++ * ++ * Returns the number of bits following the sign bit that are equal to it. ++ * No special cases; output range is [0-63]. ++ */ ++static inline int clrsb64(uint64_t val) ++{ ++#if __has_builtin(__builtin_clrsbll) || !defined(__clang__) ++ return __builtin_clrsbll(val); ++#else ++ return clz64(val ^ ((int64_t)val >> 1)) - 1; ++#endif ++} ++ ++/** ++ * ctpop8 - count the population of one bits in an 8-bit value. ++ * @val: The value to search ++ */ ++static inline int ctpop8(uint8_t val) ++{ ++ return __builtin_popcount(val); ++} ++ ++/** ++ * ctpop16 - count the population of one bits in a 16-bit value. ++ * @val: The value to search ++ */ ++static inline int ctpop16(uint16_t val) ++{ ++ return __builtin_popcount(val); ++} ++ ++/** ++ * ctpop32 - count the population of one bits in a 32-bit value. ++ * @val: The value to search ++ */ ++static inline int ctpop32(uint32_t val) ++{ ++ return __builtin_popcount(val); ++} ++ ++/** ++ * ctpop64 - count the population of one bits in a 64-bit value. ++ * @val: The value to search ++ */ ++static inline int ctpop64(uint64_t val) ++{ ++ return __builtin_popcountll(val); ++} ++ ++/** ++ * revbit8 - reverse the bits in an 8-bit value. ++ * @x: The value to modify. ++ */ ++static inline uint8_t revbit8(uint8_t x) ++{ ++#if __has_builtin(__builtin_bitreverse8) ++ return __builtin_bitreverse8(x); ++#else ++ /* Assign the correct nibble position. */ ++ x = ((x & 0xf0) >> 4) ++ | ((x & 0x0f) << 4); ++ /* Assign the correct bit position. */ ++ x = ((x & 0x88) >> 3) ++ | ((x & 0x44) >> 1) ++ | ((x & 0x22) << 1) ++ | ((x & 0x11) << 3); ++ return x; ++#endif ++} ++ ++/** ++ * revbit16 - reverse the bits in a 16-bit value. ++ * @x: The value to modify. ++ */ ++static inline uint16_t revbit16(uint16_t x) ++{ ++#if __has_builtin(__builtin_bitreverse16) ++ return __builtin_bitreverse16(x); ++#else ++ /* Assign the correct byte position. */ ++ x = bswap16(x); ++ /* Assign the correct nibble position. */ ++ x = ((x & 0xf0f0) >> 4) ++ | ((x & 0x0f0f) << 4); ++ /* Assign the correct bit position. */ ++ x = ((x & 0x8888) >> 3) ++ | ((x & 0x4444) >> 1) ++ | ((x & 0x2222) << 1) ++ | ((x & 0x1111) << 3); ++ return x; ++#endif ++} ++ ++/** ++ * revbit32 - reverse the bits in a 32-bit value. ++ * @x: The value to modify. ++ */ ++static inline uint32_t revbit32(uint32_t x) ++{ ++#if __has_builtin(__builtin_bitreverse32) ++ return __builtin_bitreverse32(x); ++#else ++ /* Assign the correct byte position. */ ++ x = bswap32(x); ++ /* Assign the correct nibble position. */ ++ x = ((x & 0xf0f0f0f0u) >> 4) ++ | ((x & 0x0f0f0f0fu) << 4); ++ /* Assign the correct bit position. */ ++ x = ((x & 0x88888888u) >> 3) ++ | ((x & 0x44444444u) >> 1) ++ | ((x & 0x22222222u) << 1) ++ | ((x & 0x11111111u) << 3); ++ return x; ++#endif ++} ++ ++/** ++ * revbit64 - reverse the bits in a 64-bit value. ++ * @x: The value to modify. ++ */ ++static inline uint64_t revbit64(uint64_t x) ++{ ++#if __has_builtin(__builtin_bitreverse64) ++ return __builtin_bitreverse64(x); ++#else ++ /* Assign the correct byte position. */ ++ x = bswap64(x); ++ /* Assign the correct nibble position. */ ++ x = ((x & 0xf0f0f0f0f0f0f0f0ull) >> 4) ++ | ((x & 0x0f0f0f0f0f0f0f0full) << 4); ++ /* Assign the correct bit position. */ ++ x = ((x & 0x8888888888888888ull) >> 3) ++ | ((x & 0x4444444444444444ull) >> 1) ++ | ((x & 0x2222222222222222ull) << 1) ++ | ((x & 0x1111111111111111ull) << 3); ++ return x; ++#endif ++} ++ ++/** ++ * Return the absolute value of a 64-bit integer as an unsigned 64-bit value ++ */ ++static inline uint64_t uabs64(int64_t v) ++{ ++ return v < 0 ? -v : v; ++} ++ ++/** ++ * sadd32_overflow - addition with overflow indication ++ * @x, @y: addends ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x + @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool sadd32_overflow(int32_t x, int32_t y, int32_t *ret) ++{ ++ return __builtin_add_overflow(x, y, ret); ++} ++ ++/** ++ * sadd64_overflow - addition with overflow indication ++ * @x, @y: addends ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x + @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool sadd64_overflow(int64_t x, int64_t y, int64_t *ret) ++{ ++ return __builtin_add_overflow(x, y, ret); ++} ++ ++/** ++ * uadd32_overflow - addition with overflow indication ++ * @x, @y: addends ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x + @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool uadd32_overflow(uint32_t x, uint32_t y, uint32_t *ret) ++{ ++ return __builtin_add_overflow(x, y, ret); ++} ++ ++/** ++ * uadd64_overflow - addition with overflow indication ++ * @x, @y: addends ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x + @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool uadd64_overflow(uint64_t x, uint64_t y, uint64_t *ret) ++{ ++ return __builtin_add_overflow(x, y, ret); ++} ++ ++/** ++ * ssub32_overflow - subtraction with overflow indication ++ * @x: Minuend ++ * @y: Subtrahend ++ * @ret: Output for difference ++ * ++ * Computes *@ret = @x - @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool ssub32_overflow(int32_t x, int32_t y, int32_t *ret) ++{ ++ return __builtin_sub_overflow(x, y, ret); ++} ++ ++/** ++ * ssub64_overflow - subtraction with overflow indication ++ * @x: Minuend ++ * @y: Subtrahend ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x - @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool ssub64_overflow(int64_t x, int64_t y, int64_t *ret) ++{ ++ return __builtin_sub_overflow(x, y, ret); ++} ++ ++/** ++ * usub32_overflow - subtraction with overflow indication ++ * @x: Minuend ++ * @y: Subtrahend ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x - @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool usub32_overflow(uint32_t x, uint32_t y, uint32_t *ret) ++{ ++ return __builtin_sub_overflow(x, y, ret); ++} ++ ++/** ++ * usub64_overflow - subtraction with overflow indication ++ * @x: Minuend ++ * @y: Subtrahend ++ * @ret: Output for sum ++ * ++ * Computes *@ret = @x - @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool usub64_overflow(uint64_t x, uint64_t y, uint64_t *ret) ++{ ++ return __builtin_sub_overflow(x, y, ret); ++} ++ ++/** ++ * smul32_overflow - multiplication with overflow indication ++ * @x, @y: Input multipliers ++ * @ret: Output for product ++ * ++ * Computes *@ret = @x * @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool smul32_overflow(int32_t x, int32_t y, int32_t *ret) ++{ ++ return __builtin_mul_overflow(x, y, ret); ++} ++ ++/** ++ * smul64_overflow - multiplication with overflow indication ++ * @x, @y: Input multipliers ++ * @ret: Output for product ++ * ++ * Computes *@ret = @x * @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool smul64_overflow(int64_t x, int64_t y, int64_t *ret) ++{ ++ return __builtin_mul_overflow(x, y, ret); ++} ++ ++/** ++ * umul32_overflow - multiplication with overflow indication ++ * @x, @y: Input multipliers ++ * @ret: Output for product ++ * ++ * Computes *@ret = @x * @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool umul32_overflow(uint32_t x, uint32_t y, uint32_t *ret) ++{ ++ return __builtin_mul_overflow(x, y, ret); ++} ++ ++/** ++ * umul64_overflow - multiplication with overflow indication ++ * @x, @y: Input multipliers ++ * @ret: Output for product ++ * ++ * Computes *@ret = @x * @y, and returns true if and only if that ++ * value has been truncated. ++ */ ++static inline bool umul64_overflow(uint64_t x, uint64_t y, uint64_t *ret) ++{ ++ return __builtin_mul_overflow(x, y, ret); ++} ++ ++/* ++ * Unsigned 128x64 multiplication. ++ * Returns true if the result got truncated to 128 bits. ++ * Otherwise, returns false and the multiplication result via plow and phigh. ++ */ ++static inline bool mulu128(uint64_t *plow, uint64_t *phigh, uint64_t factor) ++{ ++#if defined(CONFIG_INT128) ++ bool res; ++ __uint128_t r; ++ __uint128_t f = ((__uint128_t)*phigh << 64) | *plow; ++ res = __builtin_mul_overflow(f, factor, &r); ++ ++ *plow = r; ++ *phigh = r >> 64; ++ ++ return res; ++#else ++ uint64_t dhi = *phigh; ++ uint64_t dlo = *plow; ++ uint64_t ahi; ++ uint64_t blo, bhi; ++ ++ if (dhi == 0) { ++ mulu64(plow, phigh, dlo, factor); ++ return false; ++ } ++ ++ mulu64(plow, &ahi, dlo, factor); ++ mulu64(&blo, &bhi, dhi, factor); ++ ++ return uadd64_overflow(ahi, blo, phigh) || bhi != 0; ++#endif ++} ++ ++/** ++ * uadd64_carry - addition with carry-in and carry-out ++ * @x, @y: addends ++ * @pcarry: in-out carry value ++ * ++ * Computes @x + @y + *@pcarry, placing the carry-out back ++ * into *@pcarry and returning the 64-bit sum. ++ */ ++static inline uint64_t uadd64_carry(uint64_t x, uint64_t y, bool *pcarry) ++{ ++#if __has_builtin(__builtin_addcll) ++ unsigned long long c = *pcarry; ++ x = __builtin_addcll(x, y, c, &c); ++ *pcarry = c & 1; ++ return x; ++#else ++ bool c = *pcarry; ++ /* This is clang's internal expansion of __builtin_addc. */ ++ c = uadd64_overflow(x, c, &x); ++ c |= uadd64_overflow(x, y, &x); ++ *pcarry = c; ++ return x; ++#endif ++} ++ ++/** ++ * usub64_borrow - subtraction with borrow-in and borrow-out ++ * @x, @y: addends ++ * @pborrow: in-out borrow value ++ * ++ * Computes @x - @y - *@pborrow, placing the borrow-out back ++ * into *@pborrow and returning the 64-bit sum. ++ */ ++static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow) ++{ ++#if __has_builtin(__builtin_subcll) && !defined(BUILTIN_SUBCLL_BROKEN) ++ unsigned long long b = *pborrow; ++ x = __builtin_subcll(x, y, b, &b); ++ *pborrow = b & 1; ++ return x; ++#else ++ bool b = *pborrow; ++ b = usub64_overflow(x, b, &x); ++ b |= usub64_overflow(x, y, &x); ++ *pborrow = b; ++ return x; ++#endif ++} ++ ++/* Host type specific sizes of these routines. */ ++ ++#if ULONG_MAX == UINT32_MAX ++# define clzl clz32 ++# define ctzl ctz32 ++# define clol clo32 ++# define ctol cto32 ++# define ctpopl ctpop32 ++# define revbitl revbit32 ++#elif ULONG_MAX == UINT64_MAX ++# define clzl clz64 ++# define ctzl ctz64 ++# define clol clo64 ++# define ctol cto64 ++# define ctpopl ctpop64 ++# define revbitl revbit64 ++#else ++# error Unknown sizeof long ++#endif ++ ++static inline bool is_power_of_2(uint64_t value) ++{ ++ if (!value) { ++ return false; ++ } ++ ++ return !(value & (value - 1)); ++} ++ ++/** ++ * Return @value rounded down to the nearest power of two or zero. ++ */ ++static inline uint64_t pow2floor(uint64_t value) ++{ ++ if (!value) { ++ /* Avoid undefined shift by 64 */ ++ return 0; ++ } ++ return 0x8000000000000000ull >> clz64(value); ++} ++ ++/* ++ * Return @value rounded up to the nearest power of two modulo 2^64. ++ * This is *zero* for @value > 2^63, so be careful. ++ */ ++static inline uint64_t pow2ceil(uint64_t value) ++{ ++ int n = clz64(value - 1); ++ ++ if (!n) { ++ /* ++ * @value - 1 has no leading zeroes, thus @value - 1 >= 2^63 ++ * Therefore, either @value == 0 or @value > 2^63. ++ * If it's 0, return 1, else return 0. ++ */ ++ return !value; ++ } ++ return 0x8000000000000000ull >> (n - 1); ++} ++ ++static inline uint32_t pow2roundup32(uint32_t x) ++{ ++ x |= (x >> 1); ++ x |= (x >> 2); ++ x |= (x >> 4); ++ x |= (x >> 8); ++ x |= (x >> 16); ++ return x + 1; ++} ++ ++/** ++ * urshift - 128-bit Unsigned Right Shift. ++ * @plow: in/out - lower 64-bit integer. ++ * @phigh: in/out - higher 64-bit integer. ++ * @shift: in - bytes to shift, between 0 and 127. ++ * ++ * Result is zero-extended and stored in plow/phigh, which are ++ * input/output variables. Shift values outside the range will ++ * be mod to 128. In other words, the caller is responsible to ++ * verify/assert both the shift range and plow/phigh pointers. ++ */ ++void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift); ++ ++/** ++ * ulshift - 128-bit Unsigned Left Shift. ++ * @plow: in/out - lower 64-bit integer. ++ * @phigh: in/out - higher 64-bit integer. ++ * @shift: in - bytes to shift, between 0 and 127. ++ * @overflow: out - true if any 1-bit is shifted out. ++ * ++ * Result is zero-extended and stored in plow/phigh, which are ++ * input/output variables. Shift values outside the range will ++ * be mod to 128. In other words, the caller is responsible to ++ * verify/assert both the shift range and plow/phigh pointers. ++ */ ++void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow); ++ ++/* From the GNU Multi Precision Library - longlong.h __udiv_qrnnd ++ * (https://gmplib.org/repo/gmp/file/tip/longlong.h) ++ * ++ * Licensed under the GPLv2/LGPLv3 ++ */ ++static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, ++ uint64_t n0, uint64_t d) ++{ ++#if defined(__x86_64__) ++ uint64_t q; ++ asm("divq %4" : "=a"(q), "=d"(*r) : "0"(n0), "1"(n1), "rm"(d)); ++ return q; ++#elif defined(__s390x__) && !defined(__clang__) ++ /* Need to use a TImode type to get an even register pair for DLGR. */ ++ unsigned __int128 n = (unsigned __int128)n1 << 64 | n0; ++ asm("dlgr %0, %1" : "+r"(n) : "r"(d)); ++ *r = n >> 64; ++ return n; ++#elif defined(_ARCH_PPC64) && defined(_ARCH_PWR7) ++ /* From Power ISA 2.06, programming note for divdeu. */ ++ uint64_t q1, q2, Q, r1, r2, R; ++ asm("divdeu %0,%2,%4; divdu %1,%3,%4" ++ : "=&r"(q1), "=r"(q2) ++ : "r"(n1), "r"(n0), "r"(d)); ++ r1 = -(q1 * d); /* low part of (n1<<64) - (q1 * d) */ ++ r2 = n0 - (q2 * d); ++ Q = q1 + q2; ++ R = r1 + r2; ++ if (R >= d || R < r2) { /* overflow implies R > d */ ++ Q += 1; ++ R -= d; ++ } ++ *r = R; ++ return Q; ++#else ++ uint64_t d0, d1, q0, q1, r1, r0, m; ++ ++ d0 = (uint32_t)d; ++ d1 = d >> 32; ++ ++ r1 = n1 % d1; ++ q1 = n1 / d1; ++ m = q1 * d0; ++ r1 = (r1 << 32) | (n0 >> 32); ++ if (r1 < m) { ++ q1 -= 1; ++ r1 += d; ++ if (r1 >= d) { ++ if (r1 < m) { ++ q1 -= 1; ++ r1 += d; ++ } ++ } ++ } ++ r1 -= m; ++ ++ r0 = r1 % d1; ++ q0 = r1 / d1; ++ m = q0 * d0; ++ r0 = (r0 << 32) | (uint32_t)n0; ++ if (r0 < m) { ++ q0 -= 1; ++ r0 += d; ++ if (r0 >= d) { ++ if (r0 < m) { ++ q0 -= 1; ++ r0 += d; ++ } ++ } ++ } ++ r0 -= m; ++ ++ *r = r0; ++ return (q1 << 32) | q0; ++#endif ++} ++ ++Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor); ++Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); ++#endif +diff --git a/include/qemu/id.h b/include/qemu/id.h +new file mode 100644 +index 00000000..46b759b2 +--- /dev/null ++++ b/include/qemu/id.h +@@ -0,0 +1,15 @@ ++#ifndef QEMU_ID_H ++#define QEMU_ID_H ++ ++typedef enum IdSubSystems { ++ ID_QDEV, ++ ID_BLOCK, ++ ID_CHR, ++ ID_NET, ++ ID_MAX /* last element, used as array size */ ++} IdSubSystems; ++ ++char *id_generate(IdSubSystems id); ++bool id_wellformed(const char *id); ++ ++#endif +diff --git a/include/qemu/iov.h b/include/qemu/iov.h +new file mode 100644 +index 00000000..63a1c019 +--- /dev/null ++++ b/include/qemu/iov.h +@@ -0,0 +1,250 @@ ++/* ++ * Helpers for using (partial) iovecs. ++ * ++ * Copyright (C) 2010 Red Hat, Inc. ++ * ++ * Author(s): ++ * Amit Shah ++ * Michael Tokarev ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ */ ++ ++#ifndef IOV_H ++#define IOV_H ++ ++/** ++ * count and return data size, in bytes, of an iovec ++ * starting at `iov' of `iov_cnt' number of elements. ++ */ ++size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); ++ ++/** ++ * Copy from single continuous buffer to scatter-gather vector of buffers ++ * (iovec) and back like memcpy() between two continuous memory regions. ++ * Data in single continuous buffer starting at address `buf' and ++ * `bytes' bytes long will be copied to/from an iovec `iov' with ++ * `iov_cnt' number of elements, starting at byte position `offset' ++ * within the iovec. If the iovec does not contain enough space, ++ * only part of data will be copied, up to the end of the iovec. ++ * Number of bytes actually copied will be returned, which is ++ * min(bytes, iov_size(iov)-offset) ++ * `Offset' must point to the inside of iovec. ++ */ ++size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, const void *buf, size_t bytes); ++size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, void *buf, size_t bytes); ++ ++static inline size_t ++iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, const void *buf, size_t bytes) ++{ ++ if (__builtin_constant_p(bytes) && iov_cnt && ++ offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) { ++ memcpy(iov[0].iov_base + offset, buf, bytes); ++ return bytes; ++ } else { ++ return iov_from_buf_full(iov, iov_cnt, offset, buf, bytes); ++ } ++} ++ ++static inline size_t ++iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, void *buf, size_t bytes) ++{ ++ if (__builtin_constant_p(bytes) && iov_cnt && ++ offset <= iov[0].iov_len && bytes <= iov[0].iov_len - offset) { ++ memcpy(buf, iov[0].iov_base + offset, bytes); ++ return bytes; ++ } else { ++ return iov_to_buf_full(iov, iov_cnt, offset, buf, bytes); ++ } ++} ++ ++/** ++ * Set data bytes pointed out by iovec `iov' of size `iov_cnt' elements, ++ * starting at byte offset `start', to value `fillc', repeating it ++ * `bytes' number of times. `Offset' must point to the inside of iovec. ++ * If `bytes' is large enough, only last bytes portion of iovec, ++ * up to the end of it, will be filled with the specified value. ++ * Function return actual number of bytes processed, which is ++ * min(size, iov_size(iov) - offset). ++ */ ++size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, int fillc, size_t bytes); ++ ++/* ++ * Send/recv data from/to iovec buffers directly ++ * ++ * `offset' bytes in the beginning of iovec buffer are skipped and ++ * next `bytes' bytes are used, which must be within data of iovec. ++ * ++ * r = iov_send_recv(sockfd, iov, iovcnt, offset, bytes, true); ++ * ++ * is logically equivalent to ++ * ++ * char *buf = malloc(bytes); ++ * iov_to_buf(iov, iovcnt, offset, buf, bytes); ++ * r = send(sockfd, buf, bytes, 0); ++ * free(buf); ++ * ++ * For iov_send_recv() _whole_ area being sent or received ++ * should be within the iovec, not only beginning of it. ++ */ ++ssize_t iov_send_recv(int sockfd, const struct iovec *iov, unsigned iov_cnt, ++ size_t offset, size_t bytes, bool do_send); ++#define iov_recv(sockfd, iov, iov_cnt, offset, bytes) \ ++ iov_send_recv(sockfd, iov, iov_cnt, offset, bytes, false) ++#define iov_send(sockfd, iov, iov_cnt, offset, bytes) \ ++ iov_send_recv(sockfd, iov, iov_cnt, offset, bytes, true) ++ ++/** ++ * Produce a text hexdump of iovec `iov' with `iov_cnt' number of elements ++ * in file `fp', prefixing each line with `prefix' and processing not more ++ * than `limit' data bytes. ++ */ ++void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, ++ FILE *fp, const char *prefix, size_t limit); ++ ++/* ++ * Partial copy of vector from iov to dst_iov (data is not copied). ++ * dst_iov overlaps iov at a specified offset. ++ * size of dst_iov is at most bytes. dst vector count is returned. ++ */ ++unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, ++ const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, size_t bytes); ++ ++/* ++ * Remove a given number of bytes from the front or back of a vector. ++ * This may update iov and/or iov_cnt to exclude iovec elements that are ++ * no longer required. ++ * ++ * The number of bytes actually discarded is returned. This number may be ++ * smaller than requested if the vector is too small. ++ */ ++size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt, ++ size_t bytes); ++size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt, ++ size_t bytes); ++ ++/* Information needed to undo an iov_discard_*() operation */ ++typedef struct { ++ struct iovec *modified_iov; ++ struct iovec orig; ++} IOVDiscardUndo; ++ ++/* ++ * Undo an iov_discard_front_undoable() or iov_discard_back_undoable() ++ * operation. If multiple operations are made then each one needs a separate ++ * IOVDiscardUndo and iov_discard_undo() must be called in the reverse order ++ * that the operations were made. ++ */ ++void iov_discard_undo(IOVDiscardUndo *undo); ++ ++/* ++ * Undoable versions of iov_discard_front() and iov_discard_back(). Use ++ * iov_discard_undo() to reset to the state before the discard operations. ++ */ ++size_t iov_discard_front_undoable(struct iovec **iov, unsigned int *iov_cnt, ++ size_t bytes, IOVDiscardUndo *undo); ++size_t iov_discard_back_undoable(struct iovec *iov, unsigned int *iov_cnt, ++ size_t bytes, IOVDiscardUndo *undo); ++ ++typedef struct QEMUIOVector { ++ struct iovec *iov; ++ int niov; ++ ++ /* ++ * For external @iov (qemu_iovec_init_external()) or allocated @iov ++ * (qemu_iovec_init()), @size is the cumulative size of iovecs and ++ * @local_iov is invalid and unused. ++ * ++ * For embedded @iov (QEMU_IOVEC_INIT_BUF() or qemu_iovec_init_buf()), ++ * @iov is equal to &@local_iov, and @size is valid, as it has same ++ * offset and type as @local_iov.iov_len, which is guaranteed by ++ * static assertion below. ++ * ++ * @nalloc is always valid and is -1 both for embedded and external ++ * cases. It is included in the union only to ensure the padding prior ++ * to the @size field will not result in a 0-length array. ++ */ ++ union { ++ struct { ++ int nalloc; ++ struct iovec local_iov; ++ }; ++ struct { ++ char __pad[sizeof(int) + offsetof(struct iovec, iov_len)]; ++ size_t size; ++ }; ++ }; ++} QEMUIOVector; ++ ++QEMU_BUILD_BUG_ON(offsetof(QEMUIOVector, size) != ++ offsetof(QEMUIOVector, local_iov.iov_len)); ++ ++#define QEMU_IOVEC_INIT_BUF(self, buf, len) \ ++{ \ ++ .iov = &(self).local_iov, \ ++ .niov = 1, \ ++ .nalloc = -1, \ ++ .local_iov = { \ ++ .iov_base = (void *)(buf), /* cast away const */ \ ++ .iov_len = (len), \ ++ }, \ ++} ++ ++/* ++ * qemu_iovec_init_buf ++ * ++ * Initialize embedded QEMUIOVector. ++ * ++ * Note: "const" is used over @buf pointer to make it simple to pass ++ * const pointers, appearing in read functions. Then this "const" is ++ * cast away by QEMU_IOVEC_INIT_BUF(). ++ */ ++static inline void qemu_iovec_init_buf(QEMUIOVector *qiov, ++ const void *buf, size_t len) ++{ ++ *qiov = (QEMUIOVector) QEMU_IOVEC_INIT_BUF(*qiov, buf, len); ++} ++ ++static inline void *qemu_iovec_buf(QEMUIOVector *qiov) ++{ ++ /* Only supports embedded iov */ ++ assert(qiov->nalloc == -1 && qiov->iov == &qiov->local_iov); ++ ++ return qiov->local_iov.iov_base; ++} ++ ++void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); ++void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); ++void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, ++ size_t offset, size_t len); ++struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, ++ size_t offset, size_t len, ++ size_t *head, size_t *tail, int *niov); ++int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len); ++void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); ++void qemu_iovec_concat(QEMUIOVector *dst, ++ QEMUIOVector *src, size_t soffset, size_t sbytes); ++size_t qemu_iovec_concat_iov(QEMUIOVector *dst, ++ struct iovec *src_iov, unsigned int src_cnt, ++ size_t soffset, size_t sbytes); ++bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t qiov_offeset, size_t bytes); ++void qemu_iovec_destroy(QEMUIOVector *qiov); ++void qemu_iovec_reset(QEMUIOVector *qiov); ++size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, ++ void *buf, size_t bytes); ++size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset, ++ const void *buf, size_t bytes); ++size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, ++ int fillc, size_t bytes); ++ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b); ++void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf); ++void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes); ++ ++#endif +diff --git a/include/qemu/job.h b/include/qemu/job.h +new file mode 100644 +index 00000000..2b873f25 +--- /dev/null ++++ b/include/qemu/job.h +@@ -0,0 +1,731 @@ ++/* ++ * Declarations for background jobs ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef JOB_H ++#define JOB_H ++ ++#include "qapi/qapi-types-job.h" ++#include "qemu/queue.h" ++#include "qemu/progress_meter.h" ++#include "qemu/coroutine.h" ++#include "block/aio.h" ++ ++typedef struct JobDriver JobDriver; ++typedef struct JobTxn JobTxn; ++ ++ ++/** ++ * Long-running operation. ++ */ ++typedef struct Job { ++ ++ /* Fields set at initialization (job_create), and never modified */ ++ ++ /** The ID of the job. May be NULL for internal jobs. */ ++ char *id; ++ ++ /** ++ * The type of this job. ++ * All callbacks are called with job_mutex *not* held. ++ */ ++ const JobDriver *driver; ++ ++ /** ++ * The coroutine that executes the job. If not NULL, it is reentered when ++ * busy is false and the job is cancelled. ++ * Initialized in job_start() ++ */ ++ Coroutine *co; ++ ++ /** True if this job should automatically finalize itself */ ++ bool auto_finalize; ++ ++ /** True if this job should automatically dismiss itself */ ++ bool auto_dismiss; ++ ++ /** ++ * The completion function that will be called when the job completes. ++ */ ++ BlockCompletionFunc *cb; ++ ++ /** The opaque value that is passed to the completion function. */ ++ void *opaque; ++ ++ /* ProgressMeter API is thread-safe */ ++ ProgressMeter progress; ++ ++ /** ++ * AioContext to run the job coroutine in. ++ * The job Aiocontext can be read when holding *either* ++ * the BQL (so we are in the main loop) or the job_mutex. ++ * It can only be written when we hold *both* BQL ++ * and the job_mutex. ++ */ ++ AioContext *aio_context; ++ ++ ++ /** Protected by job_mutex */ ++ ++ /** Reference count of the block job */ ++ int refcnt; ++ ++ /** Current state; See @JobStatus for details. */ ++ JobStatus status; ++ ++ /** ++ * Timer that is used by @job_sleep_ns. Accessed under job_mutex (in ++ * job.c). ++ */ ++ QEMUTimer sleep_timer; ++ ++ /** ++ * Counter for pause request. If non-zero, the block job is either paused, ++ * or if busy == true will pause itself as soon as possible. ++ */ ++ int pause_count; ++ ++ /** ++ * Set to false by the job while the coroutine has yielded and may be ++ * re-entered by job_enter(). There may still be I/O or event loop activity ++ * pending. Accessed under job_mutex. ++ * ++ * When the job is deferred to the main loop, busy is true as long as the ++ * bottom half is still pending. ++ */ ++ bool busy; ++ ++ /** ++ * Set to true by the job while it is in a quiescent state, where ++ * no I/O or event loop activity is pending. ++ */ ++ bool paused; ++ ++ /** ++ * Set to true if the job is paused by user. Can be unpaused with the ++ * block-job-resume QMP command. ++ */ ++ bool user_paused; ++ ++ /** ++ * Set to true if the job should cancel itself. The flag must ++ * always be tested just before toggling the busy flag from false ++ * to true. After a job has been cancelled, it should only yield ++ * if #aio_poll will ("sooner or later") reenter the coroutine. ++ */ ++ bool cancelled; ++ ++ /** ++ * Set to true if the job should abort immediately without waiting ++ * for data to be in sync. ++ */ ++ bool force_cancel; ++ ++ /** Set to true when the job has deferred work to the main loop. */ ++ bool deferred_to_main_loop; ++ ++ /** ++ * Return code from @run and/or @prepare callback(s). ++ * Not final until the job has reached the CONCLUDED status. ++ * 0 on success, -errno on failure. ++ */ ++ int ret; ++ ++ /** ++ * Error object for a failed job. ++ * If job->ret is nonzero and an error object was not set, it will be set ++ * to strerror(-job->ret) during job_completed. ++ */ ++ Error *err; ++ ++ /** Notifiers called when a cancelled job is finalised */ ++ NotifierList on_finalize_cancelled; ++ ++ /** Notifiers called when a successfully completed job is finalised */ ++ NotifierList on_finalize_completed; ++ ++ /** Notifiers called when the job transitions to PENDING */ ++ NotifierList on_pending; ++ ++ /** Notifiers called when the job transitions to READY */ ++ NotifierList on_ready; ++ ++ /** Notifiers called when the job coroutine yields or terminates */ ++ NotifierList on_idle; ++ ++ /** Element of the list of jobs */ ++ QLIST_ENTRY(Job) job_list; ++ ++ /** Transaction this job is part of */ ++ JobTxn *txn; ++ ++ /** Element of the list of jobs in a job transaction */ ++ QLIST_ENTRY(Job) txn_list; ++} Job; ++ ++/** ++ * Callbacks and other information about a Job driver. ++ * All callbacks are invoked with job_mutex *not* held. ++ */ ++struct JobDriver { ++ ++ /* ++ * These fields are initialized when this object is created, ++ * and are never changed afterwards ++ */ ++ ++ /** Derived Job struct size */ ++ size_t instance_size; ++ ++ /** Enum describing the operation */ ++ JobType job_type; ++ ++ /** ++ * Mandatory: Entrypoint for the Coroutine. ++ * ++ * This callback will be invoked when moving from CREATED to RUNNING. ++ * ++ * If this callback returns nonzero, the job transaction it is part of is ++ * aborted. If it returns zero, the job moves into the WAITING state. If it ++ * is the last job to complete in its transaction, all jobs in the ++ * transaction move from WAITING to PENDING. ++ * ++ * This callback must be run in the job's context. ++ */ ++ int coroutine_fn (*run)(Job *job, Error **errp); ++ ++ /* ++ * Functions run without regard to the BQL that may run in any ++ * arbitrary thread. These functions do not need to be thread-safe ++ * because the caller ensures that they are invoked from one ++ * thread at time. ++ */ ++ ++ /** ++ * If the callback is not NULL, it will be invoked when the job transitions ++ * into the paused state. Paused jobs must not perform any asynchronous ++ * I/O or event loop activity. This callback is used to quiesce jobs. ++ */ ++ void coroutine_fn (*pause)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked when the job transitions ++ * out of the paused state. Any asynchronous I/O or event loop activity ++ * should be restarted from this callback. ++ */ ++ void coroutine_fn (*resume)(Job *job); ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++ /** ++ * Called when the job is resumed by the user (i.e. user_paused becomes ++ * false). .user_resume is called before .resume. ++ */ ++ void (*user_resume)(Job *job); ++ ++ /** ++ * Optional callback for job types whose completion must be triggered ++ * manually. ++ */ ++ void (*complete)(Job *job, Error **errp); ++ ++ /** ++ * If the callback is not NULL, prepare will be invoked when all the jobs ++ * belonging to the same transaction complete; or upon this job's completion ++ * if it is not in a transaction. ++ * ++ * This callback will not be invoked if the job has already failed. ++ * If it fails, abort and then clean will be called. ++ */ ++ int (*prepare)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked when all the jobs ++ * belonging to the same transaction complete; or upon this job's ++ * completion if it is not in a transaction. Skipped if NULL. ++ * ++ * All jobs will complete with a call to either .commit() or .abort() but ++ * never both. ++ */ ++ void (*commit)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked when any job in the ++ * same transaction fails; or upon this job's failure (due to error or ++ * cancellation) if it is not in a transaction. Skipped if NULL. ++ * ++ * All jobs will complete with a call to either .commit() or .abort() but ++ * never both. ++ */ ++ void (*abort)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked after a call to either ++ * .commit() or .abort(). Regardless of which callback is invoked after ++ * completion, .clean() will always be called, even if the job does not ++ * belong to a transaction group. ++ */ ++ void (*clean)(Job *job); ++ ++ /** ++ * If the callback is not NULL, it will be invoked in job_cancel_async ++ * ++ * This function must return true if the job will be cancelled ++ * immediately without any further I/O (mandatory if @force is ++ * true), and false otherwise. This lets the generic job layer ++ * know whether a job has been truly (force-)cancelled, or whether ++ * it is just in a special completion mode (like mirror after ++ * READY). ++ * (If the callback is NULL, the job is assumed to terminate ++ * without I/O.) ++ */ ++ bool (*cancel)(Job *job, bool force); ++ ++ ++ /** ++ * Called when the job is freed. ++ */ ++ void (*free)(Job *job); ++}; ++ ++typedef enum JobCreateFlags { ++ /* Default behavior */ ++ JOB_DEFAULT = 0x00, ++ /* Job is not QMP-created and should not send QMP events */ ++ JOB_INTERNAL = 0x01, ++ /* Job requires manual finalize step */ ++ JOB_MANUAL_FINALIZE = 0x02, ++ /* Job requires manual dismiss step */ ++ JOB_MANUAL_DISMISS = 0x04, ++} JobCreateFlags; ++ ++extern QemuMutex job_mutex; ++ ++#define JOB_LOCK_GUARD() QEMU_LOCK_GUARD(&job_mutex) ++ ++#define WITH_JOB_LOCK_GUARD() WITH_QEMU_LOCK_GUARD(&job_mutex) ++ ++/** ++ * job_lock: ++ * ++ * Take the mutex protecting the list of jobs and their status. ++ * Most functions called by the monitor need to call job_lock ++ * and job_unlock manually. On the other hand, function called ++ * by the block jobs themselves and by the block layer will take the ++ * lock for you. ++ */ ++void job_lock(void); ++ ++/** ++ * job_unlock: ++ * ++ * Release the mutex protecting the list of jobs and their status. ++ */ ++void job_unlock(void); ++ ++/** ++ * Allocate and return a new job transaction. Jobs can be added to the ++ * transaction using job_txn_add_job(). ++ * ++ * The transaction is automatically freed when the last job completes or is ++ * cancelled. ++ * ++ * All jobs in the transaction either complete successfully or fail/cancel as a ++ * group. Jobs wait for each other before completing. Cancelling one job ++ * cancels all jobs in the transaction. ++ */ ++JobTxn *job_txn_new(void); ++ ++/** ++ * Release a reference that was previously acquired with job_txn_add_job or ++ * job_txn_new. If it's the last reference to the object, it will be freed. ++ * ++ * Called with job lock *not* held. ++ */ ++void job_txn_unref(JobTxn *txn); ++ ++/* ++ * Same as job_txn_unref(), but called with job lock held. ++ * Might release the lock temporarily. ++ */ ++void job_txn_unref_locked(JobTxn *txn); ++ ++/** ++ * Create a new long-running job and return it. ++ * Called with job_mutex *not* held. ++ * ++ * @job_id: The id of the newly-created job, or %NULL for internal jobs ++ * @driver: The class object for the newly-created job. ++ * @txn: The transaction this job belongs to, if any. %NULL otherwise. ++ * @ctx: The AioContext to run the job coroutine in. ++ * @flags: Creation flags for the job. See @JobCreateFlags. ++ * @cb: Completion function for the job. ++ * @opaque: Opaque pointer value passed to @cb. ++ * @errp: Error object. ++ */ ++void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, ++ AioContext *ctx, int flags, BlockCompletionFunc *cb, ++ void *opaque, Error **errp); ++ ++/** ++ * Add a reference to Job refcnt, it will be decreased with job_unref, and then ++ * be freed if it comes to be the last reference. ++ * ++ * Called with job lock held. ++ */ ++void job_ref_locked(Job *job); ++ ++/** ++ * Release a reference that was previously acquired with job_ref_locked() or ++ * job_create(). If it's the last reference to the object, it will be freed. ++ * ++ * Called with job lock held. ++ */ ++void job_unref_locked(Job *job); ++ ++/** ++ * @job: The job that has made progress ++ * @done: How much progress the job made since the last call ++ * ++ * Updates the progress counter of the job. ++ * ++ * May be called with mutex held or not held. ++ */ ++void job_progress_update(Job *job, uint64_t done); ++ ++/** ++ * @job: The job whose expected progress end value is set ++ * @remaining: Missing progress (on top of the current progress counter value) ++ * until the new expected end value is reached ++ * ++ * Sets the expected end value of the progress counter of a job so that a ++ * completion percentage can be calculated when the progress is updated. ++ * ++ * May be called with mutex held or not held. ++ */ ++void job_progress_set_remaining(Job *job, uint64_t remaining); ++ ++/** ++ * @job: The job whose expected progress end value is updated ++ * @delta: Value which is to be added to the current expected end ++ * value ++ * ++ * Increases the expected end value of the progress counter of a job. ++ * This is useful for parenthesis operations: If a job has to ++ * conditionally perform a high-priority operation as part of its ++ * progress, it calls this function with the expected operation's ++ * length before, and job_progress_update() afterwards. ++ * (So the operation acts as a parenthesis in regards to the main job ++ * operation running in background.) ++ * ++ * May be called with mutex held or not held. ++ */ ++void job_progress_increase_remaining(Job *job, uint64_t delta); ++ ++/** ++ * Conditionally enter the job coroutine if the job is ready to run, not ++ * already busy and fn() returns true. fn() is called while under the job_lock ++ * critical section. ++ * ++ * Called with job lock held, but might release it temporarily. ++ */ ++void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)); ++ ++/** ++ * @job: A job that has not yet been started. ++ * ++ * Begins execution of a job. ++ * Takes ownership of one reference to the job object. ++ * ++ * Called with job_mutex *not* held. ++ */ ++void job_start(Job *job); ++ ++/** ++ * @job: The job to enter. ++ * ++ * Continue the specified job by entering the coroutine. ++ * Called with job_mutex *not* held. ++ */ ++void job_enter(Job *job); ++ ++/** ++ * @job: The job that is ready to pause. ++ * ++ * Pause now if job_pause() has been called. Jobs that perform lots of I/O ++ * must call this between requests so that the job can be paused. ++ * ++ * Called with job_mutex *not* held. ++ */ ++void coroutine_fn GRAPH_UNLOCKED job_pause_point(Job *job); ++ ++/** ++ * @job: The job that calls the function. ++ * ++ * Yield the job coroutine. ++ * Called with job_mutex *not* held. ++ */ ++void coroutine_fn job_yield(Job *job); ++ ++/** ++ * @job: The job that calls the function. ++ * @ns: How many nanoseconds to stop for. ++ * ++ * Put the job to sleep (assuming that it wasn't canceled) for @ns ++ * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately ++ * interrupt the wait. ++ * ++ * Called with job_mutex *not* held. ++ */ ++void coroutine_fn job_sleep_ns(Job *job, int64_t ns); ++ ++/** Returns the JobType of a given Job. */ ++JobType job_type(const Job *job); ++ ++/** Returns the enum string for the JobType of a given Job. */ ++const char *job_type_str(const Job *job); ++ ++/** Returns true if the job should not be visible to the management layer. */ ++bool job_is_internal(Job *job); ++ ++/** ++ * Returns whether the job is being cancelled. ++ * Called with job_mutex *not* held. ++ */ ++bool job_is_cancelled(Job *job); ++ ++/* Same as job_is_cancelled(), but called with job lock held. */ ++bool job_is_cancelled_locked(Job *job); ++ ++/** ++ * Returns whether the job is scheduled for cancellation (at an ++ * indefinite point). ++ * Called with job_mutex *not* held. ++ */ ++bool job_cancel_requested(Job *job); ++ ++/** ++ * Returns whether the job is in a completed state. ++ * Called with job lock held. ++ */ ++bool job_is_completed_locked(Job *job); ++ ++/** ++ * Returns whether the job is ready to be completed. ++ * Called with job_mutex *not* held. ++ */ ++bool job_is_ready(Job *job); ++ ++/* Same as job_is_ready(), but called with job lock held. */ ++bool job_is_ready_locked(Job *job); ++ ++/** ++ * Request @job to pause at the next pause point. Must be paired with ++ * job_resume(). If the job is supposed to be resumed by user action, call ++ * job_user_pause_locked() instead. ++ * ++ * Called with job lock *not* held. ++ */ ++void job_pause(Job *job); ++ ++/* Same as job_pause(), but called with job lock held. */ ++void job_pause_locked(Job *job); ++ ++/** Resumes a @job paused with job_pause. Called with job lock *not* held. */ ++void job_resume(Job *job); ++ ++/* ++ * Same as job_resume(), but called with job lock held. ++ * Might release the lock temporarily. ++ */ ++void job_resume_locked(Job *job); ++ ++/** ++ * Asynchronously pause the specified @job. ++ * Do not allow a resume until a matching call to job_user_resume. ++ * Called with job lock held. ++ */ ++void job_user_pause_locked(Job *job, Error **errp); ++ ++/** ++ * Returns true if the job is user-paused. ++ * Called with job lock held. ++ */ ++bool job_user_paused_locked(Job *job); ++ ++/** ++ * Resume the specified @job. ++ * Must be paired with a preceding job_user_pause_locked. ++ * Called with job lock held, but might release it temporarily. ++ */ ++void job_user_resume_locked(Job *job, Error **errp); ++ ++/** ++ * Get the next element from the list of block jobs after @job, or the ++ * first one if @job is %NULL. ++ * ++ * Returns the requested job, or %NULL if there are no more jobs left. ++ * Called with job lock *not* held. ++ */ ++Job *job_next(Job *job); ++ ++/* Same as job_next(), but called with job lock held. */ ++Job *job_next_locked(Job *job); ++ ++/** ++ * Get the job identified by @id (which must not be %NULL). ++ * ++ * Returns the requested job, or %NULL if it doesn't exist. ++ * Called with job lock held. ++ */ ++Job *job_get_locked(const char *id); ++ ++/** ++ * Check whether the verb @verb can be applied to @job in its current state. ++ * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM ++ * returned. ++ * ++ * Called with job lock held. ++ */ ++int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp); ++ ++/** ++ * The @job could not be started, free it. ++ * Called with job_mutex *not* held. ++ */ ++void job_early_fail(Job *job); ++ ++/** ++ * Moves the @job from RUNNING to READY. ++ * Called with job_mutex *not* held. ++ */ ++void job_transition_to_ready(Job *job); ++ ++/** ++ * Asynchronously complete the specified @job. ++ * Called with job lock held, but might release it temporarily. ++ */ ++void job_complete_locked(Job *job, Error **errp); ++ ++/** ++ * Asynchronously cancel the specified @job. If @force is true, the job should ++ * be cancelled immediately without waiting for a consistent state. ++ * Called with job lock held. ++ */ ++void job_cancel_locked(Job *job, bool force); ++ ++/** ++ * Cancels the specified job like job_cancel_locked(), but may refuse ++ * to do so if the operation isn't meaningful in the current state of the job. ++ * Called with job lock held. ++ */ ++void job_user_cancel_locked(Job *job, bool force, Error **errp); ++ ++/** ++ * Synchronously cancel the @job. The completion callback is called ++ * before the function returns. If @force is false, the job may ++ * actually complete instead of canceling itself; the circumstances ++ * under which this happens depend on the kind of job that is active. ++ * ++ * Returns the return value from the job if the job actually completed ++ * during the call, or -ECANCELED if it was canceled. ++ * ++ * Called with job_lock *not* held. ++ */ ++int job_cancel_sync(Job *job, bool force); ++ ++/* Same as job_cancel_sync, but called with job lock held. */ ++int job_cancel_sync_locked(Job *job, bool force); ++ ++/** ++ * Synchronously force-cancels all jobs using job_cancel_sync_locked(). ++ * ++ * Called with job_lock *not* held. ++ */ ++void job_cancel_sync_all(void); ++ ++/** ++ * @job: The job to be completed. ++ * @errp: Error object which may be set by job_complete_locked(); this is not ++ * necessarily set on every error, the job return value has to be ++ * checked as well. ++ * ++ * Synchronously complete the job. The completion callback is called before the ++ * function returns, unless it is NULL (which is permissible when using this ++ * function). ++ * ++ * Returns the return value from the job. ++ * Called with job_lock held. ++ */ ++int job_complete_sync_locked(Job *job, Error **errp); ++ ++/** ++ * For a @job that has finished its work and is pending awaiting explicit ++ * acknowledgement to commit its work, this will commit that work. ++ * ++ * FIXME: Make the below statement universally true: ++ * For jobs that support the manual workflow mode, all graph changes that occur ++ * as a result will occur after this command and before a successful reply. ++ * ++ * Called with job lock held. ++ */ ++void job_finalize_locked(Job *job, Error **errp); ++ ++/** ++ * Remove the concluded @job from the query list and resets the passed pointer ++ * to %NULL. Returns an error if the job is not actually concluded. ++ * ++ * Called with job lock held. ++ */ ++void job_dismiss_locked(Job **job, Error **errp); ++ ++/** ++ * Synchronously finishes the given @job. If @finish is given, it is called to ++ * trigger completion or cancellation of the job. ++ * ++ * Returns 0 if the job is successfully completed, -ECANCELED if the job was ++ * cancelled before completing, and -errno in other error cases. ++ * ++ * Called with job_lock held, but might release it temporarily. ++ */ ++int job_finish_sync_locked(Job *job, void (*finish)(Job *, Error **errp), ++ Error **errp); ++ ++/** ++ * Sets the @job->aio_context. ++ * Called with job_mutex *not* held. ++ * ++ * This function must run in the main thread to protect against ++ * concurrent read in job_finish_sync_locked(), takes the job_mutex ++ * lock to protect against the read in job_do_yield_locked(), and must ++ * be called when the job is quiescent. ++ */ ++void job_set_aio_context(Job *job, AioContext *ctx); ++ ++#endif +diff --git a/include/qemu/lockable.h b/include/qemu/lockable.h +new file mode 100644 +index 00000000..66713bd4 +--- /dev/null ++++ b/include/qemu/lockable.h +@@ -0,0 +1,184 @@ ++/* ++ * Polymorphic locking functions (aka poor man templates) ++ * ++ * Copyright Red Hat, Inc. 2017, 2018 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_LOCKABLE_H ++#define QEMU_LOCKABLE_H ++ ++#include "qemu/coroutine-core.h" ++#include "qemu/thread.h" ++ ++typedef void QemuLockUnlockFunc(void *); ++ ++typedef struct QemuLockable { ++ void *object; ++ QemuLockUnlockFunc *lock; ++ QemuLockUnlockFunc *unlock; ++} QemuLockable; ++ ++static inline __attribute__((__always_inline__)) QemuLockable * ++qemu_make_lockable(void *x, QemuLockable *lockable) ++{ ++ /* ++ * We cannot test this in a macro, otherwise we get compiler ++ * warnings like "the address of 'm' will always evaluate as 'true'". ++ */ ++ return x ? lockable : NULL; ++} ++ ++static inline __attribute__((__always_inline__)) QemuLockable * ++qemu_null_lockable(void *x) ++{ ++ if (x != NULL) { ++ qemu_build_not_reached(); ++ } ++ return NULL; ++} ++ ++#define QML_FUNC_(name) \ ++ static inline void qemu_lockable_ ## name ## _lock(void *x) \ ++ { \ ++ qemu_ ## name ## _lock(x); \ ++ } \ ++ static inline void qemu_lockable_ ## name ## _unlock(void *x) \ ++ { \ ++ qemu_ ## name ## _unlock(x); \ ++ } ++ ++QML_FUNC_(mutex) ++QML_FUNC_(rec_mutex) ++QML_FUNC_(co_mutex) ++QML_FUNC_(spin) ++ ++/* ++ * In C, compound literals have the lifetime of an automatic variable. ++ * In C++ it would be different, but then C++ wouldn't need QemuLockable ++ * either... ++ */ ++#define QML_OBJ_(x, name) (&(QemuLockable) { \ ++ .object = (x), \ ++ .lock = qemu_lockable_ ## name ## _lock, \ ++ .unlock = qemu_lockable_ ## name ## _unlock \ ++ }) ++ ++/** ++ * QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable ++ * ++ * @x: a lock object (currently one of QemuMutex, QemuRecMutex, ++ * CoMutex, QemuSpin). ++ * ++ * Returns a QemuLockable object that can be passed around ++ * to a function that can operate with locks of any kind, or ++ * NULL if @x is %NULL. ++ * ++ * Note the special case for void *, so that we may pass "NULL". ++ */ ++#define QEMU_MAKE_LOCKABLE(x) \ ++ _Generic((x), QemuLockable *: (x), \ ++ void *: qemu_null_lockable(x), \ ++ QemuMutex *: qemu_make_lockable(x, QML_OBJ_(x, mutex)), \ ++ QemuRecMutex *: qemu_make_lockable(x, QML_OBJ_(x, rec_mutex)), \ ++ CoMutex *: qemu_make_lockable(x, QML_OBJ_(x, co_mutex)), \ ++ QemuSpin *: qemu_make_lockable(x, QML_OBJ_(x, spin))) ++ ++/** ++ * QEMU_MAKE_LOCKABLE_NONNULL - Make a polymorphic QemuLockable ++ * ++ * @x: a lock object (currently one of QemuMutex, QemuRecMutex, ++ * CoMutex, QemuSpin). ++ * ++ * Returns a QemuLockable object that can be passed around ++ * to a function that can operate with locks of any kind. ++ */ ++#define QEMU_MAKE_LOCKABLE_NONNULL(x) \ ++ _Generic((x), QemuLockable *: (x), \ ++ QemuMutex *: QML_OBJ_(x, mutex), \ ++ QemuRecMutex *: QML_OBJ_(x, rec_mutex), \ ++ CoMutex *: QML_OBJ_(x, co_mutex), \ ++ QemuSpin *: QML_OBJ_(x, spin)) ++ ++static inline void qemu_lockable_lock(QemuLockable *x) ++{ ++ x->lock(x->object); ++} ++ ++static inline void qemu_lockable_unlock(QemuLockable *x) ++{ ++ x->unlock(x->object); ++} ++ ++static inline QemuLockable *qemu_lockable_auto_lock(QemuLockable *x) ++{ ++ qemu_lockable_lock(x); ++ return x; ++} ++ ++static inline void qemu_lockable_auto_unlock(QemuLockable *x) ++{ ++ if (x) { ++ qemu_lockable_unlock(x); ++ } ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuLockable, qemu_lockable_auto_unlock) ++ ++#define WITH_QEMU_LOCK_GUARD_(x, var) \ ++ for (g_autoptr(QemuLockable) var = \ ++ qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \ ++ var; \ ++ qemu_lockable_auto_unlock(var), var = NULL) ++ ++/** ++ * WITH_QEMU_LOCK_GUARD - Lock a lock object for scope ++ * ++ * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). ++ * ++ * This macro defines a lock scope such that entering the scope takes the lock ++ * and leaving the scope releases the lock. Return statements are allowed ++ * within the scope and release the lock. Break and continue statements leave ++ * the scope early and release the lock. ++ * ++ * WITH_QEMU_LOCK_GUARD(&mutex) { ++ * ... ++ * if (error) { ++ * return; <-- mutex is automatically unlocked ++ * } ++ * ++ * if (early_exit) { ++ * break; <-- leave this scope early ++ * } ++ * ... ++ * } ++ */ ++#define WITH_QEMU_LOCK_GUARD(x) \ ++ WITH_QEMU_LOCK_GUARD_((x), glue(qemu_lockable_auto, __COUNTER__)) ++ ++/** ++ * QEMU_LOCK_GUARD - Lock an object until the end of the scope ++ * ++ * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin). ++ * ++ * This macro takes a lock until the end of the scope. Return statements ++ * release the lock. ++ * ++ * ... <-- mutex not locked ++ * QEMU_LOCK_GUARD(&mutex); <-- mutex locked from here onwards ++ * ... ++ * if (error) { ++ * return; <-- mutex is automatically unlocked ++ * } ++ */ ++#define QEMU_LOCK_GUARD(x) \ ++ g_autoptr(QemuLockable) \ ++ glue(qemu_lockable_auto, __COUNTER__) G_GNUC_UNUSED = \ ++ qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE((x))) ++ ++#endif +diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h +new file mode 100644 +index 00000000..5764db15 +--- /dev/null ++++ b/include/qemu/main-loop.h +@@ -0,0 +1,411 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_MAIN_LOOP_H ++#define QEMU_MAIN_LOOP_H ++ ++#include "block/aio.h" ++#include "qom/object.h" ++#include "sysemu/event-loop-base.h" ++ ++#define SIG_IPI SIGUSR1 ++ ++#define TYPE_MAIN_LOOP "main-loop" ++OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP) ++ ++struct MainLoop { ++ EventLoopBase parent_obj; ++}; ++typedef struct MainLoop MainLoop; ++ ++/** ++ * qemu_init_main_loop: Set up the process so that it can run the main loop. ++ * ++ * This includes setting up signal handlers. It should be called before ++ * any other threads are created. In addition, threads other than the ++ * main one should block signals that are trapped by the main loop. ++ * For simplicity, you can consider these signals to be safe: SIGUSR1, ++ * SIGUSR2, thread signals (SIGFPE, SIGILL, SIGSEGV, SIGBUS) and real-time ++ * signals if available. Remember that Windows in practice does not have ++ * signals, though. ++ * ++ * In the case of QEMU tools, this will also start/initialize timers. ++ */ ++int qemu_init_main_loop(Error **errp); ++ ++/** ++ * main_loop_wait: Run one iteration of the main loop. ++ * ++ * If @nonblocking is true, poll for events, otherwise suspend until ++ * one actually occurs. The main loop usually consists of a loop that ++ * repeatedly calls main_loop_wait(false). ++ * ++ * Main loop services include file descriptor callbacks, bottom halves ++ * and timers (defined in qemu/timer.h). Bottom halves are similar to timers ++ * that execute immediately, but have a lower overhead and scheduling them ++ * is wait-free, thread-safe and signal-safe. ++ * ++ * It is sometimes useful to put a whole program in a coroutine. In this ++ * case, the coroutine actually should be started from within the main loop, ++ * so that the main loop can run whenever the coroutine yields. To do this, ++ * you can use a bottom half to enter the coroutine as soon as the main loop ++ * starts: ++ * ++ * void enter_co_bh(void *opaque) { ++ * QEMUCoroutine *co = opaque; ++ * qemu_coroutine_enter(co); ++ * } ++ * ++ * ... ++ * QEMUCoroutine *co = qemu_coroutine_create(coroutine_entry, NULL); ++ * QEMUBH *start_bh = qemu_bh_new(enter_co_bh, co); ++ * qemu_bh_schedule(start_bh); ++ * while (...) { ++ * main_loop_wait(false); ++ * } ++ * ++ * (In the future we may provide a wrapper for this). ++ * ++ * @nonblocking: Whether the caller should block until an event occurs. ++ */ ++void main_loop_wait(int nonblocking); ++ ++/** ++ * qemu_get_aio_context: Return the main loop's AioContext ++ */ ++AioContext *qemu_get_aio_context(void); ++ ++/** ++ * qemu_notify_event: Force processing of pending events. ++ * ++ * Similar to signaling a condition variable, qemu_notify_event forces ++ * main_loop_wait to look at pending events and exit. The caller of ++ * main_loop_wait will usually call it again very soon, so qemu_notify_event ++ * also has the side effect of recalculating the sets of file descriptors ++ * that the main loop waits for. ++ * ++ * Calling qemu_notify_event is rarely necessary, because main loop ++ * services (bottom halves and timers) call it themselves. ++ */ ++void qemu_notify_event(void); ++ ++#ifdef _WIN32 ++/* return TRUE if no sleep should be done afterwards */ ++typedef int PollingFunc(void *opaque); ++ ++/** ++ * qemu_add_polling_cb: Register a Windows-specific polling callback ++ * ++ * Currently, under Windows some events are polled rather than waited for. ++ * Polling callbacks do not ensure that @func is called timely, because ++ * the main loop might wait for an arbitrarily long time. If possible, ++ * you should instead create a separate thread that does a blocking poll ++ * and set a Win32 event object. The event can then be passed to ++ * qemu_add_wait_object. ++ * ++ * Polling callbacks really have nothing Windows specific in them, but ++ * as they are a hack and are currently not necessary under POSIX systems, ++ * they are only available when QEMU is running under Windows. ++ * ++ * @func: The function that does the polling, and returns 1 to force ++ * immediate completion of main_loop_wait. ++ * @opaque: A pointer-size value that is passed to @func. ++ */ ++int qemu_add_polling_cb(PollingFunc *func, void *opaque); ++ ++/** ++ * qemu_del_polling_cb: Unregister a Windows-specific polling callback ++ * ++ * This function removes a callback that was registered with ++ * qemu_add_polling_cb. ++ * ++ * @func: The function that was passed to qemu_add_polling_cb. ++ * @opaque: A pointer-size value that was passed to qemu_add_polling_cb. ++ */ ++void qemu_del_polling_cb(PollingFunc *func, void *opaque); ++ ++/* Wait objects handling */ ++typedef void WaitObjectFunc(void *opaque); ++ ++/** ++ * qemu_add_wait_object: Register a callback for a Windows handle ++ * ++ * Under Windows, the iohandler mechanism can only be used with sockets. ++ * QEMU must use the WaitForMultipleObjects API to wait on other handles. ++ * This function registers a #HANDLE with QEMU, so that it will be included ++ * in the main loop's calls to WaitForMultipleObjects. When the handle ++ * is in a signaled state, QEMU will call @func. ++ * ++ * If the same HANDLE is added twice, this function returns -1. ++ * ++ * @handle: The Windows handle to be observed. ++ * @func: A function to be called when @handle is in a signaled state. ++ * @opaque: A pointer-size value that is passed to @func. ++ */ ++int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); ++ ++/** ++ * qemu_del_wait_object: Unregister a callback for a Windows handle ++ * ++ * This function removes a callback that was registered with ++ * qemu_add_wait_object. ++ * ++ * @func: The function that was passed to qemu_add_wait_object. ++ * @opaque: A pointer-size value that was passed to qemu_add_wait_object. ++ */ ++void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); ++#endif ++ ++/* async I/O support */ ++ ++typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); ++ ++/** ++ * IOCanReadHandler: Return the number of bytes that #IOReadHandler can accept ++ * ++ * This function reports how many bytes #IOReadHandler is prepared to accept. ++ * #IOReadHandler may be invoked with up to this number of bytes. If this ++ * function returns 0 then #IOReadHandler is not invoked. ++ * ++ * This function is typically called from an event loop. If the number of ++ * bytes changes outside the event loop (e.g. because a vcpu thread drained the ++ * buffer), then it is necessary to kick the event loop so that this function ++ * is called again. aio_notify() or qemu_notify_event() can be used to kick ++ * the event loop. ++ */ ++typedef int IOCanReadHandler(void *opaque); ++ ++/** ++ * qemu_set_fd_handler: Register a file descriptor with the main loop ++ * ++ * This function tells the main loop to wake up whenever one of the ++ * following conditions is true: ++ * ++ * 1) if @fd_write is not %NULL, when the file descriptor is writable; ++ * ++ * 2) if @fd_read is not %NULL, when the file descriptor is readable. ++ * ++ * The callbacks that are set up by qemu_set_fd_handler are level-triggered. ++ * If @fd_read does not read from @fd, or @fd_write does not write to @fd ++ * until its buffers are full, they will be called again on the next ++ * iteration. ++ * ++ * @fd: The file descriptor to be observed. Under Windows it must be ++ * a #SOCKET. ++ * ++ * @fd_read: A level-triggered callback that is fired if @fd is readable ++ * at the beginning of a main loop iteration, or if it becomes readable ++ * during one. ++ * ++ * @fd_write: A level-triggered callback that is fired when @fd is writable ++ * at the beginning of a main loop iteration, or if it becomes writable ++ * during one. ++ * ++ * @opaque: A pointer-sized value that is passed to @fd_read and @fd_write. ++ */ ++void qemu_set_fd_handler(int fd, ++ IOHandler *fd_read, ++ IOHandler *fd_write, ++ void *opaque); ++ ++ ++/** ++ * event_notifier_set_handler: Register an EventNotifier with the main loop ++ * ++ * This function tells the main loop to wake up whenever the ++ * #EventNotifier was set. ++ * ++ * @e: The #EventNotifier to be observed. ++ * ++ * @handler: A level-triggered callback that is fired when @e ++ * has been set. @e is passed to it as a parameter. ++ */ ++void event_notifier_set_handler(EventNotifier *e, ++ EventNotifierHandler *handler); ++ ++GSource *iohandler_get_g_source(void); ++AioContext *iohandler_get_aio_context(void); ++ ++/** ++ * bql_locked: Return lock status of the Big QEMU Lock (BQL) ++ * ++ * The Big QEMU Lock (BQL) is the coarsest lock in QEMU, and as such it ++ * must always be taken outside other locks. This function helps ++ * functions take different paths depending on whether the current ++ * thread is running within the BQL. ++ * ++ * This function should never be used in the block layer, because ++ * unit tests, block layer tools and qemu-storage-daemon do not ++ * have a BQL. ++ * Please instead refer to qemu_in_main_thread(). ++ */ ++bool bql_locked(void); ++ ++/** ++ * qemu_in_main_thread: return whether it's possible to safely access ++ * the global state of the block layer. ++ * ++ * Global state of the block layer is not accessible from I/O threads ++ * or worker threads; only from threads that "own" the default ++ * AioContext that qemu_get_aio_context() returns. For tests, block ++ * layer tools and qemu-storage-daemon there is a designated thread that ++ * runs the event loop for qemu_get_aio_context(), and that is the ++ * main thread. ++ * ++ * For emulators, however, any thread that holds the BQL can act ++ * as the block layer main thread; this will be any of the actual ++ * main thread, the vCPU threads or the RCU thread. ++ * ++ * For clarity, do not use this function outside the block layer. ++ */ ++bool qemu_in_main_thread(void); ++ ++/* ++ * Mark and check that the function is part of the Global State API. ++ * Please refer to include/block/block-global-state.h for more ++ * information about GS API. ++ */ ++#define GLOBAL_STATE_CODE() \ ++ do { \ ++ assert(qemu_in_main_thread()); \ ++ } while (0) ++ ++/* ++ * Mark and check that the function is part of the I/O API. ++ * Please refer to include/block/block-io.h for more ++ * information about IO API. ++ */ ++#define IO_CODE() \ ++ do { \ ++ /* nop */ \ ++ } while (0) ++ ++/* ++ * Mark and check that the function is part of the "I/O OR GS" API. ++ * Please refer to include/block/block-io.h for more ++ * information about "IO or GS" API. ++ */ ++#define IO_OR_GS_CODE() \ ++ do { \ ++ /* nop */ \ ++ } while (0) ++ ++/** ++ * bql_lock: Lock the Big QEMU Lock (BQL). ++ * ++ * This function locks the Big QEMU Lock (BQL). The lock is taken by ++ * main() in vl.c and always taken except while waiting on ++ * external events (such as with select). The lock should be taken ++ * by threads other than the main loop thread when calling ++ * qemu_bh_new(), qemu_set_fd_handler() and basically all other ++ * functions documented in this file. ++ * ++ * NOTE: tools currently are single-threaded and bql_lock ++ * is a no-op there. ++ */ ++#define bql_lock() bql_lock_impl(__FILE__, __LINE__) ++void bql_lock_impl(const char *file, int line); ++ ++/** ++ * bql_unlock: Unlock the Big QEMU Lock (BQL). ++ * ++ * This function unlocks the Big QEMU Lock. The lock is taken by ++ * main() in vl.c and always taken except while waiting on ++ * external events (such as with select). The lock should be unlocked ++ * as soon as possible by threads other than the main loop thread, ++ * because it prevents the main loop from processing callbacks, ++ * including timers and bottom halves. ++ * ++ * NOTE: tools currently are single-threaded and bql_unlock ++ * is a no-op there. ++ */ ++void bql_unlock(void); ++ ++/** ++ * BQL_LOCK_GUARD ++ * ++ * Wrap a block of code in a conditional bql_{lock,unlock}. ++ */ ++typedef struct BQLLockAuto BQLLockAuto; ++ ++static inline BQLLockAuto *bql_auto_lock(const char *file, int line) ++{ ++ if (bql_locked()) { ++ return NULL; ++ } ++ bql_lock_impl(file, line); ++ /* Anything non-NULL causes the cleanup function to be called */ ++ return (BQLLockAuto *)(uintptr_t)1; ++} ++ ++static inline void bql_auto_unlock(BQLLockAuto *l) ++{ ++ bql_unlock(); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BQLLockAuto, bql_auto_unlock) ++ ++#define BQL_LOCK_GUARD() \ ++ g_autoptr(BQLLockAuto) _bql_lock_auto __attribute__((unused)) \ ++ = bql_auto_lock(__FILE__, __LINE__) ++ ++/* ++ * qemu_cond_wait_bql: Wait on condition for the Big QEMU Lock (BQL) ++ * ++ * This function atomically releases the Big QEMU Lock (BQL) and causes ++ * the calling thread to block on the condition. ++ */ ++void qemu_cond_wait_bql(QemuCond *cond); ++ ++/* ++ * qemu_cond_timedwait_bql: like the previous, but with timeout ++ */ ++void qemu_cond_timedwait_bql(QemuCond *cond, int ms); ++ ++/* internal interfaces */ ++ ++#define qemu_bh_new_guarded(cb, opaque, guard) \ ++ qemu_bh_new_full((cb), (opaque), (stringify(cb)), guard) ++#define qemu_bh_new(cb, opaque) \ ++ qemu_bh_new_full((cb), (opaque), (stringify(cb)), NULL) ++QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name, ++ MemReentrancyGuard *reentrancy_guard); ++void qemu_bh_schedule_idle(QEMUBH *bh); ++ ++enum { ++ MAIN_LOOP_POLL_FILL, ++ MAIN_LOOP_POLL_ERR, ++ MAIN_LOOP_POLL_OK, ++}; ++ ++typedef struct MainLoopPoll { ++ int state; ++ uint32_t timeout; ++ GArray *pollfds; ++} MainLoopPoll; ++ ++void main_loop_poll_add_notifier(Notifier *notify); ++void main_loop_poll_remove_notifier(Notifier *notify); ++ ++#endif +diff --git a/include/qemu/memalign.h b/include/qemu/memalign.h +new file mode 100644 +index 00000000..fa299f3b +--- /dev/null ++++ b/include/qemu/memalign.h +@@ -0,0 +1,61 @@ ++/* ++ * Allocation and free functions for aligned memory ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_MEMALIGN_H ++#define QEMU_MEMALIGN_H ++ ++/** ++ * qemu_try_memalign: Allocate aligned memory ++ * @alignment: required alignment, in bytes ++ * @size: size of allocation, in bytes ++ * ++ * Allocate memory on an aligned boundary (i.e. the returned ++ * address will be an exact multiple of @alignment). ++ * @alignment must be a power of 2, or the function will assert(). ++ * On success, returns allocated memory; on failure, returns NULL. ++ * ++ * The memory allocated through this function must be freed via ++ * qemu_vfree() (and not via free()). ++ */ ++void *qemu_try_memalign(size_t alignment, size_t size); ++/** ++ * qemu_memalign: Allocate aligned memory, without failing ++ * @alignment: required alignment, in bytes ++ * @size: size of allocation, in bytes ++ * ++ * Allocate memory in the same way as qemu_try_memalign(), but ++ * abort() with an error message if the memory allocation fails. ++ * ++ * The memory allocated through this function must be freed via ++ * qemu_vfree() (and not via free()). ++ */ ++void *qemu_memalign(size_t alignment, size_t size); ++/** ++ * qemu_vfree: Free memory allocated through qemu_memalign ++ * @ptr: memory to free ++ * ++ * This function must be used to free memory allocated via qemu_memalign() ++ * or qemu_try_memalign(). (Using the wrong free function will cause ++ * subtle bugs on Windows hosts.) ++ */ ++void qemu_vfree(void *ptr); ++/* ++ * It's an analog of GLIB's g_autoptr_cleanup_generic_gfree(), used to define ++ * g_autofree macro. ++ */ ++static inline void qemu_cleanup_generic_vfree(void *p) ++{ ++ void **pp = (void **)p; ++ qemu_vfree(*pp); ++} ++ ++/* ++ * Analog of g_autofree, but qemu_vfree is called on cleanup instead of g_free. ++ */ ++#define QEMU_AUTO_VFREE __attribute__((cleanup(qemu_cleanup_generic_vfree))) ++ ++#endif +diff --git a/include/qemu/module.h b/include/qemu/module.h +new file mode 100644 +index 00000000..c37ce74b +--- /dev/null ++++ b/include/qemu/module.h +@@ -0,0 +1,192 @@ ++/* ++ * QEMU Module Infrastructure ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_MODULE_H ++#define QEMU_MODULE_H ++ ++ ++#define DSO_STAMP_FUN glue(qemu_stamp, CONFIG_STAMP) ++#define DSO_STAMP_FUN_STR stringify(DSO_STAMP_FUN) ++ ++#ifdef BUILD_DSO ++void DSO_STAMP_FUN(void); ++/* This is a dummy symbol to identify a loaded DSO as a QEMU module, so we can ++ * distinguish "version mismatch" from "not a QEMU module", when the stamp ++ * check fails during module loading */ ++void qemu_module_dummy(void); ++ ++#define module_init(function, type) \ ++static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ ++{ \ ++ register_dso_module_init(function, type); \ ++} ++#else ++/* This should not be used directly. Use block_init etc. instead. */ ++#define module_init(function, type) \ ++static void __attribute__((constructor)) do_qemu_init_ ## function(void) \ ++{ \ ++ register_module_init(function, type); \ ++} ++#endif ++ ++typedef enum { ++ MODULE_INIT_MIGRATION, ++ MODULE_INIT_BLOCK, ++ MODULE_INIT_OPTS, ++ MODULE_INIT_QOM, ++ MODULE_INIT_TRACE, ++ MODULE_INIT_XEN_BACKEND, ++ MODULE_INIT_LIBQOS, ++ MODULE_INIT_FUZZ_TARGET, ++ MODULE_INIT_MAX ++} module_init_type; ++ ++#define block_init(function) module_init(function, MODULE_INIT_BLOCK) ++#define opts_init(function) module_init(function, MODULE_INIT_OPTS) ++#define type_init(function) module_init(function, MODULE_INIT_QOM) ++#define trace_init(function) module_init(function, MODULE_INIT_TRACE) ++#define xen_backend_init(function) module_init(function, \ ++ MODULE_INIT_XEN_BACKEND) ++#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS) ++#define fuzz_target_init(function) module_init(function, \ ++ MODULE_INIT_FUZZ_TARGET) ++#define migration_init(function) module_init(function, MODULE_INIT_MIGRATION) ++#define block_module_load(lib, errp) module_load("block-", lib, errp) ++#define ui_module_load(lib, errp) module_load("ui-", lib, errp) ++#define audio_module_load(lib, errp) module_load("audio-", lib, errp) ++ ++void register_module_init(void (*fn)(void), module_init_type type); ++void register_dso_module_init(void (*fn)(void), module_init_type type); ++ ++void module_call_init(module_init_type type); ++ ++/* ++ * module_load: attempt to load a module from a set of directories ++ * ++ * directories searched are: ++ * - getenv("QEMU_MODULE_DIR") ++ * - get_relocated_path(CONFIG_QEMU_MODDIR); ++ * - /var/run/qemu/${version_dir} ++ * ++ * prefix: a subsystem prefix, or the empty string ("audio-", ..., "") ++ * name: name of the module ++ * errp: error to set in case the module is found, but load failed. ++ * ++ * Return value: -1 on error (errp set if not NULL). ++ * 0 if module or one of its dependencies are not installed, ++ * 1 if the module is found and loaded, ++ * 2 if the module is already loaded, or module is built-in. ++ */ ++int module_load(const char *prefix, const char *name, Error **errp); ++ ++/* ++ * module_load_qom: attempt to load a module to provide a QOM type ++ * ++ * type: the type to be provided ++ * errp: error to set. ++ * ++ * Return value: as per module_load. ++ */ ++int module_load_qom(const char *type, Error **errp); ++void module_load_qom_all(void); ++void module_allow_arch(const char *arch); ++ ++/** ++ * DOC: module info annotation macros ++ * ++ * ``scripts/modinfo-collect.py`` will collect module info, ++ * using the preprocessor and -DQEMU_MODINFO. ++ * ++ * ``scripts/modinfo-generate.py`` will create a module meta-data database ++ * from the collected information so qemu knows about module ++ * dependencies and QOM objects implemented by modules. ++ * ++ * See ``*.modinfo`` and ``modinfo.c`` in the build directory to check the ++ * script results. ++ */ ++#ifdef QEMU_MODINFO ++# define modinfo(kind, value) \ ++ MODINFO_START kind value MODINFO_END ++#else ++# define modinfo(kind, value) ++#endif ++ ++/** ++ * module_obj ++ * ++ * @name: QOM type. ++ * ++ * This module implements QOM type @name. ++ */ ++#define module_obj(name) modinfo(obj, name) ++ ++/** ++ * module_dep ++ * ++ * @name: module name ++ * ++ * This module depends on module @name. ++ */ ++#define module_dep(name) modinfo(dep, name) ++ ++/** ++ * module_arch ++ * ++ * @name: target architecture ++ * ++ * This module is for target architecture @arch. ++ * ++ * Note that target-dependent modules are tagged automatically, so ++ * this is only needed in case target-independent modules should be ++ * restricted. Use case example: the ccw bus is implemented by s390x ++ * only. ++ */ ++#define module_arch(name) modinfo(arch, name) ++ ++/** ++ * module_opts ++ * ++ * @name: QemuOpts name ++ * ++ * This module registers QemuOpts @name. ++ */ ++#define module_opts(name) modinfo(opts, name) ++ ++/** ++ * module_kconfig ++ * ++ * @name: Kconfig requirement necessary to load the module ++ * ++ * This module requires a core module that should be implemented and ++ * enabled in Kconfig. ++ */ ++#define module_kconfig(name) modinfo(kconfig, name) ++ ++/* ++ * module info database ++ * ++ * scripts/modinfo-generate.c will build this using the data collected ++ * by scripts/modinfo-collect.py ++ */ ++typedef struct QemuModinfo QemuModinfo; ++struct QemuModinfo { ++ const char *name; ++ const char *arch; ++ const char **objs; ++ const char **deps; ++ const char **opts; ++}; ++extern const QemuModinfo qemu_modinfo[]; ++void module_init_info(const QemuModinfo *info); ++ ++#endif +diff --git a/include/qemu/notify.h b/include/qemu/notify.h +new file mode 100644 +index 00000000..abf18dbf +--- /dev/null ++++ b/include/qemu/notify.h +@@ -0,0 +1,78 @@ ++/* ++ * Notifier lists ++ * ++ * Copyright IBM, Corp. 2010 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_NOTIFY_H ++#define QEMU_NOTIFY_H ++ ++#include "qemu/queue.h" ++ ++typedef struct Notifier Notifier; ++ ++struct Notifier ++{ ++ void (*notify)(Notifier *notifier, void *data); ++ QLIST_ENTRY(Notifier) node; ++}; ++ ++typedef struct NotifierList ++{ ++ QLIST_HEAD(, Notifier) notifiers; ++} NotifierList; ++ ++#define NOTIFIER_LIST_INITIALIZER(head) \ ++ { QLIST_HEAD_INITIALIZER((head).notifiers) } ++ ++void notifier_list_init(NotifierList *list); ++ ++void notifier_list_add(NotifierList *list, Notifier *notifier); ++ ++void notifier_remove(Notifier *notifier); ++ ++void notifier_list_notify(NotifierList *list, void *data); ++ ++bool notifier_list_empty(NotifierList *list); ++ ++/* Same as Notifier but allows .notify() to return errors */ ++typedef struct NotifierWithReturn NotifierWithReturn; ++ ++/* Return int to allow for different failure modes and recovery actions */ ++typedef int (*NotifierWithReturnFunc)(NotifierWithReturn *notifier, void *data, ++ Error **errp); ++ ++struct NotifierWithReturn { ++ /** ++ * Return 0 on success (next notifier will be invoked), otherwise ++ * notifier_with_return_list_notify() will stop and return the value. ++ */ ++ NotifierWithReturnFunc notify; ++ QLIST_ENTRY(NotifierWithReturn) node; ++}; ++ ++typedef struct NotifierWithReturnList { ++ QLIST_HEAD(, NotifierWithReturn) notifiers; ++} NotifierWithReturnList; ++ ++#define NOTIFIER_WITH_RETURN_LIST_INITIALIZER(head) \ ++ { QLIST_HEAD_INITIALIZER((head).notifiers) } ++ ++void notifier_with_return_list_init(NotifierWithReturnList *list); ++ ++void notifier_with_return_list_add(NotifierWithReturnList *list, ++ NotifierWithReturn *notifier); ++ ++void notifier_with_return_remove(NotifierWithReturn *notifier); ++ ++int notifier_with_return_list_notify(NotifierWithReturnList *list, ++ void *data, Error **errp); ++ ++#endif +diff --git a/include/qemu/option.h b/include/qemu/option.h +new file mode 100644 +index 00000000..01e673ae +--- /dev/null ++++ b/include/qemu/option.h +@@ -0,0 +1,151 @@ ++/* ++ * Commandline option parsing functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2009 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_OPTION_H ++#define QEMU_OPTION_H ++ ++#include "qemu/queue.h" ++ ++/** ++ * get_opt_value ++ * @p: a pointer to the option name, delimited by commas ++ * @value: a non-NULL pointer that will received the delimited options ++ * ++ * The @value char pointer will be allocated and filled with ++ * the delimited options. ++ * ++ * Returns the position of the comma delimiter/zero byte after the ++ * option name in @p. ++ * The memory pointer in @value must be released with a call to g_free() ++ * when no longer required. ++ */ ++const char *get_opt_value(const char *p, char **value); ++ ++bool parse_option_size(const char *name, const char *value, ++ uint64_t *ret, Error **errp); ++bool has_help_option(const char *param); ++ ++enum QemuOptType { ++ QEMU_OPT_STRING = 0, /* no parsing (use string as-is) */ ++ QEMU_OPT_BOOL, /* on/off */ ++ QEMU_OPT_NUMBER, /* simple number */ ++ QEMU_OPT_SIZE, /* size, accepts (K)ilo, (M)ega, (G)iga, (T)era postfix */ ++}; ++ ++typedef struct QemuOpt QemuOpt; ++ ++typedef struct QemuOptDesc { ++ const char *name; ++ enum QemuOptType type; ++ const char *help; ++ const char *def_value_str; ++} QemuOptDesc; ++ ++struct QemuOptsList { ++ const char *name; ++ const char *implied_opt_name; ++ bool merge_lists; /* Merge multiple uses of option into a single list? */ ++ QTAILQ_HEAD(, QemuOpts) head; ++ QemuOptDesc desc[]; ++}; ++ ++const char *qemu_opt_get(QemuOpts *opts, const char *name); ++char *qemu_opt_get_del(QemuOpts *opts, const char *name); ++/** ++ * qemu_opt_has_help_opt: ++ * @opts: options to search for a help request ++ * ++ * Check whether the options specified by @opts include one of the ++ * standard strings which indicate that the user is asking for a ++ * list of the valid values for a command line option (as defined ++ * by is_help_option()). ++ * ++ * Returns: true if @opts includes 'help' or equivalent. ++ */ ++bool qemu_opt_has_help_opt(QemuOpts *opts); ++QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name); ++bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval); ++uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval); ++uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval); ++bool qemu_opt_get_bool_del(QemuOpts *opts, const char *name, bool defval); ++uint64_t qemu_opt_get_number_del(QemuOpts *opts, const char *name, ++ uint64_t defval); ++uint64_t qemu_opt_get_size_del(QemuOpts *opts, const char *name, ++ uint64_t defval); ++int qemu_opt_unset(QemuOpts *opts, const char *name); ++bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value, ++ Error **errp); ++bool qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, ++ Error **errp); ++bool qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, ++ Error **errp); ++typedef int (*qemu_opt_loopfunc)(void *opaque, ++ const char *name, const char *value, ++ Error **errp); ++int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, ++ Error **errp); ++ ++typedef struct { ++ QemuOpts *opts; ++ QemuOpt *opt; ++ const char *name; ++} QemuOptsIter; ++ ++void qemu_opt_iter_init(QemuOptsIter *iter, QemuOpts *opts, const char *name); ++const char *qemu_opt_iter_next(QemuOptsIter *iter); ++ ++QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id); ++QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, ++ int fail_if_exists, Error **errp); ++void qemu_opts_reset(QemuOptsList *list); ++void qemu_opts_loc_restore(QemuOpts *opts); ++const char *qemu_opts_id(QemuOpts *opts); ++void qemu_opts_set_id(QemuOpts *opts, char *id); ++void qemu_opts_del(QemuOpts *opts); ++bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp); ++bool qemu_opts_do_parse(QemuOpts *opts, const char *params, ++ const char *firstname, Error **errp); ++QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params, ++ bool permit_abbrev); ++QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, ++ bool permit_abbrev, Error **errp); ++QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, ++ Error **errp); ++QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict, ++ QemuOptsList *list, bool del); ++QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict); ++bool qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp); ++ ++typedef int (*qemu_opts_loopfunc)(void *opaque, QemuOpts *opts, Error **errp); ++int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, ++ void *opaque, Error **errp); ++void qemu_opts_print(QemuOpts *opts, const char *sep); ++void qemu_opts_print_help(QemuOptsList *list, bool print_caption); ++void qemu_opts_free(QemuOptsList *list); ++QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuOpts, qemu_opts_del) ++ ++#endif +diff --git a/include/qemu/option_int.h b/include/qemu/option_int.h +new file mode 100644 +index 00000000..5dd9a516 +--- /dev/null ++++ b/include/qemu/option_int.h +@@ -0,0 +1,54 @@ ++/* ++ * Commandline option parsing functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2009 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_OPTION_INT_H ++#define QEMU_OPTION_INT_H ++ ++#include "qemu/option.h" ++#include "qemu/error-report.h" ++ ++struct QemuOpt { ++ char *name; ++ char *str; ++ ++ const QemuOptDesc *desc; ++ union { ++ bool boolean; ++ uint64_t uint; ++ } value; ++ ++ QemuOpts *opts; ++ QTAILQ_ENTRY(QemuOpt) next; ++}; ++ ++struct QemuOpts { ++ char *id; ++ QemuOptsList *list; ++ Location loc; ++ QTAILQ_HEAD(, QemuOpt) head; ++ QTAILQ_ENTRY(QemuOpts) next; ++}; ++ ++#endif +diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h +new file mode 100644 +index 00000000..fe7c3c5f +--- /dev/null ++++ b/include/qemu/osdep.h +@@ -0,0 +1,831 @@ ++/* ++ * OS includes and handling of OS dependencies ++ * ++ * This header exists to pull in some common system headers that ++ * most code in QEMU will want, and to fix up some possible issues with ++ * it (missing defines, Windows weirdness, and so on). ++ * ++ * To avoid getting into possible circular include dependencies, this ++ * file should not include any other QEMU headers, with the exceptions ++ * of config-host.h, config-target.h, qemu/compiler.h, ++ * sysemu/os-posix.h, sysemu/os-win32.h, glib-compat.h and ++ * qemu/typedefs.h, all of which are doing a similar job to this file ++ * and are under similar constraints. ++ * ++ * This header also contains prototypes for functions defined in ++ * os-*.c and util/oslib-*.c; those would probably be better split ++ * out into separate header files. ++ * ++ * In an ideal world this header would contain only: ++ * (1) things which everybody needs ++ * (2) things without which code would work on most platforms but ++ * fail to compile or misbehave on a minority of host OSes ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++#ifndef QEMU_OSDEP_H ++#define QEMU_OSDEP_H ++ ++#if !defined _FORTIFY_SOURCE && defined __OPTIMIZE__ && __OPTIMIZE__ && defined __linux__ ++# define _FORTIFY_SOURCE 2 ++#endif ++ ++#include "config-host.h" ++#ifdef COMPILING_PER_TARGET ++#include CONFIG_TARGET ++#else ++#include "exec/poison.h" ++#endif ++ ++/* ++ * HOST_WORDS_BIGENDIAN was replaced with HOST_BIG_ENDIAN. Prevent it from ++ * creeping back in. ++ */ ++#pragma GCC poison HOST_WORDS_BIGENDIAN ++ ++/* ++ * TARGET_WORDS_BIGENDIAN was replaced with TARGET_BIG_ENDIAN. Prevent it from ++ * creeping back in. ++ */ ++#pragma GCC poison TARGET_WORDS_BIGENDIAN ++ ++#include "qemu/compiler.h" ++ ++/* Older versions of C++ don't get definitions of various macros from ++ * stdlib.h unless we define these macros before first inclusion of ++ * that system header. ++ */ ++#ifndef __STDC_CONSTANT_MACROS ++#define __STDC_CONSTANT_MACROS ++#endif ++#ifndef __STDC_LIMIT_MACROS ++#define __STDC_LIMIT_MACROS ++#endif ++#ifndef __STDC_FORMAT_MACROS ++#define __STDC_FORMAT_MACROS ++#endif ++ ++/* The following block of code temporarily renames the daemon() function so the ++ * compiler does not see the warning associated with it in stdlib.h on OSX ++ */ ++#ifdef __APPLE__ ++#define daemon qemu_fake_daemon_function ++#include ++#undef daemon ++QEMU_EXTERN_C int daemon(int, int); ++#endif ++ ++#ifdef _WIN32 ++/* as defined in sdkddkver.h */ ++#ifndef _WIN32_WINNT ++#define _WIN32_WINNT 0x0602 /* Windows 8 API (should be >= the one from glib) */ ++#endif ++/* reduces the number of implicitly included headers */ ++#ifndef WIN32_LEAN_AND_MEAN ++#define WIN32_LEAN_AND_MEAN ++#endif ++#endif ++ ++/* enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) */ ++#ifdef __MINGW32__ ++#define __USE_MINGW_ANSI_STDIO 1 ++#endif ++ ++/* ++ * We need the FreeBSD "legacy" definitions. Rust needs the FreeBSD 11 system ++ * calls since it doesn't use libc at all, so we have to emulate that despite ++ * FreeBSD 11 being EOL'd. ++ */ ++#ifdef __FreeBSD__ ++#define _WANT_FREEBSD11_STAT ++#define _WANT_FREEBSD11_STATFS ++#define _WANT_FREEBSD11_DIRENT ++#define _WANT_KERNEL_ERRNO ++#define _WANT_SEMUN ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++/* Put unistd.h before time.h as that triggers localtime_r/gmtime_r ++ * function availability on recentish Mingw-w64 platforms. */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++/* setjmp must be declared before sysemu/os-win32.h ++ * because it is redefined there. */ ++#include ++#include ++ ++#ifdef CONFIG_IOVEC ++#include ++#endif ++ ++#if defined(__linux__) && defined(__sparc__) ++/* The SPARC definition of QEMU_VMALLOC_ALIGN needs SHMLBA */ ++#include ++#endif ++ ++#ifndef _WIN32 ++#include ++#else ++#define WIFEXITED(x) 1 ++#define WEXITSTATUS(x) (x) ++#endif ++ ++#ifdef __APPLE__ ++#include ++#endif ++ ++/* ++ * This is somewhat like a system header; it must be outside any extern "C" ++ * block because it includes system headers itself, including glib.h, ++ * which will not compile if inside an extern "C" block. ++ */ ++#include "glib-compat.h" ++ ++#ifdef _WIN32 ++#include "sysemu/os-win32.h" ++#endif ++ ++#ifdef CONFIG_POSIX ++#include "sysemu/os-posix.h" ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "qemu/typedefs.h" ++ ++/** ++ * Mark a function that executes in coroutine context ++ * ++ * Functions that execute in coroutine context cannot be called directly from ++ * normal functions. In the future it would be nice to enable compiler or ++ * static checker support for catching such errors. This annotation might make ++ * it possible and in the meantime it serves as documentation. ++ * ++ * For example: ++ * ++ * static void coroutine_fn foo(void) { ++ * .... ++ * } ++ */ ++#ifdef __clang__ ++#define coroutine_fn QEMU_ANNOTATE("coroutine_fn") ++#else ++#define coroutine_fn ++#endif ++ ++/** ++ * Mark a function that can suspend when executed in coroutine context, ++ * but can handle running in non-coroutine context too. ++ */ ++#ifdef __clang__ ++#define coroutine_mixed_fn QEMU_ANNOTATE("coroutine_mixed_fn") ++#else ++#define coroutine_mixed_fn ++#endif ++ ++/** ++ * Mark a function that should not be called from a coroutine context. ++ * Usually there will be an analogous, coroutine_fn function that should ++ * be used instead. ++ * ++ * When the function is also marked as coroutine_mixed_fn, the function should ++ * only be called if the caller does not know whether it is in coroutine ++ * context. ++ * ++ * Functions that are only no_coroutine_fn, on the other hand, should not ++ * be called from within coroutines at all. This for example includes ++ * functions that block. ++ * ++ * In the future it would be nice to enable compiler or static checker ++ * support for catching such errors. This annotation is the first step ++ * towards this, and in the meantime it serves as documentation. ++ * ++ * For example: ++ * ++ * static void no_coroutine_fn foo(void) { ++ * .... ++ * } ++ */ ++#ifdef __clang__ ++#define no_coroutine_fn QEMU_ANNOTATE("no_coroutine_fn") ++#else ++#define no_coroutine_fn ++#endif ++ ++ ++/* ++ * For mingw, as of v6.0.0, the function implementing the assert macro is ++ * not marked as noreturn, so the compiler cannot delete code following an ++ * assert(false) as unused. We rely on this within the code base to delete ++ * code that is unreachable when features are disabled. ++ * All supported versions of Glib's g_assert() satisfy this requirement. ++ */ ++#ifdef __MINGW32__ ++#undef assert ++#define assert(x) g_assert(x) ++#endif ++ ++/** ++ * qemu_build_not_reached() ++ * ++ * The compiler, during optimization, is expected to prove that a call ++ * to this function cannot be reached and remove it. If the compiler ++ * supports QEMU_ERROR, this will be reported at compile time; otherwise ++ * this will be reported at link time due to the missing symbol. ++ */ ++G_NORETURN ++void QEMU_ERROR("code path is reachable") ++ qemu_build_not_reached_always(void); ++#if defined(__OPTIMIZE__) && !defined(__NO_INLINE__) ++#define qemu_build_not_reached() qemu_build_not_reached_always() ++#else ++#define qemu_build_not_reached() g_assert_not_reached() ++#endif ++ ++/** ++ * qemu_build_assert() ++ * ++ * The compiler, during optimization, is expected to prove that the ++ * assertion is true. ++ */ ++#define qemu_build_assert(test) while (!(test)) qemu_build_not_reached() ++ ++/* ++ * According to waitpid man page: ++ * WCOREDUMP ++ * This macro is not specified in POSIX.1-2001 and is not ++ * available on some UNIX implementations (e.g., AIX, SunOS). ++ * Therefore, enclose its use inside #ifdef WCOREDUMP ... #endif. ++ */ ++#ifndef WCOREDUMP ++#define WCOREDUMP(status) 0 ++#endif ++/* ++ * We have a lot of unaudited code that may fail in strange ways, or ++ * even be a security risk during migration, if you disable assertions ++ * at compile-time. You may comment out these safety checks if you ++ * absolutely want to disable assertion overhead, but it is not ++ * supported upstream so the risk is all yours. Meanwhile, please ++ * submit patches to remove any side-effects inside an assertion, or ++ * fixing error handling that should use Error instead of assert. ++ */ ++#ifdef NDEBUG ++#error building with NDEBUG is not supported ++#endif ++#ifdef G_DISABLE_ASSERT ++#error building with G_DISABLE_ASSERT is not supported ++#endif ++ ++#ifndef O_LARGEFILE ++#define O_LARGEFILE 0 ++#endif ++#ifndef O_BINARY ++#define O_BINARY 0 ++#endif ++#ifndef MAP_ANONYMOUS ++#define MAP_ANONYMOUS MAP_ANON ++#endif ++#ifndef MAP_NORESERVE ++#define MAP_NORESERVE 0 ++#endif ++#ifndef ENOMEDIUM ++#define ENOMEDIUM ENODEV ++#endif ++#if !defined(ENOTSUP) ++#define ENOTSUP 4096 ++#endif ++#if !defined(ECANCELED) ++#define ECANCELED 4097 ++#endif ++#if !defined(EMEDIUMTYPE) ++#define EMEDIUMTYPE 4098 ++#endif ++#if !defined(ESHUTDOWN) ++#define ESHUTDOWN 4099 ++#endif ++ ++#define RETRY_ON_EINTR(expr) \ ++ (__extension__ \ ++ ({ typeof(expr) __result; \ ++ do { \ ++ __result = (expr); \ ++ } while (__result == -1 && errno == EINTR); \ ++ __result; })) ++ ++/* time_t may be either 32 or 64 bits depending on the host OS, and ++ * can be either signed or unsigned, so we can't just hardcode a ++ * specific maximum value. This is not a C preprocessor constant, ++ * so you can't use TIME_MAX in an #ifdef, but for our purposes ++ * this isn't a problem. ++ */ ++ ++/* The macros TYPE_SIGNED, TYPE_WIDTH, and TYPE_MAXIMUM are from ++ * Gnulib, and are under the LGPL v2.1 or (at your option) any ++ * later version. ++ */ ++ ++/* True if the real type T is signed. */ ++#define TYPE_SIGNED(t) (!((t)0 < (t)-1)) ++ ++/* The width in bits of the integer type or expression T. ++ * Padding bits are not supported. ++ */ ++#define TYPE_WIDTH(t) (sizeof(t) * CHAR_BIT) ++ ++/* The maximum and minimum values for the integer type T. */ ++#define TYPE_MAXIMUM(t) \ ++ ((t) (!TYPE_SIGNED(t) \ ++ ? (t)-1 \ ++ : ((((t)1 << (TYPE_WIDTH(t) - 2)) - 1) * 2 + 1))) ++ ++#ifndef TIME_MAX ++#define TIME_MAX TYPE_MAXIMUM(time_t) ++#endif ++ ++/* Mac OSX has a bug that incorrectly defines SIZE_MAX with ++ * the wrong type. Our replacement isn't usable in preprocessor ++ * expressions, but it is sufficient for our needs. */ ++#ifdef HAVE_BROKEN_SIZE_MAX ++#undef SIZE_MAX ++#define SIZE_MAX ((size_t)-1) ++#endif ++ ++/* ++ * Two variations of MIN/MAX macros. The first is for runtime use, and ++ * evaluates arguments only once (so it is safe even with side ++ * effects), but will not work in constant contexts (such as array ++ * size declarations) because of the '{}'. The second is for constant ++ * expression use, where evaluating arguments twice is safe because ++ * the result is going to be constant anyway, but will not work in a ++ * runtime context because of a void expression where a value is ++ * expected. Thus, both gcc and clang will fail to compile if you use ++ * the wrong macro (even if the error may seem a bit cryptic). ++ * ++ * Note that neither form is usable as an #if condition; if you truly ++ * need to write conditional code that depends on a minimum or maximum ++ * determined by the pre-processor instead of the compiler, you'll ++ * have to open-code it. Sadly, Coverity is severely confused by the ++ * constant variants, so we have to dumb things down there. ++ * ++ * Preprocessor sorcery ahead: use different identifiers for the local ++ * variables in each expansion, so we can nest macro calls without ++ * shadowing variables. ++ */ ++#define MIN_INTERNAL(a, b, _a, _b) \ ++ ({ \ ++ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ ++ _a < _b ? _a : _b; \ ++ }) ++#undef MIN ++#define MIN(a, b) \ ++ MIN_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) ++ ++#define MAX_INTERNAL(a, b, _a, _b) \ ++ ({ \ ++ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ ++ _a > _b ? _a : _b; \ ++ }) ++#undef MAX ++#define MAX(a, b) \ ++ MAX_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) ++ ++#ifdef __COVERITY__ ++# define MIN_CONST(a, b) ((a) < (b) ? (a) : (b)) ++# define MAX_CONST(a, b) ((a) > (b) ? (a) : (b)) ++#else ++# define MIN_CONST(a, b) \ ++ __builtin_choose_expr( \ ++ __builtin_constant_p(a) && __builtin_constant_p(b), \ ++ (a) < (b) ? (a) : (b), \ ++ ((void)0)) ++# define MAX_CONST(a, b) \ ++ __builtin_choose_expr( \ ++ __builtin_constant_p(a) && __builtin_constant_p(b), \ ++ (a) > (b) ? (a) : (b), \ ++ ((void)0)) ++#endif ++ ++/* ++ * Minimum function that returns zero only if both values are zero. ++ * Intended for use with unsigned values only. ++ * ++ * Preprocessor sorcery ahead: use different identifiers for the local ++ * variables in each expansion, so we can nest macro calls without ++ * shadowing variables. ++ */ ++#define MIN_NON_ZERO_INTERNAL(a, b, _a, _b) \ ++ ({ \ ++ typeof(1 ? (a) : (b)) _a = (a), _b = (b); \ ++ _a == 0 ? _b : (_b == 0 || _b > _a) ? _a : _b; \ ++ }) ++#define MIN_NON_ZERO(a, b) \ ++ MIN_NON_ZERO_INTERNAL((a), (b), MAKE_IDENTIFIER(_a), MAKE_IDENTIFIER(_b)) ++ ++/* ++ * Round number down to multiple. Safe when m is not a power of 2 (see ++ * ROUND_DOWN for a faster version when a power of 2 is guaranteed). ++ */ ++#define QEMU_ALIGN_DOWN(n, m) ((n) / (m) * (m)) ++ ++/* ++ * Round number up to multiple. Safe when m is not a power of 2 (see ++ * ROUND_UP for a faster version when a power of 2 is guaranteed). ++ */ ++#define QEMU_ALIGN_UP(n, m) QEMU_ALIGN_DOWN((n) + (m) - 1, (m)) ++ ++/* Check if n is a multiple of m */ ++#define QEMU_IS_ALIGNED(n, m) (((n) % (m)) == 0) ++ ++/* n-byte align pointer down */ ++#define QEMU_ALIGN_PTR_DOWN(p, n) \ ++ ((typeof(p))QEMU_ALIGN_DOWN((uintptr_t)(p), (n))) ++ ++/* n-byte align pointer up */ ++#define QEMU_ALIGN_PTR_UP(p, n) \ ++ ((typeof(p))QEMU_ALIGN_UP((uintptr_t)(p), (n))) ++ ++/* Check if pointer p is n-bytes aligned */ ++#define QEMU_PTR_IS_ALIGNED(p, n) QEMU_IS_ALIGNED((uintptr_t)(p), (n)) ++ ++/* ++ * Round number down to multiple. Requires that d be a power of 2 (see ++ * QEMU_ALIGN_UP for a safer but slower version on arbitrary ++ * numbers); works even if d is a smaller type than n. ++ */ ++#ifndef ROUND_DOWN ++#define ROUND_DOWN(n, d) ((n) & -(0 ? (n) : (d))) ++#endif ++ ++/* ++ * Round number up to multiple. Requires that d be a power of 2 (see ++ * QEMU_ALIGN_UP for a safer but slower version on arbitrary ++ * numbers); works even if d is a smaller type than n. ++ */ ++#ifndef ROUND_UP ++#define ROUND_UP(n, d) ROUND_DOWN((n) + (d) - 1, (d)) ++#endif ++ ++#ifndef DIV_ROUND_UP ++#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) ++#endif ++ ++/* ++ * &(x)[0] is always a pointer - if it's same type as x then the argument is a ++ * pointer, not an array. ++ */ ++#define QEMU_IS_ARRAY(x) (!__builtin_types_compatible_p(typeof(x), \ ++ typeof(&(x)[0]))) ++#ifndef ARRAY_SIZE ++#define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])) + \ ++ QEMU_BUILD_BUG_ON_ZERO(!QEMU_IS_ARRAY(x))) ++#endif ++ ++int qemu_daemon(int nochdir, int noclose); ++void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared, ++ bool noreserve); ++void qemu_anon_ram_free(void *ptr, size_t size); ++ ++#ifdef _WIN32 ++#define HAVE_CHARDEV_SERIAL 1 ++#define HAVE_CHARDEV_PARALLEL 1 ++#else ++#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ ++ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ ++ || defined(__GLIBC__) || defined(__APPLE__) ++#define HAVE_CHARDEV_SERIAL 1 ++#endif ++#if defined(__linux__) || defined(__FreeBSD__) \ ++ || defined(__FreeBSD_kernel__) || defined(__DragonFly__) ++#define HAVE_CHARDEV_PARALLEL 1 ++#endif ++#endif ++ ++#if defined(__HAIKU__) ++#define SIGIO SIGPOLL ++#endif ++ ++#ifdef HAVE_MADVISE_WITHOUT_PROTOTYPE ++/* ++ * See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for discussion ++ * about Solaris missing the madvise() prototype. ++ */ ++int madvise(char *, size_t, int); ++#endif ++ ++#if defined(CONFIG_LINUX) ++#ifndef BUS_MCEERR_AR ++#define BUS_MCEERR_AR 4 ++#endif ++#ifndef BUS_MCEERR_AO ++#define BUS_MCEERR_AO 5 ++#endif ++#endif ++ ++#if defined(__linux__) && \ ++ (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) \ ++ || defined(__powerpc64__)) ++ /* Use 2 MiB alignment so transparent hugepages can be used by KVM. ++ Valgrind does not support alignments larger than 1 MiB, ++ therefore we need special code which handles running on Valgrind. */ ++# define QEMU_VMALLOC_ALIGN (512 * 4096) ++#elif defined(__linux__) && defined(__s390x__) ++ /* Use 1 MiB (segment size) alignment so gmap can be used by KVM. */ ++# define QEMU_VMALLOC_ALIGN (256 * 4096) ++#elif defined(__linux__) && defined(__sparc__) ++# define QEMU_VMALLOC_ALIGN MAX(qemu_real_host_page_size(), SHMLBA) ++#elif defined(__linux__) && defined(__loongarch__) ++ /* ++ * For transparent hugepage optimization, it has better be huge page ++ * aligned. LoongArch host system supports two kinds of pagesize: 4K ++ * and 16K, here calculate huge page size from host page size ++ */ ++# define QEMU_VMALLOC_ALIGN (qemu_real_host_page_size() * \ ++ qemu_real_host_page_size() / sizeof(long)) ++#else ++# define QEMU_VMALLOC_ALIGN qemu_real_host_page_size() ++#endif ++ ++#ifdef CONFIG_POSIX ++struct qemu_signalfd_siginfo { ++ uint32_t ssi_signo; /* Signal number */ ++ int32_t ssi_errno; /* Error number (unused) */ ++ int32_t ssi_code; /* Signal code */ ++ uint32_t ssi_pid; /* PID of sender */ ++ uint32_t ssi_uid; /* Real UID of sender */ ++ int32_t ssi_fd; /* File descriptor (SIGIO) */ ++ uint32_t ssi_tid; /* Kernel timer ID (POSIX timers) */ ++ uint32_t ssi_band; /* Band event (SIGIO) */ ++ uint32_t ssi_overrun; /* POSIX timer overrun count */ ++ uint32_t ssi_trapno; /* Trap number that caused signal */ ++ int32_t ssi_status; /* Exit status or signal (SIGCHLD) */ ++ int32_t ssi_int; /* Integer sent by sigqueue(2) */ ++ uint64_t ssi_ptr; /* Pointer sent by sigqueue(2) */ ++ uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */ ++ uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */ ++ uint64_t ssi_addr; /* Address that generated signal ++ (for hardware-generated signals) */ ++ uint8_t pad[48]; /* Pad size to 128 bytes (allow for ++ additional fields in the future) */ ++}; ++ ++int qemu_signalfd(const sigset_t *mask); ++void sigaction_invoke(struct sigaction *action, ++ struct qemu_signalfd_siginfo *info); ++#endif ++ ++/* ++ * Don't introduce new usage of this function, prefer the following ++ * qemu_open/qemu_create that take an "Error **errp" ++ */ ++int qemu_open_old(const char *name, int flags, ...); ++int qemu_open(const char *name, int flags, Error **errp); ++int qemu_create(const char *name, int flags, mode_t mode, Error **errp); ++int qemu_close(int fd); ++int qemu_unlink(const char *name); ++#ifndef _WIN32 ++int qemu_dup_flags(int fd, int flags); ++int qemu_dup(int fd); ++int qemu_lock_fd(int fd, int64_t start, int64_t len, bool exclusive); ++int qemu_unlock_fd(int fd, int64_t start, int64_t len); ++int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive); ++bool qemu_has_ofd_lock(void); ++#endif ++ ++bool qemu_has_direct_io(void); ++ ++#if defined(__HAIKU__) && defined(__i386__) ++#define FMT_pid "%ld" ++#elif defined(WIN64) ++#define FMT_pid "%" PRId64 ++#else ++#define FMT_pid "%d" ++#endif ++ ++bool qemu_write_pidfile(const char *pidfile, Error **errp); ++ ++int qemu_get_thread_id(void); ++ ++#ifndef CONFIG_IOVEC ++struct iovec { ++ void *iov_base; ++ size_t iov_len; ++}; ++/* ++ * Use the same value as Linux for now. ++ */ ++#define IOV_MAX 1024 ++ ++ssize_t readv(int fd, const struct iovec *iov, int iov_cnt); ++ssize_t writev(int fd, const struct iovec *iov, int iov_cnt); ++#endif ++ ++#ifdef _WIN32 ++static inline void qemu_timersub(const struct timeval *val1, ++ const struct timeval *val2, ++ struct timeval *res) ++{ ++ res->tv_sec = val1->tv_sec - val2->tv_sec; ++ if (val1->tv_usec < val2->tv_usec) { ++ res->tv_sec--; ++ res->tv_usec = val1->tv_usec - val2->tv_usec + 1000 * 1000; ++ } else { ++ res->tv_usec = val1->tv_usec - val2->tv_usec; ++ } ++} ++#else ++#define qemu_timersub timersub ++#endif ++ ++ssize_t qemu_write_full(int fd, const void *buf, size_t count) ++ G_GNUC_WARN_UNUSED_RESULT; ++ ++void qemu_set_cloexec(int fd); ++ ++/* Return a dynamically allocated directory path that is appropriate for storing ++ * local state. ++ * ++ * The caller is responsible for releasing the value returned with g_free() ++ * after use. ++ */ ++char *qemu_get_local_state_dir(void); ++ ++/** ++ * qemu_getauxval: ++ * @type: the auxiliary vector key to lookup ++ * ++ * Search the auxiliary vector for @type, returning the value ++ * or 0 if @type is not present. ++ */ ++unsigned long qemu_getauxval(unsigned long type); ++ ++void qemu_set_tty_echo(int fd, bool echo); ++ ++typedef struct ThreadContext ThreadContext; ++ ++/** ++ * qemu_prealloc_mem: ++ * @fd: the fd mapped into the area, -1 for anonymous memory ++ * @area: start address of the are to preallocate ++ * @sz: the size of the area to preallocate ++ * @max_threads: maximum number of threads to use ++ * @tc: prealloc context threads pointer, NULL if not in use ++ * @async: request asynchronous preallocation, requires @tc ++ * @errp: returns an error if this function fails ++ * ++ * Preallocate memory (populate/prefault page tables writable) for the virtual ++ * memory area starting at @area with the size of @sz. After a successful call, ++ * each page in the area was faulted in writable at least once, for example, ++ * after allocating file blocks for mapped files. ++ * ++ * When setting @async, allocation might be performed asynchronously. ++ * qemu_finish_async_prealloc_mem() must be called to finish any asynchronous ++ * preallocation. ++ * ++ * Return: true on success, else false setting @errp with error. ++ */ ++bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, ++ ThreadContext *tc, bool async, Error **errp); ++ ++/** ++ * qemu_finish_async_prealloc_mem: ++ * @errp: returns an error if this function fails ++ * ++ * Finish all outstanding asynchronous memory preallocation. ++ * ++ * Return: true on success, else false setting @errp with error. ++ */ ++bool qemu_finish_async_prealloc_mem(Error **errp); ++ ++/** ++ * qemu_get_pid_name: ++ * @pid: pid of a process ++ * ++ * For given @pid fetch its name. Caller is responsible for ++ * freeing the string when no longer needed. ++ * Returns allocated string on success, NULL on failure. ++ */ ++char *qemu_get_pid_name(pid_t pid); ++ ++/* Using intptr_t ensures that qemu_*_page_mask is sign-extended even ++ * when intptr_t is 32-bit and we are aligning a long long. ++ */ ++static inline uintptr_t qemu_real_host_page_size(void) ++{ ++ return getpagesize(); ++} ++ ++static inline intptr_t qemu_real_host_page_mask(void) ++{ ++ return -(intptr_t)qemu_real_host_page_size(); ++} ++ ++/* ++ * After using getopt or getopt_long, if you need to parse another set ++ * of options, then you must reset optind. Unfortunately the way to ++ * do this varies between implementations of getopt. ++ */ ++static inline void qemu_reset_optind(void) ++{ ++#ifdef HAVE_OPTRESET ++ optind = 1; ++ optreset = 1; ++#else ++ optind = 0; ++#endif ++} ++ ++int qemu_fdatasync(int fd); ++ ++/** ++ * qemu_close_all_open_fd: ++ * ++ * Close all open file descriptors except the ones supplied in the @skip array ++ * ++ * @skip: ordered array of distinct file descriptors that should not be closed ++ * if any, or NULL. ++ * @nskip: number of entries in the @skip array or 0 if @skip is NULL. ++ */ ++void qemu_close_all_open_fd(const int *skip, unsigned int nskip); ++ ++/** ++ * Sync changes made to the memory mapped file back to the backing ++ * storage. For POSIX compliant systems this will fallback ++ * to regular msync call. Otherwise it will trigger whole file sync ++ * (including the metadata case there is no support to skip that otherwise) ++ * ++ * @addr - start of the memory area to be synced ++ * @length - length of the are to be synced ++ * @fd - file descriptor for the file to be synced ++ * (mandatory only for POSIX non-compliant systems) ++ */ ++int qemu_msync(void *addr, size_t length, int fd); ++ ++/** ++ * qemu_get_host_physmem: ++ * ++ * Operating system agnostic way of querying host memory. ++ * ++ * Returns amount of physical memory on the system. This is purely ++ * advisery and may return 0 if we can't work it out. At the other ++ * end we saturate to SIZE_MAX if you are lucky enough to have that ++ * much memory. ++ */ ++size_t qemu_get_host_physmem(void); ++ ++/* ++ * Toggle write/execute on the pages marked MAP_JIT ++ * for the current thread. ++ */ ++#ifdef __APPLE__ ++static inline void qemu_thread_jit_execute(void) ++{ ++ pthread_jit_write_protect_np(true); ++} ++ ++static inline void qemu_thread_jit_write(void) ++{ ++ pthread_jit_write_protect_np(false); ++} ++#else ++static inline void qemu_thread_jit_write(void) {} ++static inline void qemu_thread_jit_execute(void) {} ++#endif ++ ++/** ++ * Platforms which do not support system() return ENOSYS ++ */ ++#ifndef HAVE_SYSTEM_FUNCTION ++#define system platform_does_not_support_system ++static inline int platform_does_not_support_system(const char *command) ++{ ++ errno = ENOSYS; ++ return -1; ++} ++#endif /* !HAVE_SYSTEM_FUNCTION */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/qemu/processor.h b/include/qemu/processor.h +new file mode 100644 +index 00000000..9f0dcdf2 +--- /dev/null ++++ b/include/qemu/processor.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2016, Emilio G. Cota ++ * ++ * License: GNU GPL, version 2. ++ * See the COPYING file in the top-level directory. ++ */ ++#ifndef QEMU_PROCESSOR_H ++#define QEMU_PROCESSOR_H ++ ++#if defined(__i386__) || defined(__x86_64__) ++# define cpu_relax() asm volatile("rep; nop" ::: "memory") ++ ++#elif defined(__aarch64__) ++# define cpu_relax() asm volatile("yield" ::: "memory") ++ ++#elif defined(__powerpc64__) ++/* set Hardware Multi-Threading (HMT) priority to low; then back to medium */ ++# define cpu_relax() asm volatile("or 1, 1, 1;" \ ++ "or 2, 2, 2;" ::: "memory") ++ ++#else ++# define cpu_relax() barrier() ++#endif ++ ++#endif /* QEMU_PROCESSOR_H */ +diff --git a/include/qemu/progress_meter.h b/include/qemu/progress_meter.h +new file mode 100644 +index 00000000..0f2c0a32 +--- /dev/null ++++ b/include/qemu/progress_meter.h +@@ -0,0 +1,62 @@ ++/* ++ * Helper functionality for some process progress tracking. ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * Copyright (c) 2020 Virtuozzo International GmbH ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_PROGRESS_METER_H ++#define QEMU_PROGRESS_METER_H ++ ++#include "qemu/thread.h" ++ ++typedef struct ProgressMeter { ++ /** ++ * Current progress. The unit is arbitrary as long as the ratio between ++ * current and total represents the estimated percentage ++ * of work already done. ++ */ ++ uint64_t current; ++ ++ /** Estimated current value at the completion of the process */ ++ uint64_t total; ++ ++ QemuMutex lock; /* protects concurrent access to above fields */ ++} ProgressMeter; ++ ++void progress_init(ProgressMeter *pm); ++void progress_destroy(ProgressMeter *pm); ++ ++/* Get a snapshot of internal current and total values */ ++void progress_get_snapshot(ProgressMeter *pm, uint64_t *current, ++ uint64_t *total); ++ ++/* Increases the amount of work done so far by @done */ ++void progress_work_done(ProgressMeter *pm, uint64_t done); ++ ++/* Sets how much work has to be done to complete to @remaining */ ++void progress_set_remaining(ProgressMeter *pm, uint64_t remaining); ++ ++/* Increases the total work to do by @delta */ ++void progress_increase_remaining(ProgressMeter *pm, uint64_t delta); ++ ++#endif /* QEMU_PROGRESS_METER_H */ +diff --git a/include/qemu/qdist.h b/include/qemu/qdist.h +new file mode 100644 +index 00000000..bfb32115 +--- /dev/null ++++ b/include/qemu/qdist.h +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) 2016, Emilio G. Cota ++ * ++ * License: GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++#ifndef QEMU_QDIST_H ++#define QEMU_QDIST_H ++ ++#include "qemu/bitops.h" ++ ++/* ++ * Samples with the same 'x value' end up in the same qdist_entry, ++ * e.g. inc(0.1) and inc(0.1) end up as {x=0.1, count=2}. ++ * ++ * Binning happens only at print time, so that we retain the flexibility to ++ * choose the binning. This might not be ideal for workloads that do not care ++ * much about precision and insert many samples all with different x values; ++ * in that case, pre-binning (e.g. entering both 0.115 and 0.097 as 0.1) ++ * should be considered. ++ */ ++struct qdist_entry { ++ double x; ++ unsigned long count; ++}; ++ ++struct qdist { ++ struct qdist_entry *entries; ++ size_t n; ++ size_t size; ++}; ++ ++#define QDIST_PR_BORDER BIT(0) ++#define QDIST_PR_LABELS BIT(1) ++/* the remaining options only work if PR_LABELS is set */ ++#define QDIST_PR_NODECIMAL BIT(2) ++#define QDIST_PR_PERCENT BIT(3) ++#define QDIST_PR_100X BIT(4) ++#define QDIST_PR_NOBINRANGE BIT(5) ++ ++void qdist_init(struct qdist *dist); ++void qdist_destroy(struct qdist *dist); ++ ++void qdist_add(struct qdist *dist, double x, long count); ++void qdist_inc(struct qdist *dist, double x); ++double qdist_xmin(const struct qdist *dist); ++double qdist_xmax(const struct qdist *dist); ++double qdist_avg(const struct qdist *dist); ++unsigned long qdist_sample_count(const struct qdist *dist); ++size_t qdist_unique_entries(const struct qdist *dist); ++ ++/* callers must free the returned string with g_free() */ ++char *qdist_pr_plain(const struct qdist *dist, size_t n_groups); ++ ++/* callers must free the returned string with g_free() */ ++char *qdist_pr(const struct qdist *dist, size_t n_groups, uint32_t opt); ++ ++/* Only qdist code and test code should ever call this function */ ++void qdist_bin__internal(struct qdist *to, const struct qdist *from, size_t n); ++ ++#endif /* QEMU_QDIST_H */ +diff --git a/include/qemu/qemu-print.h b/include/qemu/qemu-print.h +new file mode 100644 +index 00000000..1b709206 +--- /dev/null ++++ b/include/qemu/qemu-print.h +@@ -0,0 +1,23 @@ ++/* ++ * Print to stream or current monitor ++ * ++ * Copyright (C) 2019 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_PRINT_H ++#define QEMU_PRINT_H ++ ++int qemu_vprintf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); ++int qemu_printf(const char *fmt, ...) G_GNUC_PRINTF(1, 2); ++ ++int qemu_vfprintf(FILE *stream, const char *fmt, va_list ap) ++ G_GNUC_PRINTF(2, 0); ++int qemu_fprintf(FILE *stream, const char *fmt, ...) G_GNUC_PRINTF(2, 3); ++ ++#endif +diff --git a/include/qemu/qemu-progress.h b/include/qemu/qemu-progress.h +new file mode 100644 +index 00000000..137e1c31 +--- /dev/null ++++ b/include/qemu/qemu-progress.h +@@ -0,0 +1,8 @@ ++#ifndef QEMU_PROGRESS_H ++#define QEMU_PROGRESS_H ++ ++void qemu_progress_init(int enabled, float min_skip); ++void qemu_progress_end(void); ++void qemu_progress_print(float delta, int max); ++ ++#endif /* QEMU_PROGRESS_H */ +diff --git a/include/qemu/qsp.h b/include/qemu/qsp.h +new file mode 100644 +index 00000000..bf36aabf +--- /dev/null ++++ b/include/qemu/qsp.h +@@ -0,0 +1,27 @@ ++/* ++ * qsp.c - QEMU Synchronization Profiler ++ * ++ * Copyright (C) 2018, Emilio G. Cota ++ * ++ * License: GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ * Note: this header file can *only* be included from thread.h. ++ */ ++#ifndef QEMU_QSP_H ++#define QEMU_QSP_H ++ ++enum QSPSortBy { ++ QSP_SORT_BY_TOTAL_WAIT_TIME, ++ QSP_SORT_BY_AVG_WAIT_TIME, ++}; ++ ++void qsp_report(size_t max, enum QSPSortBy sort_by, ++ bool callsite_coalesce); ++ ++bool qsp_is_enabled(void); ++void qsp_enable(void); ++void qsp_disable(void); ++void qsp_reset(void); ++ ++#endif /* QEMU_QSP_H */ +diff --git a/include/qemu/queue.h b/include/qemu/queue.h +new file mode 100644 +index 00000000..e029e7bf +--- /dev/null ++++ b/include/qemu/queue.h +@@ -0,0 +1,576 @@ ++/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */ ++ ++/* ++ * QEMU version: Copy from netbsd, removed debug code, removed some of ++ * the implementations. Left in singly-linked lists, lists, simple ++ * queues, and tail queues. ++ */ ++ ++/* ++ * Copyright (c) 1991, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * @(#)queue.h 8.5 (Berkeley) 8/20/94 ++ */ ++ ++#ifndef QEMU_SYS_QUEUE_H ++#define QEMU_SYS_QUEUE_H ++ ++/* ++ * This file defines four types of data structures: singly-linked lists, ++ * lists, simple queues, and tail queues. ++ * ++ * A singly-linked list is headed by a single forward pointer. The ++ * elements are singly linked for minimum space and pointer manipulation ++ * overhead at the expense of O(n) removal for arbitrary elements. New ++ * elements can be added to the list after an existing element or at the ++ * head of the list. Elements being removed from the head of the list ++ * should use the explicit macro for this purpose for optimum ++ * efficiency. A singly-linked list may only be traversed in the forward ++ * direction. Singly-linked lists are ideal for applications with large ++ * datasets and few or no removals or for implementing a LIFO queue. ++ * ++ * A list is headed by a single forward pointer (or an array of forward ++ * pointers for a hash table header). The elements are doubly linked ++ * so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before ++ * or after an existing element or at the head of the list. A list ++ * may only be traversed in the forward direction. ++ * ++ * A simple queue is headed by a pair of pointers, one the head of the ++ * list and the other to the tail of the list. The elements are singly ++ * linked to save space, so elements can only be removed from the ++ * head of the list. New elements can be added to the list after ++ * an existing element, at the head of the list, or at the end of the ++ * list. A simple queue may only be traversed in the forward direction. ++ * ++ * A tail queue is headed by a pair of pointers, one to the head of the ++ * list and the other to the tail of the list. The elements are doubly ++ * linked so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before or ++ * after an existing element, at the head of the list, or at the end of ++ * the list. A tail queue may be traversed in either direction. ++ * ++ * For details on the use of these macros, see the queue(3) manual page. ++ */ ++ ++/* ++ * List definitions. ++ */ ++#define QLIST_HEAD(name, type) \ ++struct name { \ ++ struct type *lh_first; /* first element */ \ ++} ++ ++#define QLIST_HEAD_INITIALIZER(head) \ ++ { NULL } ++ ++#define QLIST_ENTRY(type) \ ++struct { \ ++ struct type *le_next; /* next element */ \ ++ struct type **le_prev; /* address of previous next element */ \ ++} ++ ++/* ++ * List functions. ++ */ ++#define QLIST_INIT(head) do { \ ++ (head)->lh_first = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_SWAP(dstlist, srclist, field) do { \ ++ void *tmplist; \ ++ tmplist = (srclist)->lh_first; \ ++ (srclist)->lh_first = (dstlist)->lh_first; \ ++ if ((srclist)->lh_first != NULL) { \ ++ (srclist)->lh_first->field.le_prev = &(srclist)->lh_first; \ ++ } \ ++ (dstlist)->lh_first = tmplist; \ ++ if ((dstlist)->lh_first != NULL) { \ ++ (dstlist)->lh_first->field.le_prev = &(dstlist)->lh_first; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_INSERT_AFTER(listelm, elm, field) do { \ ++ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ ++ (listelm)->field.le_next->field.le_prev = \ ++ &(elm)->field.le_next; \ ++ (listelm)->field.le_next = (elm); \ ++ (elm)->field.le_prev = &(listelm)->field.le_next; \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_INSERT_BEFORE(listelm, elm, field) do { \ ++ (elm)->field.le_prev = (listelm)->field.le_prev; \ ++ (elm)->field.le_next = (listelm); \ ++ *(listelm)->field.le_prev = (elm); \ ++ (listelm)->field.le_prev = &(elm)->field.le_next; \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ ++ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ ++ (head)->lh_first = (elm); \ ++ (elm)->field.le_prev = &(head)->lh_first; \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_REMOVE(elm, field) do { \ ++ if ((elm)->field.le_next != NULL) \ ++ (elm)->field.le_next->field.le_prev = \ ++ (elm)->field.le_prev; \ ++ *(elm)->field.le_prev = (elm)->field.le_next; \ ++ (elm)->field.le_next = NULL; \ ++ (elm)->field.le_prev = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++/* ++ * Like QLIST_REMOVE() but safe to call when elm is not in a list ++ */ ++#define QLIST_SAFE_REMOVE(elm, field) do { \ ++ if ((elm)->field.le_prev != NULL) { \ ++ if ((elm)->field.le_next != NULL) \ ++ (elm)->field.le_next->field.le_prev = \ ++ (elm)->field.le_prev; \ ++ *(elm)->field.le_prev = (elm)->field.le_next; \ ++ (elm)->field.le_next = NULL; \ ++ (elm)->field.le_prev = NULL; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++/* Is elm in a list? */ ++#define QLIST_IS_INSERTED(elm, field) ((elm)->field.le_prev != NULL) ++ ++#define QLIST_FOREACH(var, head, field) \ ++ for ((var) = ((head)->lh_first); \ ++ (var); \ ++ (var) = ((var)->field.le_next)) ++ ++#define QLIST_FOREACH_SAFE(var, head, field, next_var) \ ++ for ((var) = ((head)->lh_first); \ ++ (var) && ((next_var) = ((var)->field.le_next), 1); \ ++ (var) = (next_var)) ++ ++/* ++ * List access methods. ++ */ ++#define QLIST_EMPTY(head) ((head)->lh_first == NULL) ++#define QLIST_FIRST(head) ((head)->lh_first) ++#define QLIST_NEXT(elm, field) ((elm)->field.le_next) ++ ++ ++/* ++ * Singly-linked List definitions. ++ */ ++#define QSLIST_HEAD(name, type) \ ++struct name { \ ++ struct type *slh_first; /* first element */ \ ++} ++ ++#define QSLIST_HEAD_INITIALIZER(head) \ ++ { NULL } ++ ++#define QSLIST_ENTRY(type) \ ++struct { \ ++ struct type *sle_next; /* next element */ \ ++} ++ ++/* ++ * Singly-linked List functions. ++ */ ++#define QSLIST_INIT(head) do { \ ++ (head)->slh_first = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_INSERT_AFTER(slistelm, elm, field) do { \ ++ (elm)->field.sle_next = (slistelm)->field.sle_next; \ ++ (slistelm)->field.sle_next = (elm); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_INSERT_HEAD(head, elm, field) do { \ ++ (elm)->field.sle_next = (head)->slh_first; \ ++ (head)->slh_first = (elm); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_INSERT_HEAD_ATOMIC(head, elm, field) do { \ ++ typeof(elm) save_sle_next; \ ++ do { \ ++ save_sle_next = (elm)->field.sle_next = (head)->slh_first; \ ++ } while (qatomic_cmpxchg(&(head)->slh_first, save_sle_next, (elm)) !=\ ++ save_sle_next); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_MOVE_ATOMIC(dest, src) do { \ ++ (dest)->slh_first = qatomic_xchg(&(src)->slh_first, NULL); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE_HEAD(head, field) do { \ ++ typeof((head)->slh_first) elm = (head)->slh_first; \ ++ (head)->slh_first = elm->field.sle_next; \ ++ elm->field.sle_next = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE_AFTER(slistelm, field) do { \ ++ typeof(slistelm) next = (slistelm)->field.sle_next; \ ++ (slistelm)->field.sle_next = next->field.sle_next; \ ++ next->field.sle_next = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE(head, elm, type, field) do { \ ++ if ((head)->slh_first == (elm)) { \ ++ QSLIST_REMOVE_HEAD((head), field); \ ++ } else { \ ++ struct type *curelm = (head)->slh_first; \ ++ while (curelm->field.sle_next != (elm)) \ ++ curelm = curelm->field.sle_next; \ ++ curelm->field.sle_next = curelm->field.sle_next->field.sle_next; \ ++ (elm)->field.sle_next = NULL; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_FOREACH(var, head, field) \ ++ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) ++ ++#define QSLIST_FOREACH_SAFE(var, head, field, tvar) \ ++ for ((var) = QSLIST_FIRST((head)); \ ++ (var) && ((tvar) = QSLIST_NEXT((var), field), 1); \ ++ (var) = (tvar)) ++ ++/* ++ * Singly-linked List access methods. ++ */ ++#define QSLIST_EMPTY(head) ((head)->slh_first == NULL) ++#define QSLIST_FIRST(head) ((head)->slh_first) ++#define QSLIST_NEXT(elm, field) ((elm)->field.sle_next) ++ ++ ++/* ++ * Simple queue definitions. ++ */ ++#define QSIMPLEQ_HEAD(name, type) \ ++struct name { \ ++ struct type *sqh_first; /* first element */ \ ++ struct type **sqh_last; /* addr of last next element */ \ ++} ++ ++#define QSIMPLEQ_HEAD_INITIALIZER(head) \ ++ { NULL, &(head).sqh_first } ++ ++#define QSIMPLEQ_ENTRY(type) \ ++struct { \ ++ struct type *sqe_next; /* next element */ \ ++} ++ ++/* ++ * Simple queue functions. ++ */ ++#define QSIMPLEQ_INIT(head) do { \ ++ (head)->sqh_first = NULL; \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (head)->sqh_first = (elm); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.sqe_next = NULL; \ ++ *(head)->sqh_last = (elm); \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (listelm)->field.sqe_next = (elm); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_REMOVE_HEAD(head, field) do { \ ++ typeof((head)->sqh_first) elm = (head)->sqh_first; \ ++ if (((head)->sqh_first = elm->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++ elm->field.sqe_next = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_SPLIT_AFTER(head, elm, field, removed) do { \ ++ QSIMPLEQ_INIT(removed); \ ++ if (((removed)->sqh_first = (head)->sqh_first) != NULL) { \ ++ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) { \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++ } \ ++ (removed)->sqh_last = &(elm)->field.sqe_next; \ ++ (elm)->field.sqe_next = NULL; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_REMOVE(head, elm, type, field) do { \ ++ if ((head)->sqh_first == (elm)) { \ ++ QSIMPLEQ_REMOVE_HEAD((head), field); \ ++ } else { \ ++ struct type *curelm = (head)->sqh_first; \ ++ while (curelm->field.sqe_next != (elm)) \ ++ curelm = curelm->field.sqe_next; \ ++ if ((curelm->field.sqe_next = \ ++ curelm->field.sqe_next->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(curelm)->field.sqe_next; \ ++ (elm)->field.sqe_next = NULL; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_FOREACH(var, head, field) \ ++ for ((var) = ((head)->sqh_first); \ ++ (var); \ ++ (var) = ((var)->field.sqe_next)) ++ ++#define QSIMPLEQ_FOREACH_SAFE(var, head, field, next) \ ++ for ((var) = ((head)->sqh_first); \ ++ (var) && ((next = ((var)->field.sqe_next)), 1); \ ++ (var) = (next)) ++ ++#define QSIMPLEQ_CONCAT(head1, head2) do { \ ++ if (!QSIMPLEQ_EMPTY((head2))) { \ ++ *(head1)->sqh_last = (head2)->sqh_first; \ ++ (head1)->sqh_last = (head2)->sqh_last; \ ++ QSIMPLEQ_INIT((head2)); \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_PREPEND(head1, head2) do { \ ++ if (!QSIMPLEQ_EMPTY((head2))) { \ ++ *(head2)->sqh_last = (head1)->sqh_first; \ ++ (head1)->sqh_first = (head2)->sqh_first; \ ++ QSIMPLEQ_INIT((head2)); \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_LAST(head, type, field) \ ++ (QSIMPLEQ_EMPTY((head)) ? \ ++ NULL : \ ++ ((struct type *)(void *) \ ++ ((char *)((head)->sqh_last) - offsetof(struct type, field)))) ++ ++/* ++ * Simple queue access methods. ++ */ ++#define QSIMPLEQ_EMPTY_ATOMIC(head) \ ++ (qatomic_read(&((head)->sqh_first)) == NULL) ++#define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) ++#define QSIMPLEQ_FIRST(head) ((head)->sqh_first) ++#define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) ++ ++typedef struct QTailQLink { ++ void *tql_next; ++ struct QTailQLink *tql_prev; ++} QTailQLink; ++ ++/* ++ * Tail queue definitions. The union acts as a poor man template, as if ++ * it were QTailQLink. ++ */ ++#define QTAILQ_HEAD(name, type) \ ++union name { \ ++ struct type *tqh_first; /* first element */ \ ++ QTailQLink tqh_circ; /* link for circular backwards list */ \ ++} ++ ++#define QTAILQ_HEAD_INITIALIZER(head) \ ++ { .tqh_circ = { NULL, &(head).tqh_circ } } ++ ++#define QTAILQ_ENTRY(type) \ ++union { \ ++ struct type *tqe_next; /* next element */ \ ++ QTailQLink tqe_circ; /* link for circular backwards list */ \ ++} ++ ++/* ++ * Tail queue functions. ++ */ ++#define QTAILQ_INIT(head) do { \ ++ (head)->tqh_first = NULL; \ ++ (head)->tqh_circ.tql_prev = &(head)->tqh_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ ++ (head)->tqh_first->field.tqe_circ.tql_prev = \ ++ &(elm)->field.tqe_circ; \ ++ else \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++ (head)->tqh_first = (elm); \ ++ (elm)->field.tqe_circ.tql_prev = &(head)->tqh_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.tqe_next = NULL; \ ++ (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \ ++ (head)->tqh_circ.tql_prev->tql_next = (elm); \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ ++ (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ &(elm)->field.tqe_circ; \ ++ else \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++ (listelm)->field.tqe_next = (elm); \ ++ (elm)->field.tqe_circ.tql_prev = &(listelm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \ ++ (elm)->field.tqe_circ.tql_prev = (listelm)->field.tqe_circ.tql_prev; \ ++ (elm)->field.tqe_next = (listelm); \ ++ (listelm)->field.tqe_circ.tql_prev->tql_next = (elm); \ ++ (listelm)->field.tqe_circ.tql_prev = &(elm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_REMOVE(head, elm, field) do { \ ++ if (((elm)->field.tqe_next) != NULL) \ ++ (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ (elm)->field.tqe_circ.tql_prev; \ ++ else \ ++ (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ ++ (elm)->field.tqe_circ.tql_prev->tql_next = (elm)->field.tqe_next; \ ++ (elm)->field.tqe_circ.tql_prev = NULL; \ ++ (elm)->field.tqe_circ.tql_next = NULL; \ ++ (elm)->field.tqe_next = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++/* remove @left, @right and all elements in between from @head */ ++#define QTAILQ_REMOVE_SEVERAL(head, left, right, field) do { \ ++ if (((right)->field.tqe_next) != NULL) \ ++ (right)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ (left)->field.tqe_circ.tql_prev; \ ++ else \ ++ (head)->tqh_circ.tql_prev = (left)->field.tqe_circ.tql_prev; \ ++ (left)->field.tqe_circ.tql_prev->tql_next = (right)->field.tqe_next; \ ++ } while (/*CONSTCOND*/0) ++ ++#define QTAILQ_FOREACH(var, head, field) \ ++ for ((var) = ((head)->tqh_first); \ ++ (var); \ ++ (var) = ((var)->field.tqe_next)) ++ ++#define QTAILQ_FOREACH_SAFE(var, head, field, next_var) \ ++ for ((var) = ((head)->tqh_first); \ ++ (var) && ((next_var) = ((var)->field.tqe_next), 1); \ ++ (var) = (next_var)) ++ ++#define QTAILQ_FOREACH_REVERSE(var, head, field) \ ++ for ((var) = QTAILQ_LAST(head); \ ++ (var); \ ++ (var) = QTAILQ_PREV(var, field)) ++ ++#define QTAILQ_FOREACH_REVERSE_SAFE(var, head, field, prev_var) \ ++ for ((var) = QTAILQ_LAST(head); \ ++ (var) && ((prev_var) = QTAILQ_PREV(var, field), 1); \ ++ (var) = (prev_var)) ++ ++/* ++ * Tail queue access methods. ++ */ ++#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL) ++#define QTAILQ_FIRST(head) ((head)->tqh_first) ++#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next) ++#define QTAILQ_IN_USE(elm, field) ((elm)->field.tqe_circ.tql_prev != NULL) ++ ++#define QTAILQ_LINK_PREV(link) \ ++ ((link).tql_prev->tql_prev->tql_next) ++#define QTAILQ_LAST(head) \ ++ ((typeof((head)->tqh_first)) QTAILQ_LINK_PREV((head)->tqh_circ)) ++#define QTAILQ_PREV(elm, field) \ ++ ((typeof((elm)->field.tqe_next)) QTAILQ_LINK_PREV((elm)->field.tqe_circ)) ++ ++#define field_at_offset(base, offset, type) \ ++ ((type *) (((char *) (base)) + (offset))) ++ ++/* ++ * Raw access of elements of a tail queue head. Offsets are all zero ++ * because it's a union. ++ */ ++#define QTAILQ_RAW_FIRST(head) \ ++ field_at_offset(head, 0, void *) ++#define QTAILQ_RAW_TQH_CIRC(head) \ ++ field_at_offset(head, 0, QTailQLink) ++ ++/* ++ * Raw access of elements of a tail entry ++ */ ++#define QTAILQ_RAW_NEXT(elm, entry) \ ++ field_at_offset(elm, entry, void *) ++#define QTAILQ_RAW_TQE_CIRC(elm, entry) \ ++ field_at_offset(elm, entry, QTailQLink) ++/* ++ * Tail queue traversal using pointer arithmetic. ++ */ ++#define QTAILQ_RAW_FOREACH(elm, head, entry) \ ++ for ((elm) = *QTAILQ_RAW_FIRST(head); \ ++ (elm); \ ++ (elm) = *QTAILQ_RAW_NEXT(elm, entry)) ++/* ++ * Tail queue insertion using pointer arithmetic. ++ */ ++#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do { \ ++ *QTAILQ_RAW_NEXT(elm, entry) = NULL; \ ++ QTAILQ_RAW_TQE_CIRC(elm, entry)->tql_prev = QTAILQ_RAW_TQH_CIRC(head)->tql_prev; \ ++ QTAILQ_RAW_TQH_CIRC(head)->tql_prev->tql_next = (elm); \ ++ QTAILQ_RAW_TQH_CIRC(head)->tql_prev = QTAILQ_RAW_TQE_CIRC(elm, entry); \ ++} while (/*CONSTCOND*/0) ++ ++#define QLIST_RAW_FIRST(head) \ ++ field_at_offset(head, 0, void *) ++ ++#define QLIST_RAW_NEXT(elm, entry) \ ++ field_at_offset(elm, entry, void *) ++ ++#define QLIST_RAW_PREVIOUS(elm, entry) \ ++ field_at_offset(elm, entry + sizeof(void *), void *) ++ ++#define QLIST_RAW_FOREACH(elm, head, entry) \ ++ for ((elm) = *QLIST_RAW_FIRST(head); \ ++ (elm); \ ++ (elm) = *QLIST_RAW_NEXT(elm, entry)) ++ ++#define QLIST_RAW_INSERT_AFTER(head, prev, elem, entry) do { \ ++ *QLIST_RAW_NEXT(prev, entry) = elem; \ ++ *QLIST_RAW_PREVIOUS(elem, entry) = QLIST_RAW_NEXT(prev, entry); \ ++ *QLIST_RAW_NEXT(elem, entry) = NULL; \ ++} while (0) ++ ++#define QLIST_RAW_INSERT_HEAD(head, elm, entry) do { \ ++ void *first = *QLIST_RAW_FIRST(head); \ ++ *QLIST_RAW_FIRST(head) = elm; \ ++ *QLIST_RAW_PREVIOUS(elm, entry) = QLIST_RAW_FIRST(head); \ ++ if (first) { \ ++ *QLIST_RAW_NEXT(elm, entry) = first; \ ++ *QLIST_RAW_PREVIOUS(first, entry) = QLIST_RAW_NEXT(elm, entry); \ ++ } else { \ ++ *QLIST_RAW_NEXT(elm, entry) = NULL; \ ++ } \ ++} while (0) ++ ++#endif /* QEMU_SYS_QUEUE_H */ +diff --git a/include/qemu/range.h b/include/qemu/range.h +new file mode 100644 +index 00000000..d446ad88 +--- /dev/null ++++ b/include/qemu/range.h +@@ -0,0 +1,247 @@ ++/* ++ * QEMU 64-bit address ranges ++ * ++ * Copyright (c) 2015-2016 Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#ifndef QEMU_RANGE_H ++#define QEMU_RANGE_H ++ ++#include "qemu/bitops.h" ++ ++/* ++ * Operations on 64 bit address ranges. ++ * Notes: ++ * - Ranges must not wrap around 0, but can include UINT64_MAX. ++ */ ++ ++struct Range { ++ /* ++ * Do not access members directly, use the functions! ++ * A non-empty range has @lob <= @upb. ++ * An empty range has @lob == @upb + 1. ++ */ ++ uint64_t lob; /* inclusive lower bound */ ++ uint64_t upb; /* inclusive upper bound */ ++}; ++ ++static inline void range_invariant(const Range *range) ++{ ++ assert(range->lob <= range->upb || range->lob == range->upb + 1); ++} ++ ++/* Compound literal encoding the empty range */ ++#define range_empty ((Range){ .lob = 1, .upb = 0 }) ++ ++/* Is @range empty? */ ++static inline bool range_is_empty(const Range *range) ++{ ++ range_invariant(range); ++ return range->lob > range->upb; ++} ++ ++/* Does @range contain @val? */ ++static inline bool range_contains(const Range *range, uint64_t val) ++{ ++ return val >= range->lob && val <= range->upb; ++} ++ ++/* Initialize @range to the empty range */ ++static inline void range_make_empty(Range *range) ++{ ++ *range = range_empty; ++ assert(range_is_empty(range)); ++} ++ ++/* ++ * Initialize @range to span the interval [@lob,@upb]. ++ * Both bounds are inclusive. ++ * The interval must not be empty, i.e. @lob must be less than or ++ * equal @upb. ++ */ ++static inline void range_set_bounds(Range *range, uint64_t lob, uint64_t upb) ++{ ++ range->lob = lob; ++ range->upb = upb; ++ assert(!range_is_empty(range)); ++} ++ ++/* ++ * Initialize @range to span the interval [@lob,@upb_plus1). ++ * The lower bound is inclusive, the upper bound is exclusive. ++ * Zero @upb_plus1 is special: if @lob is also zero, set @range to the ++ * empty range. Else, set @range to [@lob,UINT64_MAX]. ++ */ ++static inline void range_set_bounds1(Range *range, ++ uint64_t lob, uint64_t upb_plus1) ++{ ++ if (!lob && !upb_plus1) { ++ *range = range_empty; ++ } else { ++ range->lob = lob; ++ range->upb = upb_plus1 - 1; ++ } ++ range_invariant(range); ++} ++ ++/* Return @range's lower bound. @range must not be empty. */ ++static inline uint64_t range_lob(Range *range) ++{ ++ assert(!range_is_empty(range)); ++ return range->lob; ++} ++ ++/* Return @range's upper bound. @range must not be empty. */ ++static inline uint64_t range_upb(Range *range) ++{ ++ assert(!range_is_empty(range)); ++ return range->upb; ++} ++ ++/* ++ * Initialize @range to span the interval [@lob,@lob + @size - 1]. ++ * @size may be 0. If the range would overflow, returns -ERANGE, otherwise ++ * 0. ++ */ ++G_GNUC_WARN_UNUSED_RESULT ++static inline int range_init(Range *range, uint64_t lob, uint64_t size) ++{ ++ if (lob + size < lob) { ++ return -ERANGE; ++ } ++ range->lob = lob; ++ range->upb = lob + size - 1; ++ range_invariant(range); ++ return 0; ++} ++ ++/* ++ * Initialize @range to span the interval [@lob,@lob + @size - 1]. ++ * @size may be 0. Range must not overflow. ++ */ ++static inline void range_init_nofail(Range *range, uint64_t lob, uint64_t size) ++{ ++ range->lob = lob; ++ range->upb = lob + size - 1; ++ range_invariant(range); ++} ++ ++/* ++ * Get the size of @range. ++ */ ++static inline uint64_t range_size(const Range *range) ++{ ++ return range->upb - range->lob + 1; ++} ++ ++/* ++ * Check if @range1 overlaps with @range2. If one of the ranges is empty, ++ * the result is always "false". ++ */ ++static inline bool range_overlaps_range(const Range *range1, ++ const Range *range2) ++{ ++ if (range_is_empty(range1) || range_is_empty(range2)) { ++ return false; ++ } ++ return !(range2->upb < range1->lob || range1->upb < range2->lob); ++} ++ ++/* ++ * Check if @range1 contains @range2. If one of the ranges is empty, ++ * the result is always "false". ++ */ ++static inline bool range_contains_range(const Range *range1, ++ const Range *range2) ++{ ++ if (range_is_empty(range1) || range_is_empty(range2)) { ++ return false; ++ } ++ return range1->lob <= range2->lob && range1->upb >= range2->upb; ++} ++ ++/* ++ * Extend @range to the smallest interval that includes @extend_by, too. ++ */ ++static inline void range_extend(Range *range, Range *extend_by) ++{ ++ if (range_is_empty(extend_by)) { ++ return; ++ } ++ if (range_is_empty(range)) { ++ *range = *extend_by; ++ return; ++ } ++ if (range->lob > extend_by->lob) { ++ range->lob = extend_by->lob; ++ } ++ if (range->upb < extend_by->upb) { ++ range->upb = extend_by->upb; ++ } ++ range_invariant(range); ++} ++ ++/* Get last byte of a range from offset + length. ++ * Undefined for ranges that wrap around 0. */ ++static inline uint64_t range_get_last(uint64_t offset, uint64_t len) ++{ ++ return offset + len - 1; ++} ++ ++/* Check whether a given range covers a given byte. */ ++static inline int range_covers_byte(uint64_t offset, uint64_t len, ++ uint64_t byte) ++{ ++ return offset <= byte && byte <= range_get_last(offset, len); ++} ++ ++/* Check whether 2 given ranges overlap. ++ * Undefined if ranges that wrap around 0. */ ++static inline bool ranges_overlap(uint64_t first1, uint64_t len1, ++ uint64_t first2, uint64_t len2) ++{ ++ uint64_t last1 = range_get_last(first1, len1); ++ uint64_t last2 = range_get_last(first2, len2); ++ ++ return !(last2 < first1 || last1 < first2); ++} ++ ++/* Get highest non-zero bit position of a range */ ++static inline int range_get_last_bit(Range *range) ++{ ++ if (range_is_empty(range)) { ++ return -1; ++ } ++ return 63 - clz64(range->upb); ++} ++ ++/* ++ * Return -1 if @a < @b, 1 @a > @b, and 0 if they touch or overlap. ++ * Both @a and @b must not be empty. ++ */ ++int range_compare(Range *a, Range *b); ++ ++GList *range_list_insert(GList *list, Range *data); ++ ++/* ++ * Inverse an array of sorted ranges over the [low, high] span, ie. ++ * original ranges becomes holes in the newly allocated inv_ranges ++ */ ++void range_inverse_array(GList *in_ranges, ++ GList **out_ranges, ++ uint64_t low, uint64_t high); ++ ++#endif +diff --git a/include/qemu/ratelimit.h b/include/qemu/ratelimit.h +new file mode 100644 +index 00000000..48bf59e8 +--- /dev/null ++++ b/include/qemu/ratelimit.h +@@ -0,0 +1,97 @@ ++/* ++ * Ratelimiting calculations ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Stefan Hajnoczi ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_RATELIMIT_H ++#define QEMU_RATELIMIT_H ++ ++#include "qemu/lockable.h" ++#include "qemu/timer.h" ++ ++typedef struct { ++ QemuMutex lock; ++ int64_t slice_start_time; ++ int64_t slice_end_time; ++ uint64_t slice_quota; ++ uint64_t slice_ns; ++ uint64_t dispatched; ++} RateLimit; ++ ++/** Calculate and return delay for next request in ns ++ * ++ * Record that we sent @n data units (where @n matches the scale chosen ++ * during ratelimit_set_speed). If we may send more data units ++ * in the current time slice, return 0 (i.e. no delay). Otherwise ++ * return the amount of time (in ns) until the start of the next time ++ * slice that will permit sending the next chunk of data. ++ * ++ * Recording sent data units even after exceeding the quota is ++ * permitted; the time slice will be extended accordingly. ++ */ ++static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n) ++{ ++ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ double delay_slices; ++ ++ QEMU_LOCK_GUARD(&limit->lock); ++ if (!limit->slice_quota) { ++ /* Throttling disabled. */ ++ return 0; ++ } ++ assert(limit->slice_ns); ++ ++ if (limit->slice_end_time < now) { ++ /* Previous, possibly extended, time slice finished; reset the ++ * accounting. */ ++ limit->slice_start_time = now; ++ limit->slice_end_time = now + limit->slice_ns; ++ limit->dispatched = 0; ++ } ++ ++ limit->dispatched += n; ++ if (limit->dispatched < limit->slice_quota) { ++ /* We may send further data within the current time slice, no ++ * need to delay the next request. */ ++ return 0; ++ } ++ ++ /* Quota exceeded. Wait based on the excess amount and then start a new ++ * slice. */ ++ delay_slices = (double)limit->dispatched / limit->slice_quota; ++ limit->slice_end_time = limit->slice_start_time + ++ (uint64_t)(delay_slices * limit->slice_ns); ++ return limit->slice_end_time - now; ++} ++ ++static inline void ratelimit_init(RateLimit *limit) ++{ ++ qemu_mutex_init(&limit->lock); ++} ++ ++static inline void ratelimit_destroy(RateLimit *limit) ++{ ++ qemu_mutex_destroy(&limit->lock); ++} ++ ++static inline void ratelimit_set_speed(RateLimit *limit, uint64_t speed, ++ uint64_t slice_ns) ++{ ++ QEMU_LOCK_GUARD(&limit->lock); ++ limit->slice_ns = slice_ns; ++ if (speed == 0) { ++ limit->slice_quota = 0; ++ } else { ++ limit->slice_quota = MAX(((double)speed * slice_ns) / 1000000000ULL, 1); ++ } ++} ++ ++#endif +diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h +new file mode 100644 +index 00000000..fea058aa +--- /dev/null ++++ b/include/qemu/rcu.h +@@ -0,0 +1,198 @@ ++#ifndef QEMU_RCU_H ++#define QEMU_RCU_H ++ ++/* ++ * urcu-mb.h ++ * ++ * Userspace RCU header with explicit memory barrier. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * IBM's contributions to this file may be relicensed under LGPLv2 or later. ++ */ ++ ++ ++#include "qemu/thread.h" ++#include "qemu/queue.h" ++#include "qemu/atomic.h" ++#include "qemu/notify.h" ++#include "qemu/sys_membarrier.h" ++#include "qemu/coroutine-tls.h" ++ ++/* ++ * Important ! ++ * ++ * Each thread containing read-side critical sections must be registered ++ * with rcu_register_thread() before calling rcu_read_lock(). ++ * rcu_unregister_thread() should be called before the thread exits. ++ */ ++ ++#ifdef DEBUG_RCU ++#define rcu_assert(args...) assert(args) ++#else ++#define rcu_assert(args...) ++#endif ++ ++/* ++ * Global quiescent period counter with low-order bits unused. ++ * Using a int rather than a char to eliminate false register dependencies ++ * causing stalls on some architectures. ++ */ ++extern unsigned long rcu_gp_ctr; ++ ++extern QemuEvent rcu_gp_event; ++ ++struct rcu_reader_data { ++ /* Data used by both reader and synchronize_rcu() */ ++ unsigned long ctr; ++ bool waiting; ++ ++ /* Data used by reader only */ ++ unsigned depth; ++ ++ /* Data used for registry, protected by rcu_registry_lock */ ++ QLIST_ENTRY(rcu_reader_data) node; ++ ++ /* ++ * NotifierList used to force an RCU grace period. Accessed under ++ * rcu_registry_lock. Note that the notifier is called _outside_ ++ * the thread! ++ */ ++ NotifierList force_rcu; ++}; ++ ++QEMU_DECLARE_CO_TLS(struct rcu_reader_data, rcu_reader) ++ ++static inline void rcu_read_lock(void) ++{ ++ struct rcu_reader_data *p_rcu_reader = get_ptr_rcu_reader(); ++ unsigned ctr; ++ ++ if (p_rcu_reader->depth++ > 0) { ++ return; ++ } ++ ++ ctr = qatomic_read(&rcu_gp_ctr); ++ qatomic_set(&p_rcu_reader->ctr, ctr); ++ ++ /* ++ * Read rcu_gp_ptr and write p_rcu_reader->ctr before reading ++ * RCU-protected pointers. ++ */ ++ smp_mb_placeholder(); ++} ++ ++static inline void rcu_read_unlock(void) ++{ ++ struct rcu_reader_data *p_rcu_reader = get_ptr_rcu_reader(); ++ ++ assert(p_rcu_reader->depth != 0); ++ if (--p_rcu_reader->depth > 0) { ++ return; ++ } ++ ++ /* Ensure that the critical section is seen to precede the ++ * store to p_rcu_reader->ctr. Together with the following ++ * smp_mb_placeholder(), this ensures writes to p_rcu_reader->ctr ++ * are sequentially consistent. ++ */ ++ qatomic_store_release(&p_rcu_reader->ctr, 0); ++ ++ /* Write p_rcu_reader->ctr before reading p_rcu_reader->waiting. */ ++ smp_mb_placeholder(); ++ if (unlikely(qatomic_read(&p_rcu_reader->waiting))) { ++ qatomic_set(&p_rcu_reader->waiting, false); ++ qemu_event_set(&rcu_gp_event); ++ } ++} ++ ++void synchronize_rcu(void); ++ ++/* ++ * Reader thread registration. ++ */ ++void rcu_register_thread(void); ++void rcu_unregister_thread(void); ++ ++/* ++ * Support for fork(). fork() support is enabled at startup. ++ */ ++void rcu_enable_atfork(void); ++void rcu_disable_atfork(void); ++ ++struct rcu_head; ++typedef void RCUCBFunc(struct rcu_head *head); ++ ++struct rcu_head { ++ struct rcu_head *next; ++ RCUCBFunc *func; ++}; ++ ++void call_rcu1(struct rcu_head *head, RCUCBFunc *func); ++void drain_call_rcu(void); ++ ++/* The operands of the minus operator must have the same type, ++ * which must be the one that we specify in the cast. ++ */ ++#define call_rcu(head, func, field) \ ++ call_rcu1(({ \ ++ char __attribute__((unused)) \ ++ offset_must_be_zero[-offsetof(typeof(*(head)), field)], \ ++ func_type_invalid = (func) - (void (*)(typeof(head)))(func); \ ++ &(head)->field; \ ++ }), \ ++ (RCUCBFunc *)(func)) ++ ++#define g_free_rcu(obj, field) \ ++ call_rcu1(({ \ ++ char __attribute__((unused)) \ ++ offset_must_be_zero[-offsetof(typeof(*(obj)), field)]; \ ++ &(obj)->field; \ ++ }), \ ++ (RCUCBFunc *)g_free); ++ ++typedef void RCUReadAuto; ++static inline RCUReadAuto *rcu_read_auto_lock(void) ++{ ++ rcu_read_lock(); ++ /* Anything non-NULL causes the cleanup function to be called */ ++ return (void *)(uintptr_t)0x1; ++} ++ ++static inline void rcu_read_auto_unlock(RCUReadAuto *r) ++{ ++ rcu_read_unlock(); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(RCUReadAuto, rcu_read_auto_unlock) ++ ++#define WITH_RCU_READ_LOCK_GUARD() \ ++ WITH_RCU_READ_LOCK_GUARD_(glue(_rcu_read_auto, __COUNTER__)) ++ ++#define WITH_RCU_READ_LOCK_GUARD_(var) \ ++ for (g_autoptr(RCUReadAuto) var = rcu_read_auto_lock(); \ ++ (var); rcu_read_auto_unlock(var), (var) = NULL) ++ ++#define RCU_READ_LOCK_GUARD() \ ++ g_autoptr(RCUReadAuto) _rcu_read_auto __attribute__((unused)) = rcu_read_auto_lock() ++ ++/* ++ * Force-RCU notifiers tell readers that they should exit their ++ * read-side critical section. ++ */ ++void rcu_add_force_rcu_notifier(Notifier *n); ++void rcu_remove_force_rcu_notifier(Notifier *n); ++ ++#endif /* QEMU_RCU_H */ +diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h +new file mode 100644 +index 00000000..4e6298d4 +--- /dev/null ++++ b/include/qemu/rcu_queue.h +@@ -0,0 +1,309 @@ ++#ifndef QEMU_RCU_QUEUE_H ++#define QEMU_RCU_QUEUE_H ++ ++/* ++ * rcu_queue.h ++ * ++ * RCU-friendly versions of the queue.h primitives. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * Copyright (c) 2013 Mike D. Day, IBM Corporation. ++ * ++ * IBM's contributions to this file may be relicensed under LGPLv2 or later. ++ */ ++ ++#include "qemu/queue.h" ++#include "qemu/atomic.h" ++ ++/* ++ * List access methods. ++ */ ++#define QLIST_EMPTY_RCU(head) (qatomic_read(&(head)->lh_first) == NULL) ++#define QLIST_FIRST_RCU(head) (qatomic_rcu_read(&(head)->lh_first)) ++#define QLIST_NEXT_RCU(elm, field) (qatomic_rcu_read(&(elm)->field.le_next)) ++ ++/* ++ * List functions. ++ */ ++ ++ ++/* ++ * The difference between qatomic_read/set and qatomic_rcu_read/set ++ * is in the including of a read/write memory barrier to the volatile ++ * access. atomic_rcu_* macros include the memory barrier, the ++ * plain atomic macros do not. Therefore, it should be correct to ++ * issue a series of reads or writes to the same element using only ++ * the atomic_* macro, until the last read or write, which should be ++ * atomic_rcu_* to introduce a read or write memory barrier as ++ * appropriate. ++ */ ++ ++/* Upon publication of the listelm->next value, list readers ++ * will see the new node when following next pointers from ++ * antecedent nodes, but may not see the new node when following ++ * prev pointers from subsequent nodes until after the RCU grace ++ * period expires. ++ * see linux/include/rculist.h __list_add_rcu(new, prev, next) ++ */ ++#define QLIST_INSERT_AFTER_RCU(listelm, elm, field) do { \ ++ (elm)->field.le_next = (listelm)->field.le_next; \ ++ (elm)->field.le_prev = &(listelm)->field.le_next; \ ++ qatomic_rcu_set(&(listelm)->field.le_next, (elm)); \ ++ if ((elm)->field.le_next != NULL) { \ ++ (elm)->field.le_next->field.le_prev = \ ++ &(elm)->field.le_next; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++/* Upon publication of the listelm->prev->next value, list ++ * readers will see the new element when following prev pointers ++ * from subsequent elements, but may not see the new element ++ * when following next pointers from antecedent elements ++ * until after the RCU grace period expires. ++ */ ++#define QLIST_INSERT_BEFORE_RCU(listelm, elm, field) do { \ ++ (elm)->field.le_prev = (listelm)->field.le_prev; \ ++ (elm)->field.le_next = (listelm); \ ++ qatomic_rcu_set((listelm)->field.le_prev, (elm)); \ ++ (listelm)->field.le_prev = &(elm)->field.le_next; \ ++} while (/*CONSTCOND*/0) ++ ++/* Upon publication of the head->first value, list readers ++ * will see the new element when following the head, but may ++ * not see the new element when following prev pointers from ++ * subsequent elements until after the RCU grace period has ++ * expired. ++ */ ++#define QLIST_INSERT_HEAD_RCU(head, elm, field) do { \ ++ (elm)->field.le_prev = &(head)->lh_first; \ ++ (elm)->field.le_next = (head)->lh_first; \ ++ qatomic_rcu_set((&(head)->lh_first), (elm)); \ ++ if ((elm)->field.le_next != NULL) { \ ++ (elm)->field.le_next->field.le_prev = \ ++ &(elm)->field.le_next; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++ ++/* prior to publication of the elm->prev->next value, some list ++ * readers may still see the removed element when following ++ * the antecedent's next pointer. ++ */ ++#define QLIST_REMOVE_RCU(elm, field) do { \ ++ if ((elm)->field.le_next != NULL) { \ ++ (elm)->field.le_next->field.le_prev = \ ++ (elm)->field.le_prev; \ ++ } \ ++ qatomic_set((elm)->field.le_prev, (elm)->field.le_next); \ ++} while (/*CONSTCOND*/0) ++ ++/* List traversal must occur within an RCU critical section. */ ++#define QLIST_FOREACH_RCU(var, head, field) \ ++ for ((var) = qatomic_rcu_read(&(head)->lh_first); \ ++ (var); \ ++ (var) = qatomic_rcu_read(&(var)->field.le_next)) ++ ++/* List traversal must occur within an RCU critical section. */ ++#define QLIST_FOREACH_SAFE_RCU(var, head, field, next_var) \ ++ for ((var) = (qatomic_rcu_read(&(head)->lh_first)); \ ++ (var) && \ ++ ((next_var) = qatomic_rcu_read(&(var)->field.le_next), 1); \ ++ (var) = (next_var)) ++ ++/* ++ * RCU simple queue ++ */ ++ ++/* Simple queue access methods */ ++#define QSIMPLEQ_EMPTY_RCU(head) \ ++ (qatomic_read(&(head)->sqh_first) == NULL) ++#define QSIMPLEQ_FIRST_RCU(head) qatomic_rcu_read(&(head)->sqh_first) ++#define QSIMPLEQ_NEXT_RCU(elm, field) qatomic_rcu_read(&(elm)->field.sqe_next) ++ ++/* Simple queue functions */ ++#define QSIMPLEQ_INSERT_HEAD_RCU(head, elm, field) do { \ ++ (elm)->field.sqe_next = (head)->sqh_first; \ ++ if ((elm)->field.sqe_next == NULL) { \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ } \ ++ qatomic_rcu_set(&(head)->sqh_first, (elm)); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_TAIL_RCU(head, elm, field) do { \ ++ (elm)->field.sqe_next = NULL; \ ++ qatomic_rcu_set((head)->sqh_last, (elm)); \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ ++ (elm)->field.sqe_next = (listelm)->field.sqe_next; \ ++ if ((elm)->field.sqe_next == NULL) { \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ } \ ++ qatomic_rcu_set(&(listelm)->field.sqe_next, (elm)); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_REMOVE_HEAD_RCU(head, field) do { \ ++ qatomic_set(&(head)->sqh_first, (head)->sqh_first->field.sqe_next);\ ++ if ((head)->sqh_first == NULL) { \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_REMOVE_RCU(head, elm, type, field) do { \ ++ if ((head)->sqh_first == (elm)) { \ ++ QSIMPLEQ_REMOVE_HEAD_RCU((head), field); \ ++ } else { \ ++ struct type *curr = (head)->sqh_first; \ ++ while (curr->field.sqe_next != (elm)) { \ ++ curr = curr->field.sqe_next; \ ++ } \ ++ qatomic_set(&curr->field.sqe_next, \ ++ curr->field.sqe_next->field.sqe_next); \ ++ if (curr->field.sqe_next == NULL) { \ ++ (head)->sqh_last = &(curr)->field.sqe_next; \ ++ } \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSIMPLEQ_FOREACH_RCU(var, head, field) \ ++ for ((var) = qatomic_rcu_read(&(head)->sqh_first); \ ++ (var); \ ++ (var) = qatomic_rcu_read(&(var)->field.sqe_next)) ++ ++#define QSIMPLEQ_FOREACH_SAFE_RCU(var, head, field, next) \ ++ for ((var) = qatomic_rcu_read(&(head)->sqh_first); \ ++ (var) && ((next) = qatomic_rcu_read(&(var)->field.sqe_next), 1);\ ++ (var) = (next)) ++ ++/* ++ * RCU tail queue ++ */ ++ ++/* Tail queue access methods */ ++#define QTAILQ_EMPTY_RCU(head) (qatomic_read(&(head)->tqh_first) == NULL) ++#define QTAILQ_FIRST_RCU(head) qatomic_rcu_read(&(head)->tqh_first) ++#define QTAILQ_NEXT_RCU(elm, field) qatomic_rcu_read(&(elm)->field.tqe_next) ++ ++/* Tail queue functions */ ++#define QTAILQ_INSERT_HEAD_RCU(head, elm, field) do { \ ++ (elm)->field.tqe_next = (head)->tqh_first; \ ++ if ((elm)->field.tqe_next != NULL) { \ ++ (head)->tqh_first->field.tqe_circ.tql_prev = \ ++ &(elm)->field.tqe_circ; \ ++ } else { \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++ } \ ++ qatomic_rcu_set(&(head)->tqh_first, (elm)); \ ++ (elm)->field.tqe_circ.tql_prev = &(head)->tqh_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_TAIL_RCU(head, elm, field) do { \ ++ (elm)->field.tqe_next = NULL; \ ++ (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \ ++ qatomic_rcu_set(&(head)->tqh_circ.tql_prev->tql_next, (elm)); \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ ++ (elm)->field.tqe_next = (listelm)->field.tqe_next; \ ++ if ((elm)->field.tqe_next != NULL) { \ ++ (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ &(elm)->field.tqe_circ; \ ++ } else { \ ++ (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ ++ } \ ++ qatomic_rcu_set(&(listelm)->field.tqe_next, (elm)); \ ++ (elm)->field.tqe_circ.tql_prev = &(listelm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_INSERT_BEFORE_RCU(listelm, elm, field) do { \ ++ (elm)->field.tqe_circ.tql_prev = (listelm)->field.tqe_circ.tql_prev; \ ++ (elm)->field.tqe_next = (listelm); \ ++ qatomic_rcu_set(&(listelm)->field.tqe_circ.tql_prev->tql_next, (elm));\ ++ (listelm)->field.tqe_circ.tql_prev = &(elm)->field.tqe_circ; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_REMOVE_RCU(head, elm, field) do { \ ++ if (((elm)->field.tqe_next) != NULL) { \ ++ (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ ++ (elm)->field.tqe_circ.tql_prev; \ ++ } else { \ ++ (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ ++ } \ ++ qatomic_set(&(elm)->field.tqe_circ.tql_prev->tql_next, \ ++ (elm)->field.tqe_next); \ ++ (elm)->field.tqe_circ.tql_prev = NULL; \ ++} while (/*CONSTCOND*/0) ++ ++#define QTAILQ_FOREACH_RCU(var, head, field) \ ++ for ((var) = qatomic_rcu_read(&(head)->tqh_first); \ ++ (var); \ ++ (var) = qatomic_rcu_read(&(var)->field.tqe_next)) ++ ++#define QTAILQ_FOREACH_SAFE_RCU(var, head, field, next) \ ++ for ((var) = qatomic_rcu_read(&(head)->tqh_first); \ ++ (var) && ((next) = qatomic_rcu_read(&(var)->field.tqe_next), 1);\ ++ (var) = (next)) ++ ++/* ++ * RCU singly-linked list ++ */ ++ ++/* Singly-linked list access methods */ ++#define QSLIST_EMPTY_RCU(head) (qatomic_read(&(head)->slh_first) == NULL) ++#define QSLIST_FIRST_RCU(head) qatomic_rcu_read(&(head)->slh_first) ++#define QSLIST_NEXT_RCU(elm, field) qatomic_rcu_read(&(elm)->field.sle_next) ++ ++/* Singly-linked list functions */ ++#define QSLIST_INSERT_HEAD_RCU(head, elm, field) do { \ ++ (elm)->field.sle_next = (head)->slh_first; \ ++ qatomic_rcu_set(&(head)->slh_first, (elm)); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ ++ (elm)->field.sle_next = (listelm)->field.sle_next; \ ++ qatomic_rcu_set(&(listelm)->field.sle_next, (elm)); \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE_HEAD_RCU(head, field) do { \ ++ qatomic_set(&(head)->slh_first, (head)->slh_first->field.sle_next);\ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_REMOVE_RCU(head, elm, type, field) do { \ ++ if ((head)->slh_first == (elm)) { \ ++ QSLIST_REMOVE_HEAD_RCU((head), field); \ ++ } else { \ ++ struct type *curr = (head)->slh_first; \ ++ while (curr->field.sle_next != (elm)) { \ ++ curr = curr->field.sle_next; \ ++ } \ ++ qatomic_set(&curr->field.sle_next, \ ++ curr->field.sle_next->field.sle_next); \ ++ } \ ++} while (/*CONSTCOND*/0) ++ ++#define QSLIST_FOREACH_RCU(var, head, field) \ ++ for ((var) = qatomic_rcu_read(&(head)->slh_first); \ ++ (var); \ ++ (var) = qatomic_rcu_read(&(var)->field.sle_next)) ++ ++#define QSLIST_FOREACH_SAFE_RCU(var, head, field, next) \ ++ for ((var) = qatomic_rcu_read(&(head)->slh_first); \ ++ (var) && ((next) = qatomic_rcu_read(&(var)->field.sle_next), 1); \ ++ (var) = (next)) ++ ++#endif /* QEMU_RCU_QUEUE_H */ +diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h +new file mode 100644 +index 00000000..d935fd80 +--- /dev/null ++++ b/include/qemu/sockets.h +@@ -0,0 +1,156 @@ ++/* headers to use the BSD sockets */ ++ ++#ifndef QEMU_SOCKETS_H ++#define QEMU_SOCKETS_H ++ ++#ifdef _WIN32 ++ ++int inet_aton(const char *cp, struct in_addr *ia); ++ ++#endif /* !_WIN32 */ ++ ++#include "qapi/qapi-types-sockets.h" ++ ++/* misc helpers */ ++bool fd_is_socket(int fd); ++int qemu_socket(int domain, int type, int protocol); ++ ++/** ++ * qemu_socketpair: ++ * @domain: specifies a communication domain, such as PF_UNIX ++ * @type: specifies the socket type. ++ * @protocol: specifies a particular protocol to be used with the socket ++ * @sv: an array to store the pair of socket created ++ * ++ * Creates an unnamed pair of connected sockets in the specified domain, ++ * of the specified type, and using the optionally specified protocol. ++ * And automatically set the close-on-exec flags on the returned sockets ++ * ++ * Return 0 on success. ++ */ ++int qemu_socketpair(int domain, int type, int protocol, int sv[2]); ++ ++int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen); ++/* ++ * A variant of send(2) which handles partial send. ++ * ++ * Return the number of bytes transferred over the socket. ++ * Set errno if fewer than `count' bytes are sent. ++ * ++ * This function don't work with non-blocking socket's. ++ * Any of the possibilities with non-blocking socket's is bad: ++ * - return a short write (then name is wrong) ++ * - busy wait adding (errno == EAGAIN) to the loop ++ */ ++ssize_t qemu_send_full(int s, const void *buf, size_t count) ++ G_GNUC_WARN_UNUSED_RESULT; ++int socket_set_cork(int fd, int v); ++int socket_set_nodelay(int fd); ++void qemu_socket_set_block(int fd); ++int qemu_socket_try_set_nonblock(int fd); ++void qemu_socket_set_nonblock(int fd); ++int socket_set_fast_reuse(int fd); ++ ++#ifdef WIN32 ++/* Windows has different names for the same constants with the same values */ ++#define SHUT_RD 0 ++#define SHUT_WR 1 ++#define SHUT_RDWR 2 ++#endif ++ ++int inet_ai_family_from_address(InetSocketAddress *addr, ++ Error **errp); ++int inet_parse(InetSocketAddress *addr, const char *str, Error **errp); ++int inet_connect(const char *str, Error **errp); ++int inet_connect_saddr(InetSocketAddress *saddr, Error **errp); ++ ++NetworkAddressFamily inet_netfamily(int family); ++ ++int unix_listen(const char *path, Error **errp); ++int unix_connect(const char *path, Error **errp); ++ ++char *socket_uri(SocketAddress *addr); ++SocketAddress *socket_parse(const char *str, Error **errp); ++int socket_connect(SocketAddress *addr, Error **errp); ++int socket_listen(SocketAddress *addr, int num, Error **errp); ++void socket_listen_cleanup(int fd, Error **errp); ++int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp); ++ ++/* Old, ipv4 only bits. Don't use for new code. */ ++int convert_host_port(struct sockaddr_in *saddr, const char *host, ++ const char *port, Error **errp); ++int parse_host_port(struct sockaddr_in *saddr, const char *str, ++ Error **errp); ++int socket_init(void); ++ ++/** ++ * socket_sockaddr_to_address: ++ * @sa: socket address struct ++ * @salen: size of @sa struct ++ * @errp: pointer to uninitialized error object ++ * ++ * Get the string representation of the socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: the socket address struct, or NULL on error ++ */ ++SocketAddress * ++socket_sockaddr_to_address(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp); ++ ++/** ++ * socket_local_address: ++ * @fd: the socket file handle ++ * @errp: pointer to uninitialized error object ++ * ++ * Get the string representation of the local socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: the socket address struct, or NULL on error ++ */ ++SocketAddress *socket_local_address(int fd, Error **errp); ++ ++/** ++ * socket_remote_address: ++ * @fd: the socket file handle ++ * @errp: pointer to uninitialized error object ++ * ++ * Get the string representation of the remote socket ++ * address. A pointer to the allocated address information ++ * struct will be returned, which the caller is required to ++ * release with a call qapi_free_SocketAddress() when no ++ * longer required. ++ * ++ * Returns: the socket address struct, or NULL on error ++ */ ++SocketAddress *socket_remote_address(int fd, Error **errp); ++ ++/** ++ * socket_address_flatten: ++ * @addr: the socket address to flatten ++ * ++ * Convert SocketAddressLegacy to SocketAddress. Caller is responsible ++ * for freeing with qapi_free_SocketAddress(). ++ * ++ * Returns: the argument converted to SocketAddress. ++ */ ++SocketAddress *socket_address_flatten(SocketAddressLegacy *addr); ++ ++/** ++ * socket_address_parse_named_fd: ++ * ++ * Modify @addr, replacing a named fd by its corresponding number. ++ * Needed for callers that plan to pass @addr to a context where the ++ * current monitor is not available. ++ * ++ * Return 0 on success. ++ */ ++int socket_address_parse_named_fd(SocketAddress *addr, Error **errp); ++#endif /* QEMU_SOCKETS_H */ +diff --git a/include/qemu/stats64.h b/include/qemu/stats64.h +new file mode 100644 +index 00000000..99b5cb72 +--- /dev/null ++++ b/include/qemu/stats64.h +@@ -0,0 +1,199 @@ ++/* ++ * Atomic operations on 64-bit quantities. ++ * ++ * Copyright (C) 2017 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_STATS64_H ++#define QEMU_STATS64_H ++ ++#include "qemu/atomic.h" ++ ++/* This provides atomic operations on 64-bit type, using a reader-writer ++ * spinlock on architectures that do not have 64-bit accesses. Even on ++ * those architectures, it tries hard not to take the lock. ++ */ ++ ++typedef struct Stat64 { ++#ifdef CONFIG_ATOMIC64 ++ aligned_uint64_t value; ++#else ++ uint32_t low, high; ++ uint32_t lock; ++#endif ++} Stat64; ++ ++#ifdef CONFIG_ATOMIC64 ++static inline void stat64_init(Stat64 *s, uint64_t value) ++{ ++ /* This is not guaranteed to be atomic! */ ++ *s = (Stat64) { value }; ++} ++ ++static inline uint64_t stat64_get(const Stat64 *s) ++{ ++ return qatomic_read__nocheck(&s->value); ++} ++ ++static inline void stat64_set(Stat64 *s, uint64_t value) ++{ ++ qatomic_set__nocheck(&s->value, value); ++} ++ ++static inline void stat64_add(Stat64 *s, uint64_t value) ++{ ++ qatomic_add(&s->value, value); ++} ++ ++static inline void stat64_min(Stat64 *s, uint64_t value) ++{ ++ uint64_t orig = qatomic_read__nocheck(&s->value); ++ while (orig > value) { ++ orig = qatomic_cmpxchg__nocheck(&s->value, orig, value); ++ } ++} ++ ++static inline void stat64_max(Stat64 *s, uint64_t value) ++{ ++ uint64_t orig = qatomic_read__nocheck(&s->value); ++ while (orig < value) { ++ orig = qatomic_cmpxchg__nocheck(&s->value, orig, value); ++ } ++} ++#else ++uint64_t stat64_get(const Stat64 *s); ++void stat64_set(Stat64 *s, uint64_t value); ++bool stat64_min_slow(Stat64 *s, uint64_t value); ++bool stat64_max_slow(Stat64 *s, uint64_t value); ++bool stat64_add32_carry(Stat64 *s, uint32_t low, uint32_t high); ++ ++static inline void stat64_init(Stat64 *s, uint64_t value) ++{ ++ /* This is not guaranteed to be atomic! */ ++ *s = (Stat64) { .low = value, .high = value >> 32, .lock = 0 }; ++} ++ ++static inline void stat64_add(Stat64 *s, uint64_t value) ++{ ++ uint32_t low, high; ++ high = value >> 32; ++ low = (uint32_t) value; ++ if (!low) { ++ if (high) { ++ qatomic_add(&s->high, high); ++ } ++ return; ++ } ++ ++ for (;;) { ++ uint32_t orig = s->low; ++ uint32_t result = orig + low; ++ uint32_t old; ++ ++ if (result < low || high) { ++ /* If the high part is affected, take the lock. */ ++ if (stat64_add32_carry(s, low, high)) { ++ return; ++ } ++ continue; ++ } ++ ++ /* No carry, try with a 32-bit cmpxchg. The result is independent of ++ * the high 32 bits, so it can race just fine with stat64_add32_carry ++ * and even stat64_get! ++ */ ++ old = qatomic_cmpxchg(&s->low, orig, result); ++ if (orig == old) { ++ return; ++ } ++ } ++} ++ ++static inline void stat64_min(Stat64 *s, uint64_t value) ++{ ++ uint32_t low, high; ++ uint32_t orig_low, orig_high; ++ ++ high = value >> 32; ++ low = (uint32_t) value; ++ do { ++ orig_high = qatomic_read(&s->high); ++ if (orig_high < high) { ++ return; ++ } ++ ++ if (orig_high == high) { ++ /* High 32 bits are equal. Read low after high, otherwise we ++ * can get a false positive (e.g. 0x1235,0x0000 changes to ++ * 0x1234,0x8000 and we read it as 0x1234,0x0000). Pairs with ++ * the write barrier in stat64_min_slow. ++ */ ++ smp_rmb(); ++ orig_low = qatomic_read(&s->low); ++ if (orig_low <= low) { ++ return; ++ } ++ ++ /* See if we were lucky and a writer raced against us. The ++ * barrier is theoretically unnecessary, but if we remove it ++ * we may miss being lucky. ++ */ ++ smp_rmb(); ++ orig_high = qatomic_read(&s->high); ++ if (orig_high < high) { ++ return; ++ } ++ } ++ ++ /* If the value changes in any way, we have to take the lock. */ ++ } while (!stat64_min_slow(s, value)); ++} ++ ++static inline void stat64_max(Stat64 *s, uint64_t value) ++{ ++ uint32_t low, high; ++ uint32_t orig_low, orig_high; ++ ++ high = value >> 32; ++ low = (uint32_t) value; ++ do { ++ orig_high = qatomic_read(&s->high); ++ if (orig_high > high) { ++ return; ++ } ++ ++ if (orig_high == high) { ++ /* High 32 bits are equal. Read low after high, otherwise we ++ * can get a false positive (e.g. 0x1234,0x8000 changes to ++ * 0x1235,0x0000 and we read it as 0x1235,0x8000). Pairs with ++ * the write barrier in stat64_max_slow. ++ */ ++ smp_rmb(); ++ orig_low = qatomic_read(&s->low); ++ if (orig_low >= low) { ++ return; ++ } ++ ++ /* See if we were lucky and a writer raced against us. The ++ * barrier is theoretically unnecessary, but if we remove it ++ * we may miss being lucky. ++ */ ++ smp_rmb(); ++ orig_high = qatomic_read(&s->high); ++ if (orig_high > high) { ++ return; ++ } ++ } ++ ++ /* If the value changes in any way, we have to take the lock. */ ++ } while (!stat64_max_slow(s, value)); ++} ++ ++#endif ++ ++#endif +diff --git a/include/qemu/sys_membarrier.h b/include/qemu/sys_membarrier.h +new file mode 100644 +index 00000000..e7774891 +--- /dev/null ++++ b/include/qemu/sys_membarrier.h +@@ -0,0 +1,27 @@ ++/* ++ * Process-global memory barriers ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ */ ++ ++#ifndef QEMU_SYS_MEMBARRIER_H ++#define QEMU_SYS_MEMBARRIER_H ++ ++#ifdef CONFIG_MEMBARRIER ++/* Only block reordering at the compiler level in the performance-critical ++ * side. The slow side forces processor-level ordering on all other cores ++ * through a system call. ++ */ ++void smp_mb_global_init(void); ++void smp_mb_global(void); ++#define smp_mb_placeholder() barrier() ++#else ++/* Keep it simple, execute a real memory barrier on both sides. */ ++static inline void smp_mb_global_init(void) {} ++#define smp_mb_global() smp_mb() ++#define smp_mb_placeholder() smp_mb() ++#endif ++ ++#endif +diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h +new file mode 100644 +index 00000000..5f2f3d13 +--- /dev/null ++++ b/include/qemu/thread-posix.h +@@ -0,0 +1,48 @@ ++#ifndef QEMU_THREAD_POSIX_H ++#define QEMU_THREAD_POSIX_H ++ ++#include ++#include ++ ++struct QemuMutex { ++ pthread_mutex_t lock; ++#ifdef CONFIG_DEBUG_MUTEX ++ const char *file; ++ int line; ++#endif ++ bool initialized; ++}; ++ ++/* ++ * QemuRecMutex cannot be a typedef of QemuMutex lest we have two ++ * compatible cases in _Generic. See qemu/lockable.h. ++ */ ++typedef struct QemuRecMutex { ++ QemuMutex m; ++} QemuRecMutex; ++ ++struct QemuCond { ++ pthread_cond_t cond; ++ bool initialized; ++}; ++ ++struct QemuSemaphore { ++ QemuMutex mutex; ++ QemuCond cond; ++ unsigned int count; ++}; ++ ++struct QemuEvent { ++#ifndef __linux__ ++ pthread_mutex_t lock; ++ pthread_cond_t cond; ++#endif ++ unsigned value; ++ bool initialized; ++}; ++ ++struct QemuThread { ++ pthread_t thread; ++}; ++ ++#endif +diff --git a/include/qemu/thread.h b/include/qemu/thread.h +new file mode 100644 +index 00000000..fb74e21c +--- /dev/null ++++ b/include/qemu/thread.h +@@ -0,0 +1,407 @@ ++#ifndef QEMU_THREAD_H ++#define QEMU_THREAD_H ++ ++#include "qemu/processor.h" ++#include "qemu/atomic.h" ++#include "qemu/clang-tsa.h" ++ ++typedef struct QemuCond QemuCond; ++typedef struct QemuSemaphore QemuSemaphore; ++typedef struct QemuEvent QemuEvent; ++typedef struct QemuLockCnt QemuLockCnt; ++typedef struct QemuThread QemuThread; ++ ++#ifdef _WIN32 ++#include "qemu/thread-win32.h" ++#else ++#include "qemu/thread-posix.h" ++#endif ++ ++/* include QSP header once QemuMutex, QemuCond etc. are defined */ ++#include "qemu/qsp.h" ++ ++#define QEMU_THREAD_JOINABLE 0 ++#define QEMU_THREAD_DETACHED 1 ++ ++void qemu_mutex_init(QemuMutex *mutex); ++void qemu_mutex_destroy(QemuMutex *mutex); ++int TSA_NO_TSA qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, ++ const int line); ++void TSA_NO_TSA qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, ++ const int line); ++void TSA_NO_TSA qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, ++ const int line); ++ ++void qemu_rec_mutex_init(QemuRecMutex *mutex); ++void qemu_rec_mutex_destroy(QemuRecMutex *mutex); ++void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line); ++int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line); ++void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line); ++ ++typedef void (*QemuMutexLockFunc)(QemuMutex *m, const char *f, int l); ++typedef int (*QemuMutexTrylockFunc)(QemuMutex *m, const char *f, int l); ++typedef void (*QemuRecMutexLockFunc)(QemuRecMutex *m, const char *f, int l); ++typedef int (*QemuRecMutexTrylockFunc)(QemuRecMutex *m, const char *f, int l); ++typedef void (*QemuCondWaitFunc)(QemuCond *c, QemuMutex *m, const char *f, ++ int l); ++typedef bool (*QemuCondTimedWaitFunc)(QemuCond *c, QemuMutex *m, int ms, ++ const char *f, int l); ++ ++extern QemuMutexLockFunc bql_mutex_lock_func; ++extern QemuMutexLockFunc qemu_mutex_lock_func; ++extern QemuMutexTrylockFunc qemu_mutex_trylock_func; ++extern QemuRecMutexLockFunc qemu_rec_mutex_lock_func; ++extern QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func; ++extern QemuCondWaitFunc qemu_cond_wait_func; ++extern QemuCondTimedWaitFunc qemu_cond_timedwait_func; ++ ++/* convenience macros to bypass the profiler */ ++#define qemu_mutex_lock__raw(m) \ ++ qemu_mutex_lock_impl(m, __FILE__, __LINE__) ++#define qemu_mutex_trylock__raw(m) \ ++ qemu_mutex_trylock_impl(m, __FILE__, __LINE__) ++ ++#ifdef __COVERITY__ ++/* ++ * Coverity is severely confused by the indirect function calls, ++ * hide them. ++ */ ++#define qemu_mutex_lock(m) \ ++ qemu_mutex_lock_impl(m, __FILE__, __LINE__) ++#define qemu_mutex_trylock(m) \ ++ qemu_mutex_trylock_impl(m, __FILE__, __LINE__) ++#define qemu_rec_mutex_lock(m) \ ++ qemu_rec_mutex_lock_impl(m, __FILE__, __LINE__) ++#define qemu_rec_mutex_trylock(m) \ ++ qemu_rec_mutex_trylock_impl(m, __FILE__, __LINE__) ++#define qemu_cond_wait(c, m) \ ++ qemu_cond_wait_impl(c, m, __FILE__, __LINE__) ++#define qemu_cond_timedwait(c, m, ms) \ ++ qemu_cond_timedwait_impl(c, m, ms, __FILE__, __LINE__) ++#else ++#define qemu_mutex_lock(m) ({ \ ++ QemuMutexLockFunc _f = qatomic_read(&qemu_mutex_lock_func); \ ++ _f(m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_mutex_trylock(m) ({ \ ++ QemuMutexTrylockFunc _f = qatomic_read(&qemu_mutex_trylock_func); \ ++ _f(m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_rec_mutex_lock(m) ({ \ ++ QemuRecMutexLockFunc _f = qatomic_read(&qemu_rec_mutex_lock_func);\ ++ _f(m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_rec_mutex_trylock(m) ({ \ ++ QemuRecMutexTrylockFunc _f; \ ++ _f = qatomic_read(&qemu_rec_mutex_trylock_func); \ ++ _f(m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_cond_wait(c, m) ({ \ ++ QemuCondWaitFunc _f = qatomic_read(&qemu_cond_wait_func); \ ++ _f(c, m, __FILE__, __LINE__); \ ++ }) ++ ++#define qemu_cond_timedwait(c, m, ms) ({ \ ++ QemuCondTimedWaitFunc _f = qatomic_read(&qemu_cond_timedwait_func);\ ++ _f(c, m, ms, __FILE__, __LINE__); \ ++ }) ++#endif ++ ++#define qemu_mutex_unlock(mutex) \ ++ qemu_mutex_unlock_impl(mutex, __FILE__, __LINE__) ++ ++#define qemu_rec_mutex_unlock(mutex) \ ++ qemu_rec_mutex_unlock_impl(mutex, __FILE__, __LINE__) ++ ++static inline void (qemu_mutex_lock)(QemuMutex *mutex) ++{ ++ qemu_mutex_lock(mutex); ++} ++ ++static inline int (qemu_mutex_trylock)(QemuMutex *mutex) ++{ ++ return qemu_mutex_trylock(mutex); ++} ++ ++static inline void (qemu_mutex_unlock)(QemuMutex *mutex) ++{ ++ qemu_mutex_unlock(mutex); ++} ++ ++static inline void (qemu_rec_mutex_lock)(QemuRecMutex *mutex) ++{ ++ qemu_rec_mutex_lock(mutex); ++} ++ ++static inline int (qemu_rec_mutex_trylock)(QemuRecMutex *mutex) ++{ ++ return qemu_rec_mutex_trylock(mutex); ++} ++ ++static inline void (qemu_rec_mutex_unlock)(QemuRecMutex *mutex) ++{ ++ qemu_rec_mutex_unlock(mutex); ++} ++ ++void qemu_cond_init(QemuCond *cond); ++void qemu_cond_destroy(QemuCond *cond); ++ ++/* ++ * IMPORTANT: The implementation does not guarantee that pthread_cond_signal ++ * and pthread_cond_broadcast can be called except while the same mutex is ++ * held as in the corresponding pthread_cond_wait calls! ++ */ ++void qemu_cond_signal(QemuCond *cond); ++void qemu_cond_broadcast(QemuCond *cond); ++void TSA_NO_TSA qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, ++ const char *file, const int line); ++bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, ++ const char *file, const int line); ++ ++static inline void (qemu_cond_wait)(QemuCond *cond, QemuMutex *mutex) ++{ ++ qemu_cond_wait(cond, mutex); ++} ++ ++/* Returns true if timeout has not expired, and false otherwise */ ++static inline bool (qemu_cond_timedwait)(QemuCond *cond, QemuMutex *mutex, ++ int ms) ++{ ++ return qemu_cond_timedwait(cond, mutex, ms); ++} ++ ++void qemu_sem_init(QemuSemaphore *sem, int init); ++void qemu_sem_post(QemuSemaphore *sem); ++void qemu_sem_wait(QemuSemaphore *sem); ++int qemu_sem_timedwait(QemuSemaphore *sem, int ms); ++void qemu_sem_destroy(QemuSemaphore *sem); ++ ++void qemu_event_init(QemuEvent *ev, bool init); ++void qemu_event_set(QemuEvent *ev); ++void qemu_event_reset(QemuEvent *ev); ++void qemu_event_wait(QemuEvent *ev); ++void qemu_event_destroy(QemuEvent *ev); ++ ++void qemu_thread_create(QemuThread *thread, const char *name, ++ void *(*start_routine)(void *), ++ void *arg, int mode); ++int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, ++ unsigned long nbits); ++int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus, ++ unsigned long *nbits); ++void *qemu_thread_join(QemuThread *thread); ++void qemu_thread_get_self(QemuThread *thread); ++bool qemu_thread_is_self(QemuThread *thread); ++G_NORETURN void qemu_thread_exit(void *retval); ++void qemu_thread_naming(bool enable); ++ ++struct Notifier; ++/** ++ * qemu_thread_atexit_add: ++ * @notifier: Notifier to add ++ * ++ * Add the specified notifier to a list which will be run via ++ * notifier_list_notify() when this thread exits (either by calling ++ * qemu_thread_exit() or by returning from its start_routine). ++ * The usual usage is that the caller passes a Notifier which is ++ * a per-thread variable; it can then use the callback to free ++ * other per-thread data. ++ * ++ * If the thread exits as part of the entire process exiting, ++ * it is unspecified whether notifiers are called or not. ++ */ ++void qemu_thread_atexit_add(struct Notifier *notifier); ++/** ++ * qemu_thread_atexit_remove: ++ * @notifier: Notifier to remove ++ * ++ * Remove the specified notifier from the thread-exit notification ++ * list. It is not valid to try to remove a notifier which is not ++ * on the list. ++ */ ++void qemu_thread_atexit_remove(struct Notifier *notifier); ++ ++#ifdef CONFIG_TSAN ++#include ++#endif ++ ++struct QemuSpin { ++ int value; ++}; ++ ++static inline void qemu_spin_init(QemuSpin *spin) ++{ ++ qatomic_set(&spin->value, 0); ++#ifdef CONFIG_TSAN ++ __tsan_mutex_create(spin, __tsan_mutex_not_static); ++#endif ++} ++ ++static inline void qemu_spin_destroy(QemuSpin *spin) ++{ ++#ifdef CONFIG_TSAN ++ __tsan_mutex_destroy(spin, __tsan_mutex_not_static); ++#endif ++} ++ ++static inline void qemu_spin_lock(QemuSpin *spin) ++{ ++#ifdef CONFIG_TSAN ++ __tsan_mutex_pre_lock(spin, 0); ++#endif ++ while (unlikely(qatomic_xchg(&spin->value, 1))) { ++ while (qatomic_read(&spin->value)) { ++ cpu_relax(); ++ } ++ } ++#ifdef CONFIG_TSAN ++ __tsan_mutex_post_lock(spin, 0, 0); ++#endif ++} ++ ++static inline bool qemu_spin_trylock(QemuSpin *spin) ++{ ++#ifdef CONFIG_TSAN ++ __tsan_mutex_pre_lock(spin, __tsan_mutex_try_lock); ++#endif ++ bool busy = qatomic_xchg(&spin->value, true); ++#ifdef CONFIG_TSAN ++ unsigned flags = __tsan_mutex_try_lock; ++ flags |= busy ? __tsan_mutex_try_lock_failed : 0; ++ __tsan_mutex_post_lock(spin, flags, 0); ++#endif ++ return busy; ++} ++ ++static inline bool qemu_spin_locked(QemuSpin *spin) ++{ ++ return qatomic_read(&spin->value); ++} ++ ++static inline void qemu_spin_unlock(QemuSpin *spin) ++{ ++#ifdef CONFIG_TSAN ++ __tsan_mutex_pre_unlock(spin, 0); ++#endif ++ qatomic_store_release(&spin->value, 0); ++#ifdef CONFIG_TSAN ++ __tsan_mutex_post_unlock(spin, 0); ++#endif ++} ++ ++struct QemuLockCnt { ++#ifndef CONFIG_LINUX ++ QemuMutex mutex; ++#endif ++ unsigned count; ++}; ++ ++/** ++ * qemu_lockcnt_init: initialize a QemuLockcnt ++ * @lockcnt: the lockcnt to initialize ++ * ++ * Initialize lockcnt's counter to zero and prepare its mutex ++ * for usage. ++ */ ++void qemu_lockcnt_init(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_destroy: destroy a QemuLockcnt ++ * @lockcnt: the lockcnt to destruct ++ * ++ * Destroy lockcnt's mutex. ++ */ ++void qemu_lockcnt_destroy(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_inc: increment a QemuLockCnt's counter ++ * @lockcnt: the lockcnt to operate on ++ * ++ * If the lockcnt's count is zero, wait for critical sections ++ * to finish and increment lockcnt's count to 1. If the count ++ * is not zero, just increment it. ++ * ++ * Because this function can wait on the mutex, it must not be ++ * called while the lockcnt's mutex is held by the current thread. ++ * For the same reason, qemu_lockcnt_inc can also contribute to ++ * AB-BA deadlocks. This is a sample deadlock scenario: ++ * ++ * thread 1 thread 2 ++ * ------------------------------------------------------- ++ * qemu_lockcnt_lock(&lc1); ++ * qemu_lockcnt_lock(&lc2); ++ * qemu_lockcnt_inc(&lc2); ++ * qemu_lockcnt_inc(&lc1); ++ */ ++void qemu_lockcnt_inc(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_dec: decrement a QemuLockCnt's counter ++ * @lockcnt: the lockcnt to operate on ++ */ ++void qemu_lockcnt_dec(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_dec_and_lock: decrement a QemuLockCnt's counter and ++ * possibly lock it. ++ * @lockcnt: the lockcnt to operate on ++ * ++ * Decrement lockcnt's count. If the new count is zero, lock ++ * the mutex and return true. Otherwise, return false. ++ */ ++bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_dec_if_lock: possibly decrement a QemuLockCnt's counter and ++ * lock it. ++ * @lockcnt: the lockcnt to operate on ++ * ++ * If the count is 1, decrement the count to zero, lock ++ * the mutex and return true. Otherwise, return false. ++ */ ++bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_lock: lock a QemuLockCnt's mutex. ++ * @lockcnt: the lockcnt to operate on ++ * ++ * Remember that concurrent visits are not blocked unless the count is ++ * also zero. You can use qemu_lockcnt_count to check for this inside a ++ * critical section. ++ */ ++void qemu_lockcnt_lock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_unlock: release a QemuLockCnt's mutex. ++ * @lockcnt: the lockcnt to operate on. ++ */ ++void qemu_lockcnt_unlock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_inc_and_unlock: combined unlock/increment on a QemuLockCnt. ++ * @lockcnt: the lockcnt to operate on. ++ * ++ * This is the same as ++ * ++ * qemu_lockcnt_unlock(lockcnt); ++ * qemu_lockcnt_inc(lockcnt); ++ * ++ * but more efficient. ++ */ ++void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt); ++ ++/** ++ * qemu_lockcnt_count: query a LockCnt's count. ++ * @lockcnt: the lockcnt to query. ++ * ++ * Note that the count can change at any time. Still, while the ++ * lockcnt is locked, one can usefully check whether the count ++ * is non-zero. ++ */ ++unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt); ++ ++#endif +diff --git a/include/qemu/timed-average.h b/include/qemu/timed-average.h +new file mode 100644 +index 00000000..08245e7a +--- /dev/null ++++ b/include/qemu/timed-average.h +@@ -0,0 +1,63 @@ ++/* ++ * QEMU timed average computation ++ * ++ * Copyright (C) Nodalink, EURL. 2014 ++ * Copyright (C) Igalia, S.L. 2015 ++ * ++ * Authors: ++ * BenoĆ®t Canet ++ * Alberto Garcia ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 2 of the License, or ++ * (at your option) version 3 or any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#ifndef TIMED_AVERAGE_H ++#define TIMED_AVERAGE_H ++ ++ ++#include "qemu/timer.h" ++ ++typedef struct TimedAverageWindow TimedAverageWindow; ++typedef struct TimedAverage TimedAverage; ++ ++/* All fields of both structures are private */ ++ ++struct TimedAverageWindow { ++ uint64_t min; /* minimum value accounted in the window */ ++ uint64_t max; /* maximum value accounted in the window */ ++ uint64_t sum; /* sum of all values */ ++ uint64_t count; /* number of values */ ++ int64_t expiration; /* the end of the current window in ns */ ++}; ++ ++struct TimedAverage { ++ uint64_t period; /* period in nanoseconds */ ++ TimedAverageWindow windows[2]; /* two overlapping windows of with ++ * an offset of period / 2 between them */ ++ unsigned current; /* the current window index: it's also the ++ * oldest window index */ ++ QEMUClockType clock_type; /* the clock used */ ++}; ++ ++void timed_average_init(TimedAverage *ta, QEMUClockType clock_type, ++ uint64_t period); ++ ++void timed_average_account(TimedAverage *ta, uint64_t value); ++ ++uint64_t timed_average_min(TimedAverage *ta); ++uint64_t timed_average_avg(TimedAverage *ta); ++uint64_t timed_average_max(TimedAverage *ta); ++uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed); ++ ++#endif +diff --git a/include/qemu/timer.h b/include/qemu/timer.h +new file mode 100644 +index 00000000..fa56ec94 +--- /dev/null ++++ b/include/qemu/timer.h +@@ -0,0 +1,1038 @@ ++#ifndef QEMU_TIMER_H ++#define QEMU_TIMER_H ++ ++#include "qemu/bitops.h" ++#include "qemu/notify.h" ++#include "qemu/host-utils.h" ++ ++#define NANOSECONDS_PER_SECOND 1000000000LL ++ ++/* timers */ ++ ++#define SCALE_MS 1000000 ++#define SCALE_US 1000 ++#define SCALE_NS 1 ++ ++/** ++ * QEMUClockType: ++ * ++ * The following clock types are available: ++ * ++ * @QEMU_CLOCK_REALTIME: Real time clock ++ * ++ * The real time clock should be used only for stuff which does not ++ * change the virtual machine state, as it runs even if the virtual ++ * machine is stopped. ++ * ++ * @QEMU_CLOCK_VIRTUAL: virtual clock ++ * ++ * The virtual clock only runs during the emulation. It stops ++ * when the virtual machine is stopped. ++ * ++ * @QEMU_CLOCK_HOST: host clock ++ * ++ * The host clock should be used for device models that emulate accurate ++ * real time sources. It will continue to run when the virtual machine ++ * is suspended, and it will reflect system time changes the host may ++ * undergo (e.g. due to NTP). ++ * ++ * @QEMU_CLOCK_VIRTUAL_RT: realtime clock used for icount warp ++ * ++ * Outside icount mode, this clock is the same as @QEMU_CLOCK_VIRTUAL. ++ * In icount mode, this clock counts nanoseconds while the virtual ++ * machine is running. It is used to increase @QEMU_CLOCK_VIRTUAL ++ * while the CPUs are sleeping and thus not executing instructions. ++ */ ++ ++typedef enum { ++ QEMU_CLOCK_REALTIME = 0, ++ QEMU_CLOCK_VIRTUAL = 1, ++ QEMU_CLOCK_HOST = 2, ++ QEMU_CLOCK_VIRTUAL_RT = 3, ++ QEMU_CLOCK_MAX ++} QEMUClockType; ++ ++/** ++ * QEMU Timer attributes: ++ * ++ * An individual timer may be given one or multiple attributes when initialized. ++ * Each attribute corresponds to one bit. Attributes modify the processing ++ * of timers when they fire. ++ * ++ * The following attributes are available: ++ * ++ * QEMU_TIMER_ATTR_EXTERNAL: drives external subsystem ++ * QEMU_TIMER_ATTR_ALL: mask for all existing attributes ++ * ++ * Timers with this attribute do not recorded in rr mode, therefore it could be ++ * used for the subsystems that operate outside the guest core. Applicable only ++ * with virtual clock type. ++ */ ++ ++#define QEMU_TIMER_ATTR_EXTERNAL ((int)BIT(0)) ++#define QEMU_TIMER_ATTR_ALL 0xffffffff ++ ++typedef struct QEMUTimerList QEMUTimerList; ++ ++struct QEMUTimerListGroup { ++ QEMUTimerList *tl[QEMU_CLOCK_MAX]; ++}; ++ ++typedef void QEMUTimerCB(void *opaque); ++typedef void QEMUTimerListNotifyCB(void *opaque, QEMUClockType type); ++ ++struct QEMUTimer { ++ int64_t expire_time; /* in nanoseconds */ ++ QEMUTimerList *timer_list; ++ QEMUTimerCB *cb; ++ void *opaque; ++ QEMUTimer *next; ++ int attributes; ++ int scale; ++}; ++ ++extern QEMUTimerListGroup main_loop_tlg; ++ ++/* ++ * qemu_clock_get_ns; ++ * @type: the clock type ++ * ++ * Get the nanosecond value of a clock with ++ * type @type ++ * ++ * Returns: the clock value in nanoseconds ++ */ ++int64_t qemu_clock_get_ns(QEMUClockType type); ++ ++/** ++ * qemu_clock_get_ms; ++ * @type: the clock type ++ * ++ * Get the millisecond value of a clock with ++ * type @type ++ * ++ * Returns: the clock value in milliseconds ++ */ ++static inline int64_t qemu_clock_get_ms(QEMUClockType type) ++{ ++ return qemu_clock_get_ns(type) / SCALE_MS; ++} ++ ++/** ++ * qemu_clock_get_us; ++ * @type: the clock type ++ * ++ * Get the microsecond value of a clock with ++ * type @type ++ * ++ * Returns: the clock value in microseconds ++ */ ++static inline int64_t qemu_clock_get_us(QEMUClockType type) ++{ ++ return qemu_clock_get_ns(type) / SCALE_US; ++} ++ ++/** ++ * qemu_clock_has_timers: ++ * @type: the clock type ++ * ++ * Determines whether a clock's default timer list ++ * has timers attached ++ * ++ * Note that this function should not be used when other threads also access ++ * the timer list. The return value may be outdated by the time it is acted ++ * upon. ++ * ++ * Returns: true if the clock's default timer list ++ * has timers attached ++ */ ++bool qemu_clock_has_timers(QEMUClockType type); ++ ++/** ++ * qemu_clock_expired: ++ * @type: the clock type ++ * ++ * Determines whether a clock's default timer list ++ * has an expired timer. ++ * ++ * Returns: true if the clock's default timer list has ++ * an expired timer ++ */ ++bool qemu_clock_expired(QEMUClockType type); ++ ++/** ++ * qemu_clock_use_for_deadline: ++ * @type: the clock type ++ * ++ * Determine whether a clock should be used for deadline ++ * calculations. Some clocks, for instance vm_clock with ++ * icount_enabled() set, do not count in nanoseconds. ++ * Such clocks are not used for deadline calculations, and are presumed ++ * to interrupt any poll using qemu_notify/aio_notify ++ * etc. ++ * ++ * Returns: true if the clock runs in nanoseconds and ++ * should be used for a deadline. ++ */ ++bool qemu_clock_use_for_deadline(QEMUClockType type); ++ ++/** ++ * qemu_clock_deadline_ns_all: ++ * @type: the clock type ++ * @attr_mask: mask for the timer attributes that are included ++ * in deadline calculation ++ * ++ * Calculate the deadline across all timer lists associated ++ * with a clock (as opposed to just the default one) ++ * in nanoseconds, or -1 if no timer is set to expire. ++ * ++ * Returns: time until expiry in nanoseconds or -1 ++ */ ++int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask); ++ ++/** ++ * qemu_clock_get_main_loop_timerlist: ++ * @type: the clock type ++ * ++ * Return the default timer list associated with a clock. ++ * ++ * Returns: the default timer list ++ */ ++QEMUTimerList *qemu_clock_get_main_loop_timerlist(QEMUClockType type); ++ ++/** ++ * qemu_clock_nofify: ++ * @type: the clock type ++ * ++ * Call the notifier callback connected with the default timer ++ * list linked to the clock, or qemu_notify() if none. ++ */ ++void qemu_clock_notify(QEMUClockType type); ++ ++/** ++ * qemu_clock_enable: ++ * @type: the clock type ++ * @enabled: true to enable, false to disable ++ * ++ * Enable or disable a clock ++ * Disabling the clock will wait for related timerlists to stop ++ * executing qemu_run_timers. Thus, this functions should not ++ * be used from the callback of a timer that is based on @clock. ++ * Doing so would cause a deadlock. ++ * ++ * Caller should hold BQL. ++ */ ++void qemu_clock_enable(QEMUClockType type, bool enabled); ++ ++/** ++ * qemu_clock_run_timers: ++ * @type: clock on which to operate ++ * ++ * Run all the timers associated with the default timer list ++ * of a clock. ++ * ++ * Returns: true if any timer ran. ++ */ ++bool qemu_clock_run_timers(QEMUClockType type); ++ ++/** ++ * qemu_clock_run_all_timers: ++ * ++ * Run all the timers associated with the default timer list ++ * of every clock. ++ * ++ * Returns: true if any timer ran. ++ */ ++bool qemu_clock_run_all_timers(void); ++ ++/** ++ * qemu_clock_advance_virtual_time(): advance the virtual time tick ++ * @target_ns: target time in nanoseconds ++ * ++ * This function is used where the control of the flow of time has ++ * been delegated to outside the clock subsystem (be it qtest, icount ++ * or some other external source). You can ask the clock system to ++ * return @early at the first expired timer. ++ * ++ * Time can only move forward, attempts to reverse time would lead to ++ * an error. ++ * ++ * Returns: new virtual time. ++ */ ++int64_t qemu_clock_advance_virtual_time(int64_t target_ns); ++ ++/* ++ * QEMUTimerList ++ */ ++ ++/** ++ * timerlist_new: ++ * @type: the clock type to associate with the timerlist ++ * @cb: the callback to call on notification ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Create a new timerlist associated with the clock of ++ * type @type. ++ * ++ * Returns: a pointer to the QEMUTimerList created ++ */ ++QEMUTimerList *timerlist_new(QEMUClockType type, ++ QEMUTimerListNotifyCB *cb, void *opaque); ++ ++/** ++ * timerlist_free: ++ * @timer_list: the timer list to free ++ * ++ * Frees a timer_list. It must have no active timers. ++ */ ++void timerlist_free(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_has_timers: ++ * @timer_list: the timer list to operate on ++ * ++ * Determine whether a timer list has active timers ++ * ++ * Note that this function should not be used when other threads also access ++ * the timer list. The return value may be outdated by the time it is acted ++ * upon. ++ * ++ * Returns: true if the timer list has timers. ++ */ ++bool timerlist_has_timers(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_expired: ++ * @timer_list: the timer list to operate on ++ * ++ * Determine whether a timer list has any timers which ++ * are expired. ++ * ++ * Returns: true if the timer list has timers which ++ * have expired. ++ */ ++bool timerlist_expired(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_deadline_ns: ++ * @timer_list: the timer list to operate on ++ * ++ * Determine the deadline for a timer_list, i.e. ++ * the number of nanoseconds until the first timer ++ * expires. Return -1 if there are no timers. ++ * ++ * Returns: the number of nanoseconds until the earliest ++ * timer expires -1 if none ++ */ ++int64_t timerlist_deadline_ns(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_get_clock: ++ * @timer_list: the timer list to operate on ++ * ++ * Determine the clock type associated with a timer list. ++ * ++ * Returns: the clock type associated with the ++ * timer list. ++ */ ++QEMUClockType timerlist_get_clock(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_run_timers: ++ * @timer_list: the timer list to use ++ * ++ * Call all expired timers associated with the timer list. ++ * ++ * Returns: true if any timer expired ++ */ ++bool timerlist_run_timers(QEMUTimerList *timer_list); ++ ++/** ++ * timerlist_notify: ++ * @timer_list: the timer list to use ++ * ++ * call the notifier callback associated with the timer list. ++ */ ++void timerlist_notify(QEMUTimerList *timer_list); ++ ++/* ++ * QEMUTimerListGroup ++ */ ++ ++/** ++ * timerlistgroup_init: ++ * @tlg: the timer list group ++ * @cb: the callback to call when a notify is required ++ * @opaque: the opaque pointer to be passed to the callback. ++ * ++ * Initialise a timer list group. This must already be ++ * allocated in memory and zeroed. The notifier callback is ++ * called whenever a clock in the timer list group is ++ * reenabled or whenever a timer associated with any timer ++ * list is modified. If @cb is specified as null, qemu_notify() ++ * is used instead. ++ */ ++void timerlistgroup_init(QEMUTimerListGroup *tlg, ++ QEMUTimerListNotifyCB *cb, void *opaque); ++ ++/** ++ * timerlistgroup_deinit: ++ * @tlg: the timer list group ++ * ++ * Deinitialise a timer list group. This must already be ++ * initialised. Note the memory is not freed. ++ */ ++void timerlistgroup_deinit(QEMUTimerListGroup *tlg); ++ ++/** ++ * timerlistgroup_run_timers: ++ * @tlg: the timer list group ++ * ++ * Run the timers associated with a timer list group. ++ * This will run timers on multiple clocks. ++ * ++ * Returns: true if any timer callback ran ++ */ ++bool timerlistgroup_run_timers(QEMUTimerListGroup *tlg); ++ ++/** ++ * timerlistgroup_deadline_ns: ++ * @tlg: the timer list group ++ * ++ * Determine the deadline of the soonest timer to ++ * expire associated with any timer list linked to ++ * the timer list group. Only clocks suitable for ++ * deadline calculation are included. ++ * ++ * Returns: the deadline in nanoseconds or -1 if no ++ * timers are to expire. ++ */ ++int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg); ++ ++/* ++ * QEMUTimer ++ */ ++ ++/** ++ * timer_init_full: ++ * @ts: the timer to be initialised ++ * @timer_list_group: (optional) the timer list group to attach the timer to ++ * @type: the clock type to use ++ * @scale: the scale value for the timer ++ * @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_ values ++ * @cb: the callback to be called when the timer expires ++ * @opaque: the opaque pointer to be passed to the callback ++ * ++ * Initialise a timer with the given scale and attributes, ++ * and associate it with timer list for given clock @type in @timer_list_group ++ * (or default timer list group, if NULL). ++ * The caller is responsible for allocating the memory. ++ * ++ * You need not call an explicit deinit call. Simply make ++ * sure it is not on a list with timer_del. ++ */ ++void timer_init_full(QEMUTimer *ts, ++ QEMUTimerListGroup *timer_list_group, QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque); ++ ++/** ++ * timer_init: ++ * @ts: the timer to be initialised ++ * @type: the clock to associate with the timer ++ * @scale: the scale value for the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialize a timer with the given scale on the default timer list ++ * associated with the clock. ++ * See timer_init_full for details. ++ */ ++static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init_full(ts, NULL, type, scale, 0, cb, opaque); ++} ++ ++/** ++ * timer_init_ns: ++ * @ts: the timer to be initialised ++ * @type: the clock to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialize a timer with nanosecond scale on the default timer list ++ * associated with the clock. ++ * See timer_init_full for details. ++ */ ++static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init(ts, type, SCALE_NS, cb, opaque); ++} ++ ++/** ++ * timer_init_us: ++ * @ts: the timer to be initialised ++ * @type: the clock to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialize a timer with microsecond scale on the default timer list ++ * associated with the clock. ++ * See timer_init_full for details. ++ */ ++static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init(ts, type, SCALE_US, cb, opaque); ++} ++ ++/** ++ * timer_init_ms: ++ * @ts: the timer to be initialised ++ * @type: the clock to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Initialize a timer with millisecond scale on the default timer list ++ * associated with the clock. ++ * See timer_init_full for details. ++ */ ++static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ timer_init(ts, type, SCALE_MS, cb, opaque); ++} ++ ++/** ++ * timer_new_full: ++ * @timer_list_group: (optional) the timer list group to attach the timer to ++ * @type: the clock type to use ++ * @scale: the scale value for the timer ++ * @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_ values ++ * @cb: the callback to be called when the timer expires ++ * @opaque: the opaque pointer to be passed to the callback ++ * ++ * Create a new timer with the given scale and attributes, ++ * and associate it with timer list for given clock @type in @timer_list_group ++ * (or default timer list group, if NULL). ++ * The memory is allocated by the function. ++ * ++ * This is not the preferred interface unless you know you ++ * are going to call timer_free. Use timer_init or timer_init_full instead. ++ * ++ * The default timer list has one special feature: in icount mode, ++ * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is ++ * not true of other timer lists, which are typically associated ++ * with an AioContext---each of them runs its timer callbacks in its own ++ * AioContext thread. ++ * ++ * Returns: a pointer to the timer ++ */ ++static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group, ++ QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ QEMUTimer *ts = g_new0(QEMUTimer, 1); ++ timer_init_full(ts, timer_list_group, type, scale, attributes, cb, opaque); ++ return ts; ++} ++ ++/** ++ * timer_new: ++ * @type: the clock type to use ++ * @scale: the scale value for the timer ++ * @cb: the callback to be called when the timer expires ++ * @opaque: the opaque pointer to be passed to the callback ++ * ++ * Create a new timer with the given scale, ++ * and associate it with the default timer list for the clock type @type. ++ * See timer_new_full for details. ++ * ++ * Returns: a pointer to the timer ++ */ ++static inline QEMUTimer *timer_new(QEMUClockType type, int scale, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ return timer_new_full(NULL, type, scale, 0, cb, opaque); ++} ++ ++/** ++ * timer_new_ns: ++ * @type: the clock type to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Create a new timer with nanosecond scale on the default timer list ++ * associated with the clock. ++ * See timer_new_full for details. ++ * ++ * Returns: a pointer to the newly created timer ++ */ ++static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, ++ void *opaque) ++{ ++ return timer_new(type, SCALE_NS, cb, opaque); ++} ++ ++/** ++ * timer_new_us: ++ * @type: the clock type to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Create a new timer with microsecond scale on the default timer list ++ * associated with the clock. ++ * See timer_new_full for details. ++ * ++ * Returns: a pointer to the newly created timer ++ */ ++static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb, ++ void *opaque) ++{ ++ return timer_new(type, SCALE_US, cb, opaque); ++} ++ ++/** ++ * timer_new_ms: ++ * @type: the clock type to associate with the timer ++ * @cb: the callback to call when the timer expires ++ * @opaque: the opaque pointer to pass to the callback ++ * ++ * Create a new timer with millisecond scale on the default timer list ++ * associated with the clock. ++ * See timer_new_full for details. ++ * ++ * Returns: a pointer to the newly created timer ++ */ ++static inline QEMUTimer *timer_new_ms(QEMUClockType type, QEMUTimerCB *cb, ++ void *opaque) ++{ ++ return timer_new(type, SCALE_MS, cb, opaque); ++} ++ ++/** ++ * timer_deinit: ++ * @ts: the timer to be de-initialised ++ * ++ * Deassociate the timer from any timerlist. You should ++ * call timer_del before. After this call, any further ++ * timer_del call cannot cause dangling pointer accesses ++ * even if the previously used timerlist is freed. ++ */ ++void timer_deinit(QEMUTimer *ts); ++ ++/** ++ * timer_del: ++ * @ts: the timer ++ * ++ * Delete a timer from the active list. ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_del(QEMUTimer *ts); ++ ++/** ++ * timer_free: ++ * @ts: the timer ++ * ++ * Free a timer. This will call timer_del() for you to remove ++ * the timer from the active list if it was still active. ++ */ ++static inline void timer_free(QEMUTimer *ts) ++{ ++ if (ts) { ++ timer_del(ts); ++ g_free(ts); ++ } ++} ++ ++/** ++ * timer_mod_ns: ++ * @ts: the timer ++ * @expire_time: the expiry time in nanoseconds ++ * ++ * Modify a timer to expire at @expire_time ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_mod_ns(QEMUTimer *ts, int64_t expire_time); ++ ++/** ++ * timer_mod_anticipate_ns: ++ * @ts: the timer ++ * @expire_time: the expiry time in nanoseconds ++ * ++ * Modify a timer to expire at @expire_time or the current time, ++ * whichever comes earlier. ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_mod_anticipate_ns(QEMUTimer *ts, int64_t expire_time); ++ ++/** ++ * timer_mod: ++ * @ts: the timer ++ * @expire_time: the expire time in the units associated with the timer ++ * ++ * Modify a timer to expiry at @expire_time, taking into ++ * account the scale associated with the timer. ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_mod(QEMUTimer *ts, int64_t expire_timer); ++ ++/** ++ * timer_mod_anticipate: ++ * @ts: the timer ++ * @expire_time: the expire time in the units associated with the timer ++ * ++ * Modify a timer to expire at @expire_time or the current time, whichever ++ * comes earlier, taking into account the scale associated with the timer. ++ * ++ * This function is thread-safe but the timer and its timer list must not be ++ * freed while this function is running. ++ */ ++void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time); ++ ++/** ++ * timer_pending: ++ * @ts: the timer ++ * ++ * Determines whether a timer is pending (i.e. is on the ++ * active list of timers, whether or not it has not yet expired). ++ * ++ * Returns: true if the timer is pending ++ */ ++bool timer_pending(QEMUTimer *ts); ++ ++/** ++ * timer_expired: ++ * @ts: the timer ++ * @current_time: the current time ++ * ++ * Determines whether a timer has expired. ++ * ++ * Returns: true if the timer has expired ++ */ ++bool timer_expired(QEMUTimer *timer_head, int64_t current_time); ++ ++/** ++ * timer_expire_time_ns: ++ * @ts: the timer ++ * ++ * Determine the expiry time of a timer ++ * ++ * Returns: the expiry time in nanoseconds ++ */ ++uint64_t timer_expire_time_ns(QEMUTimer *ts); ++ ++/** ++ * timer_get: ++ * @f: the file ++ * @ts: the timer ++ * ++ * Read a timer @ts from a file @f ++ */ ++void timer_get(QEMUFile *f, QEMUTimer *ts); ++ ++/** ++ * timer_put: ++ * @f: the file ++ * @ts: the timer ++ */ ++void timer_put(QEMUFile *f, QEMUTimer *ts); ++ ++/* ++ * General utility functions ++ */ ++ ++/** ++ * qemu_timeout_ns_to_ms: ++ * @ns: nanosecond timeout value ++ * ++ * Convert a nanosecond timeout value (or -1) to ++ * a millisecond value (or -1), always rounding up. ++ * ++ * Returns: millisecond timeout value ++ */ ++int qemu_timeout_ns_to_ms(int64_t ns); ++ ++/** ++ * qemu_poll_ns: ++ * @fds: Array of file descriptors ++ * @nfds: number of file descriptors ++ * @timeout: timeout in nanoseconds ++ * ++ * Perform a poll like g_poll but with a timeout in nanoseconds. ++ * See g_poll documentation for further details. ++ * ++ * Returns: number of fds ready ++ */ ++int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout); ++ ++/** ++ * qemu_soonest_timeout: ++ * @timeout1: first timeout in nanoseconds (or -1 for infinite) ++ * @timeout2: second timeout in nanoseconds (or -1 for infinite) ++ * ++ * Calculates the soonest of two timeout values. -1 means infinite, which ++ * is later than any other value. ++ * ++ * Returns: soonest timeout value in nanoseconds (or -1 for infinite) ++ */ ++static inline int64_t qemu_soonest_timeout(int64_t timeout1, int64_t timeout2) ++{ ++ /* we can abuse the fact that -1 (which means infinite) is a maximal ++ * value when cast to unsigned. As this is disgusting, it's kept in ++ * one inline function. ++ */ ++ return ((uint64_t) timeout1 < (uint64_t) timeout2) ? timeout1 : timeout2; ++} ++ ++/** ++ * initclocks: ++ * ++ * Initialise the clock & timer infrastructure ++ */ ++void init_clocks(QEMUTimerListNotifyCB *notify_cb); ++ ++static inline int64_t get_max_clock_jump(void) ++{ ++ /* This should be small enough to prevent excessive interrupts from being ++ * generated by the RTC on clock jumps, but large enough to avoid frequent ++ * unnecessary resets in idle VMs. ++ */ ++ return 60 * NANOSECONDS_PER_SECOND; ++} ++ ++/* ++ * Low level clock functions ++ */ ++ ++/* get host real time in nanosecond */ ++static inline int64_t get_clock_realtime(void) ++{ ++ struct timeval tv; ++ ++ gettimeofday(&tv, NULL); ++ return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000); ++} ++ ++extern int64_t clock_start; ++ ++/* Warning: don't insert tracepoints into these functions, they are ++ also used by simpletrace backend and tracepoints would cause ++ an infinite recursion! */ ++#ifdef _WIN32 ++extern int64_t clock_freq; ++ ++static inline int64_t get_clock(void) ++{ ++ LARGE_INTEGER ti; ++ QueryPerformanceCounter(&ti); ++ return muldiv64(ti.QuadPart, NANOSECONDS_PER_SECOND, clock_freq); ++} ++ ++#else ++ ++extern int use_rt_clock; ++ ++static inline int64_t get_clock(void) ++{ ++ if (use_rt_clock) { ++ struct timespec ts; ++ clock_gettime(CLOCK_MONOTONIC, &ts); ++ return ts.tv_sec * 1000000000LL + ts.tv_nsec; ++ } else { ++ /* XXX: using gettimeofday leads to problems if the date ++ changes, so it should be avoided. */ ++ return get_clock_realtime(); ++ } ++} ++#endif ++ ++/*******************************************/ ++/* host CPU ticks (if available) */ ++ ++#if defined(_ARCH_PPC) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int64_t retval; ++#ifdef _ARCH_PPC64 ++ /* This reads timebase in one 64bit go and includes Cell workaround from: ++ http://ozlabs.org/pipermail/linuxppc-dev/2006-October/027052.html ++ */ ++ __asm__ __volatile__ ("mftb %0\n\t" ++ "cmpwi %0,0\n\t" ++ "beq- $-8" ++ : "=r" (retval)); ++#else ++ /* http://ozlabs.org/pipermail/linuxppc-dev/1999-October/003889.html */ ++ unsigned long junk; ++ __asm__ __volatile__ ("mfspr %1,269\n\t" /* mftbu */ ++ "mfspr %L0,268\n\t" /* mftb */ ++ "mfspr %0,269\n\t" /* mftbu */ ++ "cmpw %0,%1\n\t" ++ "bne $-16" ++ : "=r" (retval), "=r" (junk)); ++#endif ++ return retval; ++} ++ ++#elif defined(__i386__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int64_t val; ++ asm volatile ("rdtsc" : "=A" (val)); ++ return val; ++} ++ ++#elif defined(__x86_64__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ uint32_t low,high; ++ int64_t val; ++ asm volatile("rdtsc" : "=a" (low), "=d" (high)); ++ val = high; ++ val <<= 32; ++ val |= low; ++ return val; ++} ++ ++#elif defined(__hppa__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int val; ++ asm volatile ("mfctl %%cr16, %0" : "=r"(val)); ++ return val; ++} ++ ++#elif defined(__s390__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int64_t val; ++ asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); ++ return val; ++} ++ ++#elif defined(__sparc__) ++ ++static inline int64_t cpu_get_host_ticks (void) ++{ ++#if defined(_LP64) ++ uint64_t rval; ++ asm volatile("rd %%tick,%0" : "=r"(rval)); ++ return rval; ++#else ++ /* We need an %o or %g register for this. For recent enough gcc ++ there is an "h" constraint for that. Don't bother with that. */ ++ union { ++ uint64_t i64; ++ struct { ++ uint32_t high; ++ uint32_t low; ++ } i32; ++ } rval; ++ asm volatile("rd %%tick,%%g1; srlx %%g1,32,%0; mov %%g1,%1" ++ : "=r"(rval.i32.high), "=r"(rval.i32.low) : : "g1"); ++ return rval.i64; ++#endif ++} ++ ++#elif defined(__mips__) && \ ++ ((defined(__mips_isa_rev) && __mips_isa_rev >= 2) || defined(__linux__)) ++/* ++ * binutils wants to use rdhwr only on mips32r2 ++ * but as linux kernel emulate it, it's fine ++ * to use it. ++ * ++ */ ++#define MIPS_RDHWR(rd, value) { \ ++ __asm__ __volatile__ (".set push\n\t" \ ++ ".set mips32r2\n\t" \ ++ "rdhwr %0, "rd"\n\t" \ ++ ".set pop" \ ++ : "=r" (value)); \ ++ } ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ /* On kernels >= 2.6.25 rdhwr , $2 and $3 are emulated */ ++ uint32_t count; ++ static uint32_t cyc_per_count = 0; ++ ++ if (!cyc_per_count) { ++ MIPS_RDHWR("$3", cyc_per_count); ++ } ++ ++ MIPS_RDHWR("$2", count); ++ return (int64_t)(count * cyc_per_count); ++} ++ ++#elif defined(__alpha__) ++ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ uint64_t cc; ++ uint32_t cur, ofs; ++ ++ asm volatile("rpcc %0" : "=r"(cc)); ++ cur = cc; ++ ofs = cc >> 32; ++ return cur - ofs; ++} ++ ++#elif defined(__riscv) && __riscv_xlen == 32 ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ uint32_t lo, hi, tmph; ++ do { ++ asm volatile("RDTIMEH %0\n\t" ++ "RDTIME %1\n\t" ++ "RDTIMEH %2" ++ : "=r"(hi), "=r"(lo), "=r"(tmph)); ++ } while (unlikely(tmph != hi)); ++ return lo | (uint64_t)hi << 32; ++} ++ ++#elif defined(__riscv) && __riscv_xlen > 32 ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ int64_t val; ++ ++ asm volatile("RDTIME %0" : "=r"(val)); ++ return val; ++} ++ ++#elif defined(__loongarch64) ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ uint64_t val; ++ ++ asm volatile("rdtime.d %0, $zero" : "=r"(val)); ++ return val; ++} ++ ++#else ++/* The host CPU doesn't have an easily accessible cycle counter. ++ Just return a monotonically increasing value. This will be ++ totally wrong, but hopefully better than nothing. */ ++static inline int64_t cpu_get_host_ticks(void) ++{ ++ return get_clock(); ++} ++#endif ++ ++#endif +diff --git a/include/qemu/transactions.h b/include/qemu/transactions.h +new file mode 100644 +index 00000000..2f2060ac +--- /dev/null ++++ b/include/qemu/transactions.h +@@ -0,0 +1,66 @@ ++/* ++ * Simple transactions API ++ * ++ * Copyright (c) 2021 Virtuozzo International GmbH. ++ * ++ * Author: ++ * Vladimir Sementsov-Ogievskiy ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * ++ * = Generic transaction API = ++ * ++ * The intended usage is the following: you create "prepare" functions, which ++ * represents the actions. They will usually have Transaction* argument, and ++ * call tran_add() to register finalization callbacks. For finalization ++ * callbacks, prepare corresponding TransactionActionDrv structures. ++ * ++ * Then, when you need to make a transaction, create an empty Transaction by ++ * tran_create(), call your "prepare" functions on it, and finally call ++ * tran_abort() or tran_commit() to finalize the transaction by corresponding ++ * finalization actions in reverse order. ++ * ++ * The clean() functions registered by the drivers in a transaction are called ++ * last, after all abort() or commit() functions have been called. ++ */ ++ ++#ifndef QEMU_TRANSACTIONS_H ++#define QEMU_TRANSACTIONS_H ++ ++#include ++ ++typedef struct TransactionActionDrv { ++ void (*abort)(void *opaque); ++ void (*commit)(void *opaque); ++ void (*clean)(void *opaque); ++} TransactionActionDrv; ++ ++typedef struct Transaction Transaction; ++ ++Transaction *tran_new(void); ++void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque); ++void tran_abort(Transaction *tran); ++void tran_commit(Transaction *tran); ++ ++static inline void tran_finalize(Transaction *tran, int ret) ++{ ++ if (ret < 0) { ++ tran_abort(tran); ++ } else { ++ tran_commit(tran); ++ } ++} ++ ++#endif /* QEMU_TRANSACTIONS_H */ +diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h +new file mode 100644 +index 00000000..9d222dc3 +--- /dev/null ++++ b/include/qemu/typedefs.h +@@ -0,0 +1,134 @@ ++#ifndef QEMU_TYPEDEFS_H ++#define QEMU_TYPEDEFS_H ++ ++/* ++ * This header is for selectively avoiding #include just to get a ++ * typedef name. ++ * ++ * Declaring a typedef name in its "obvious" place can result in ++ * inclusion cycles, in particular for complete struct and union ++ * types that need more types for their members. It can also result ++ * in headers pulling in many more headers, slowing down builds. ++ * ++ * You can break such cycles and unwanted dependencies by declaring ++ * the typedef name here. ++ * ++ * For struct types used in only a few headers, judicious use of the ++ * struct tag instead of the typedef name is commonly preferable. ++ */ ++ ++/* ++ * Incomplete struct types ++ * Please keep this list in case-insensitive alphabetical order. ++ */ ++typedef struct AccelCPUState AccelCPUState; ++typedef struct AccelState AccelState; ++typedef struct AddressSpace AddressSpace; ++typedef struct AioContext AioContext; ++typedef struct Aml Aml; ++typedef struct ArchCPU ArchCPU; ++typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; ++typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter; ++typedef struct BlockBackend BlockBackend; ++typedef struct BlockBackendRootState BlockBackendRootState; ++typedef struct BlockDriverState BlockDriverState; ++typedef struct BusClass BusClass; ++typedef struct BusState BusState; ++typedef struct Chardev Chardev; ++typedef struct Clock Clock; ++typedef struct ConfidentialGuestSupport ConfidentialGuestSupport; ++typedef struct CPUArchState CPUArchState; ++typedef struct CPUPluginState CPUPluginState; ++typedef struct CPUState CPUState; ++typedef struct DeviceState DeviceState; ++typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; ++typedef struct DisasContextBase DisasContextBase; ++typedef struct DisplayChangeListener DisplayChangeListener; ++typedef struct DriveInfo DriveInfo; ++typedef struct DumpState DumpState; ++typedef struct Error Error; ++typedef struct EventNotifier EventNotifier; ++typedef struct FlatView FlatView; ++typedef struct FWCfgState FWCfgState; ++typedef struct HostMemoryBackend HostMemoryBackend; ++typedef struct I2CBus I2CBus; ++typedef struct I2SCodec I2SCodec; ++typedef struct IOMMUMemoryRegion IOMMUMemoryRegion; ++typedef struct ISABus ISABus; ++typedef struct ISADevice ISADevice; ++typedef struct IsaDma IsaDma; ++typedef struct JSONWriter JSONWriter; ++typedef struct MACAddr MACAddr; ++typedef struct MachineClass MachineClass; ++typedef struct MachineState MachineState; ++typedef struct MemoryListener MemoryListener; ++typedef struct MemoryMappingList MemoryMappingList; ++typedef struct MemoryRegion MemoryRegion; ++typedef struct MemoryRegionCache MemoryRegionCache; ++typedef struct MemoryRegionSection MemoryRegionSection; ++typedef struct MigrationIncomingState MigrationIncomingState; ++typedef struct MigrationState MigrationState; ++typedef struct Monitor Monitor; ++typedef struct MSIMessage MSIMessage; ++typedef struct NetClientState NetClientState; ++typedef struct NetFilterState NetFilterState; ++typedef struct NICInfo NICInfo; ++typedef struct Object Object; ++typedef struct ObjectClass ObjectClass; ++typedef struct PCIBridge PCIBridge; ++typedef struct PCIBus PCIBus; ++typedef struct PCIDevice PCIDevice; ++typedef struct PCIEPort PCIEPort; ++typedef struct PCIESlot PCIESlot; ++typedef struct PCIExpressDevice PCIExpressDevice; ++typedef struct PCIExpressHost PCIExpressHost; ++typedef struct PCIHostDeviceAddress PCIHostDeviceAddress; ++typedef struct PCIHostState PCIHostState; ++typedef struct Property Property; ++typedef struct PropertyInfo PropertyInfo; ++typedef struct QBool QBool; ++typedef struct QDict QDict; ++typedef struct QEMUBH QEMUBH; ++typedef struct QemuConsole QemuConsole; ++typedef struct QEMUCursor QEMUCursor; ++typedef struct QEMUFile QEMUFile; ++typedef struct QemuMutex QemuMutex; ++typedef struct QemuOpts QemuOpts; ++typedef struct QemuOptsList QemuOptsList; ++typedef struct QEMUSGList QEMUSGList; ++typedef struct QemuSpin QemuSpin; ++typedef struct QEMUTimer QEMUTimer; ++typedef struct QEMUTimerListGroup QEMUTimerListGroup; ++typedef struct QList QList; ++typedef struct QNull QNull; ++typedef struct QNum QNum; ++typedef struct QObject QObject; ++typedef struct QString QString; ++typedef struct RAMBlock RAMBlock; ++typedef struct Range Range; ++typedef struct ReservedRegion ReservedRegion; ++typedef struct SHPCDevice SHPCDevice; ++typedef struct SSIBus SSIBus; ++typedef struct TCGCPUOps TCGCPUOps; ++typedef struct TCGHelperInfo TCGHelperInfo; ++typedef struct TaskState TaskState; ++typedef struct TranslationBlock TranslationBlock; ++typedef struct VirtIODevice VirtIODevice; ++typedef struct Visitor Visitor; ++typedef struct VMChangeStateEntry VMChangeStateEntry; ++typedef struct VMStateDescription VMStateDescription; ++ ++/* ++ * Pointer types ++ * Such typedefs should be limited to cases where the typedef's users ++ * are oblivious of its "pointer-ness". ++ * Please keep this list in case-insensitive alphabetical order. ++ */ ++typedef struct IRQState *qemu_irq; ++ ++/* ++ * Function types ++ */ ++typedef void (*qemu_irq_handler)(void *opaque, int n, int level); ++ ++#endif /* QEMU_TYPEDEFS_H */ +diff --git a/include/qemu/unicode.h b/include/qemu/unicode.h +new file mode 100644 +index 00000000..7fa10b8e +--- /dev/null ++++ b/include/qemu/unicode.h +@@ -0,0 +1,7 @@ ++#ifndef QEMU_UNICODE_H ++#define QEMU_UNICODE_H ++ ++int mod_utf8_codepoint(const char *s, size_t n, char **end); ++ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint); ++ ++#endif +diff --git a/include/qemu/units.h b/include/qemu/units.h +new file mode 100644 +index 00000000..692db3fb +--- /dev/null ++++ b/include/qemu/units.h +@@ -0,0 +1,20 @@ ++/* ++ * IEC binary prefixes definitions ++ * ++ * Copyright (C) 2015 Nikunj A Dadhania, IBM Corporation ++ * Copyright (C) 2018 Philippe Mathieu-DaudĆ© ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ */ ++ ++#ifndef QEMU_UNITS_H ++#define QEMU_UNITS_H ++ ++#define KiB (INT64_C(1) << 10) ++#define MiB (INT64_C(1) << 20) ++#define GiB (INT64_C(1) << 30) ++#define TiB (INT64_C(1) << 40) ++#define PiB (INT64_C(1) << 50) ++#define EiB (INT64_C(1) << 60) ++ ++#endif +diff --git a/include/qemu/uri.h b/include/qemu/uri.h +new file mode 100644 +index 00000000..255e61f4 +--- /dev/null ++++ b/include/qemu/uri.h +@@ -0,0 +1,99 @@ ++/** ++ * Summary: library of generic URI related routines ++ * Description: library of generic URI related routines ++ * Implements RFC 2396 ++ * ++ * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Except as contained in this notice, the name of Daniel Veillard shall not ++ * be used in advertising or otherwise to promote the sale, use or other ++ * dealings in this Software without prior written authorization from him. ++ * ++ * Author: Daniel Veillard ++ ** ++ * Copyright (C) 2007 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library. If not, see . ++ * ++ * Authors: ++ * Richard W.M. Jones ++ * ++ * Utility functions to help parse and assemble query strings. ++ */ ++ ++#ifndef QEMU_URI_H ++#define QEMU_URI_H ++ ++/** ++ * URI: ++ * ++ * A parsed URI reference. This is a struct containing the various fields ++ * as described in RFC 2396 but separated for further processing. ++ */ ++typedef struct URI { ++ char *scheme; /* the URI scheme */ ++ char *opaque; /* opaque part */ ++ char *authority; /* the authority part */ ++ char *server; /* the server part */ ++ char *user; /* the user part */ ++ int port; /* the port number */ ++ char *path; /* the path string */ ++ char *fragment; /* the fragment identifier */ ++ int cleanup; /* parsing potentially unclean URI */ ++ char *query; /* the query string (as it appears in the URI) */ ++} URI; ++ ++URI *uri_new(void); ++URI *uri_parse(const char *str); ++URI *uri_parse_raw(const char *str, int raw); ++int uri_parse_into(URI *uri, const char *str); ++char *uri_to_string(URI *uri); ++void uri_free(URI *uri); ++ ++/* Single web service query parameter 'name=value'. */ ++typedef struct QueryParam { ++ char *name; /* Name (unescaped). */ ++ char *value; /* Value (unescaped). */ ++ int ignore; /* Ignore this field in qparam_get_query */ ++} QueryParam; ++ ++/* Set of parameters. */ ++typedef struct QueryParams { ++ int n; /* number of parameters used */ ++ int alloc; /* allocated space */ ++ QueryParam *p; /* array of parameters */ ++} QueryParams; ++ ++QueryParams *query_params_new(int init_alloc); ++QueryParams *query_params_parse(const char *query); ++void query_params_free(QueryParams *ps); ++ ++#endif /* QEMU_URI_H */ +diff --git a/include/qemu/yank.h b/include/qemu/yank.h +new file mode 100644 +index 00000000..3d88af69 +--- /dev/null ++++ b/include/qemu/yank.h +@@ -0,0 +1,87 @@ ++/* ++ * QEMU yank feature ++ * ++ * Copyright (c) Lukas Straub ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef YANK_H ++#define YANK_H ++ ++#include "qapi/qapi-types-yank.h" ++ ++typedef void (YankFn)(void *opaque); ++ ++/** ++ * yank_register_instance: Register a new instance. ++ * ++ * This registers a new instance for yanking. Must be called before any yank ++ * function is registered for this instance. ++ * ++ * This function is thread-safe. ++ * ++ * @instance: The instance. ++ * @errp: Error object. ++ * ++ * Returns true on success or false if an error occurred. ++ */ ++bool yank_register_instance(const YankInstance *instance, Error **errp); ++ ++/** ++ * yank_unregister_instance: Unregister a instance. ++ * ++ * This unregisters a instance. Must be called only after every yank function ++ * of the instance has been unregistered. ++ * ++ * This function is thread-safe. ++ * ++ * @instance: The instance. ++ */ ++void yank_unregister_instance(const YankInstance *instance); ++ ++/** ++ * yank_register_function: Register a yank function ++ * ++ * This registers a yank function. All limitations of qmp oob commands apply ++ * to the yank function as well. See docs/devel/qapi-code-gen.rst under ++ * "An OOB-capable command handler must satisfy the following conditions". ++ * ++ * This function is thread-safe. ++ * ++ * @instance: The instance. ++ * @func: The yank function. ++ * @opaque: Will be passed to the yank function. ++ */ ++void yank_register_function(const YankInstance *instance, ++ YankFn *func, ++ void *opaque); ++ ++/** ++ * yank_unregister_function: Unregister a yank function ++ * ++ * This unregisters a yank function. ++ * ++ * This function is thread-safe. ++ * ++ * @instance: The instance. ++ * @func: func that was passed to yank_register_function. ++ * @opaque: opaque that was passed to yank_register_function. ++ */ ++void yank_unregister_function(const YankInstance *instance, ++ YankFn *func, ++ void *opaque); ++ ++#define BLOCKDEV_YANK_INSTANCE(the_node_name) (&(YankInstance) { \ ++ .type = YANK_INSTANCE_TYPE_BLOCK_NODE, \ ++ .u.block_node.node_name = (the_node_name) }) ++ ++#define CHARDEV_YANK_INSTANCE(the_id) (&(YankInstance) { \ ++ .type = YANK_INSTANCE_TYPE_CHARDEV, \ ++ .u.chardev.id = (the_id) }) ++ ++#define MIGRATION_YANK_INSTANCE (&(YankInstance) { \ ++ .type = YANK_INSTANCE_TYPE_MIGRATION }) ++ ++#endif +diff --git a/include/qom/object.h b/include/qom/object.h +new file mode 100644 +index 00000000..13d3a655 +--- /dev/null ++++ b/include/qom/object.h +@@ -0,0 +1,2058 @@ ++/* ++ * QEMU Object Model ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_OBJECT_H ++#define QEMU_OBJECT_H ++ ++#include "qapi/qapi-builtin-types.h" ++#include "qemu/module.h" ++ ++struct TypeImpl; ++typedef struct TypeImpl *Type; ++ ++typedef struct TypeInfo TypeInfo; ++ ++typedef struct InterfaceClass InterfaceClass; ++typedef struct InterfaceInfo InterfaceInfo; ++ ++#define TYPE_OBJECT "object" ++ ++typedef struct ObjectProperty ObjectProperty; ++ ++/** ++ * typedef ObjectPropertyAccessor: ++ * @obj: the object that owns the property ++ * @v: the visitor that contains the property data ++ * @name: the name of the property ++ * @opaque: the object property opaque ++ * @errp: a pointer to an Error that is filled if getting/setting fails. ++ * ++ * Called when trying to get/set a property. ++ */ ++typedef void (ObjectPropertyAccessor)(Object *obj, ++ Visitor *v, ++ const char *name, ++ void *opaque, ++ Error **errp); ++ ++/** ++ * typedef ObjectPropertyResolve: ++ * @obj: the object that owns the property ++ * @opaque: the opaque registered with the property ++ * @part: the name of the property ++ * ++ * Resolves the #Object corresponding to property @part. ++ * ++ * The returned object can also be used as a starting point ++ * to resolve a relative path starting with "@part". ++ * ++ * Returns: If @path is the path that led to @obj, the function ++ * returns the #Object corresponding to "@path/@part". ++ * If "@path/@part" is not a valid object path, it returns #NULL. ++ */ ++typedef Object *(ObjectPropertyResolve)(Object *obj, ++ void *opaque, ++ const char *part); ++ ++/** ++ * typedef ObjectPropertyRelease: ++ * @obj: the object that owns the property ++ * @name: the name of the property ++ * @opaque: the opaque registered with the property ++ * ++ * Called when a property is removed from a object. ++ */ ++typedef void (ObjectPropertyRelease)(Object *obj, ++ const char *name, ++ void *opaque); ++ ++/** ++ * typedef ObjectPropertyInit: ++ * @obj: the object that owns the property ++ * @prop: the property to set ++ * ++ * Called when a property is initialized. ++ */ ++typedef void (ObjectPropertyInit)(Object *obj, ObjectProperty *prop); ++ ++struct ObjectProperty ++{ ++ char *name; ++ char *type; ++ char *description; ++ ObjectPropertyAccessor *get; ++ ObjectPropertyAccessor *set; ++ ObjectPropertyResolve *resolve; ++ ObjectPropertyRelease *release; ++ ObjectPropertyInit *init; ++ void *opaque; ++ QObject *defval; ++}; ++ ++/** ++ * typedef ObjectUnparent: ++ * @obj: the object that is being removed from the composition tree ++ * ++ * Called when an object is being removed from the QOM composition tree. ++ * The function should remove any backlinks from children objects to @obj. ++ */ ++typedef void (ObjectUnparent)(Object *obj); ++ ++/** ++ * typedef ObjectFree: ++ * @obj: the object being freed ++ * ++ * Called when an object's last reference is removed. ++ */ ++typedef void (ObjectFree)(void *obj); ++ ++#define OBJECT_CLASS_CAST_CACHE 4 ++ ++/** ++ * struct ObjectClass: ++ * ++ * The base for all classes. The only thing that #ObjectClass contains is an ++ * integer type handle. ++ */ ++struct ObjectClass ++{ ++ /* private: */ ++ Type type; ++ GSList *interfaces; ++ ++ const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE]; ++ const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE]; ++ ++ ObjectUnparent *unparent; ++ ++ GHashTable *properties; ++}; ++ ++/** ++ * struct Object: ++ * ++ * The base for all objects. The first member of this object is a pointer to ++ * a #ObjectClass. Since C guarantees that the first member of a structure ++ * always begins at byte 0 of that structure, as long as any sub-object places ++ * its parent as the first member, we can cast directly to a #Object. ++ * ++ * As a result, #Object contains a reference to the objects type as its ++ * first member. This allows identification of the real type of the object at ++ * run time. ++ */ ++struct Object ++{ ++ /* private: */ ++ ObjectClass *class; ++ ObjectFree *free; ++ GHashTable *properties; ++ uint32_t ref; ++ Object *parent; ++}; ++ ++/** ++ * DECLARE_INSTANCE_CHECKER: ++ * @InstanceType: instance struct name ++ * @OBJ_NAME: the object name in uppercase with underscore separators ++ * @TYPENAME: type name ++ * ++ * Direct usage of this macro should be avoided, and the complete ++ * OBJECT_DECLARE_TYPE macro is recommended instead. ++ * ++ * This macro will provide the instance type cast functions for a ++ * QOM type. ++ */ ++#define DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \ ++ static inline G_GNUC_UNUSED InstanceType * \ ++ OBJ_NAME(const void *obj) \ ++ { return OBJECT_CHECK(InstanceType, obj, TYPENAME); } ++ ++/** ++ * DECLARE_CLASS_CHECKERS: ++ * @ClassType: class struct name ++ * @OBJ_NAME: the object name in uppercase with underscore separators ++ * @TYPENAME: type name ++ * ++ * Direct usage of this macro should be avoided, and the complete ++ * OBJECT_DECLARE_TYPE macro is recommended instead. ++ * ++ * This macro will provide the class type cast functions for a ++ * QOM type. ++ */ ++#define DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME) \ ++ static inline G_GNUC_UNUSED ClassType * \ ++ OBJ_NAME##_GET_CLASS(const void *obj) \ ++ { return OBJECT_GET_CLASS(ClassType, obj, TYPENAME); } \ ++ \ ++ static inline G_GNUC_UNUSED ClassType * \ ++ OBJ_NAME##_CLASS(const void *klass) \ ++ { return OBJECT_CLASS_CHECK(ClassType, klass, TYPENAME); } ++ ++/** ++ * DECLARE_OBJ_CHECKERS: ++ * @InstanceType: instance struct name ++ * @ClassType: class struct name ++ * @OBJ_NAME: the object name in uppercase with underscore separators ++ * @TYPENAME: type name ++ * ++ * Direct usage of this macro should be avoided, and the complete ++ * OBJECT_DECLARE_TYPE macro is recommended instead. ++ * ++ * This macro will provide the three standard type cast functions for a ++ * QOM type. ++ */ ++#define DECLARE_OBJ_CHECKERS(InstanceType, ClassType, OBJ_NAME, TYPENAME) \ ++ DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \ ++ \ ++ DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME) ++ ++/** ++ * OBJECT_DECLARE_TYPE: ++ * @InstanceType: instance struct name ++ * @ClassType: class struct name ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * ++ * This macro is typically used in a header file, and will: ++ * ++ * - create the typedefs for the object and class structs ++ * - register the type for use with g_autoptr ++ * - provide three standard type cast functions ++ * ++ * The object struct and class struct need to be declared manually. ++ */ ++#define OBJECT_DECLARE_TYPE(InstanceType, ClassType, MODULE_OBJ_NAME) \ ++ typedef struct InstanceType InstanceType; \ ++ typedef struct ClassType ClassType; \ ++ \ ++ G_DEFINE_AUTOPTR_CLEANUP_FUNC(InstanceType, object_unref) \ ++ \ ++ DECLARE_OBJ_CHECKERS(InstanceType, ClassType, \ ++ MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME) ++ ++/** ++ * OBJECT_DECLARE_SIMPLE_TYPE: ++ * @InstanceType: instance struct name ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * ++ * This does the same as OBJECT_DECLARE_TYPE(), but with no class struct ++ * declared. ++ * ++ * This macro should be used unless the class struct needs to have ++ * virtual methods declared. ++ */ ++#define OBJECT_DECLARE_SIMPLE_TYPE(InstanceType, MODULE_OBJ_NAME) \ ++ typedef struct InstanceType InstanceType; \ ++ \ ++ G_DEFINE_AUTOPTR_CLEANUP_FUNC(InstanceType, object_unref) \ ++ \ ++ DECLARE_INSTANCE_CHECKER(InstanceType, MODULE_OBJ_NAME, TYPE_##MODULE_OBJ_NAME) ++ ++ ++/** ++ * DO_OBJECT_DEFINE_TYPE_EXTENDED: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * @ABSTRACT: boolean flag to indicate whether the object can be instantiated ++ * @CLASS_SIZE: size of the type's class ++ * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces ++ * ++ * This is the base macro used to implement all the OBJECT_DEFINE_* ++ * macros. It should never be used directly in a source file. ++ */ ++#define DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, \ ++ PARENT_MODULE_OBJ_NAME, \ ++ ABSTRACT, CLASS_SIZE, ...) \ ++ static void \ ++ module_obj_name##_finalize(Object *obj); \ ++ static void \ ++ module_obj_name##_class_init(ObjectClass *oc, void *data); \ ++ static void \ ++ module_obj_name##_init(Object *obj); \ ++ \ ++ static const TypeInfo module_obj_name##_info = { \ ++ .parent = TYPE_##PARENT_MODULE_OBJ_NAME, \ ++ .name = TYPE_##MODULE_OBJ_NAME, \ ++ .instance_size = sizeof(ModuleObjName), \ ++ .instance_align = __alignof__(ModuleObjName), \ ++ .instance_init = module_obj_name##_init, \ ++ .instance_finalize = module_obj_name##_finalize, \ ++ .class_size = CLASS_SIZE, \ ++ .class_init = module_obj_name##_class_init, \ ++ .abstract = ABSTRACT, \ ++ .interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \ ++ }; \ ++ \ ++ static void \ ++ module_obj_name##_register_types(void) \ ++ { \ ++ type_register_static(&module_obj_name##_info); \ ++ } \ ++ type_init(module_obj_name##_register_types); ++ ++/** ++ * OBJECT_DEFINE_TYPE_EXTENDED: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * @ABSTRACT: boolean flag to indicate whether the object can be instantiated ++ * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces ++ * ++ * This macro is typically used in a source file, and will: ++ * ++ * - declare prototypes for _finalize, _class_init and _init methods ++ * - declare the TypeInfo struct instance ++ * - provide the constructor to register the type ++ * ++ * After using this macro, implementations of the _finalize, _class_init, ++ * and _init methods need to be written. Any of these can be zero-line ++ * no-op impls if no special logic is required for a given type. ++ * ++ * This macro should rarely be used, instead one of the more specialized ++ * macros is usually a better choice. ++ */ ++#define OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ ABSTRACT, ...) \ ++ DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ ABSTRACT, sizeof(ModuleObjName##Class), \ ++ __VA_ARGS__) ++ ++/** ++ * OBJECT_DEFINE_TYPE: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * ++ * This is a specialization of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable ++ * for the common case of a non-abstract type, without any interfaces. ++ */ ++#define OBJECT_DEFINE_TYPE(ModuleObjName, module_obj_name, MODULE_OBJ_NAME, \ ++ PARENT_MODULE_OBJ_NAME) \ ++ OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ false, { NULL }) ++ ++/** ++ * OBJECT_DEFINE_TYPE_WITH_INTERFACES: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * @...: list of initializers for "InterfaceInfo" to declare implemented interfaces ++ * ++ * This is a specialization of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable ++ * for the common case of a non-abstract type, with one or more implemented ++ * interfaces. ++ * ++ * Note when passing the list of interfaces, be sure to include the final ++ * NULL entry, e.g. { TYPE_USER_CREATABLE }, { NULL } ++ */ ++#define OBJECT_DEFINE_TYPE_WITH_INTERFACES(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, \ ++ PARENT_MODULE_OBJ_NAME, ...) \ ++ OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ false, __VA_ARGS__) ++ ++/** ++ * OBJECT_DEFINE_ABSTRACT_TYPE: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * ++ * This is a specialization of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable ++ * for defining an abstract type, without any interfaces. ++ */ ++#define OBJECT_DEFINE_ABSTRACT_TYPE(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME) \ ++ OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ true, { NULL }) ++ ++/** ++ * OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * ++ * This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for ++ * the case of a non-abstract type, with interfaces, and with no requirement ++ * for a class struct. ++ */ ++#define OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, \ ++ module_obj_name, \ ++ MODULE_OBJ_NAME, \ ++ PARENT_MODULE_OBJ_NAME, ...) \ ++ DO_OBJECT_DEFINE_TYPE_EXTENDED(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, \ ++ false, 0, __VA_ARGS__) ++ ++/** ++ * OBJECT_DEFINE_SIMPLE_TYPE: ++ * @ModuleObjName: the object name with initial caps ++ * @module_obj_name: the object name in lowercase with underscore separators ++ * @MODULE_OBJ_NAME: the object name in uppercase with underscore separators ++ * @PARENT_MODULE_OBJ_NAME: the parent object name in uppercase with underscore ++ * separators ++ * ++ * This is a variant of OBJECT_DEFINE_TYPE_EXTENDED, which is suitable for ++ * the common case of a non-abstract type, without any interfaces, and with ++ * no requirement for a class struct. If you declared your type with ++ * OBJECT_DECLARE_SIMPLE_TYPE then this is probably the right choice for ++ * defining it. ++ */ ++#define OBJECT_DEFINE_SIMPLE_TYPE(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME) \ ++ OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ModuleObjName, module_obj_name, \ ++ MODULE_OBJ_NAME, PARENT_MODULE_OBJ_NAME, { NULL }) ++ ++/** ++ * struct TypeInfo: ++ * @name: The name of the type. ++ * @parent: The name of the parent type. ++ * @instance_size: The size of the object (derivative of #Object). If ++ * @instance_size is 0, then the size of the object will be the size of the ++ * parent object. ++ * @instance_align: The required alignment of the object. If @instance_align ++ * is 0, then normal malloc alignment is sufficient; if non-zero, then we ++ * must use qemu_memalign for allocation. ++ * @instance_init: This function is called to initialize an object. The parent ++ * class will have already been initialized so the type is only responsible ++ * for initializing its own members. ++ * @instance_post_init: This function is called to finish initialization of ++ * an object, after all @instance_init functions were called. ++ * @instance_finalize: This function is called during object destruction. This ++ * is called before the parent @instance_finalize function has been called. ++ * An object should only free the members that are unique to its type in this ++ * function. ++ * @abstract: If this field is true, then the class is considered abstract and ++ * cannot be directly instantiated. ++ * @class_size: The size of the class object (derivative of #ObjectClass) ++ * for this object. If @class_size is 0, then the size of the class will be ++ * assumed to be the size of the parent class. This allows a type to avoid ++ * implementing an explicit class type if they are not adding additional ++ * virtual functions. ++ * @class_init: This function is called after all parent class initialization ++ * has occurred to allow a class to set its default virtual method pointers. ++ * This is also the function to use to override virtual methods from a parent ++ * class. ++ * @class_base_init: This function is called for all base classes after all ++ * parent class initialization has occurred, but before the class itself ++ * is initialized. This is the function to use to undo the effects of ++ * memcpy from the parent class to the descendants. ++ * @class_data: Data to pass to the @class_init, ++ * @class_base_init. This can be useful when building dynamic ++ * classes. ++ * @interfaces: The list of interfaces associated with this type. This ++ * should point to a static array that's terminated with a zero filled ++ * element. ++ */ ++struct TypeInfo ++{ ++ const char *name; ++ const char *parent; ++ ++ size_t instance_size; ++ size_t instance_align; ++ void (*instance_init)(Object *obj); ++ void (*instance_post_init)(Object *obj); ++ void (*instance_finalize)(Object *obj); ++ ++ bool abstract; ++ size_t class_size; ++ ++ void (*class_init)(ObjectClass *klass, void *data); ++ void (*class_base_init)(ObjectClass *klass, void *data); ++ void *class_data; ++ ++ InterfaceInfo *interfaces; ++}; ++ ++/** ++ * OBJECT: ++ * @obj: A derivative of #Object ++ * ++ * Converts an object to a #Object. Since all objects are #Objects, ++ * this function will always succeed. ++ */ ++#define OBJECT(obj) \ ++ ((Object *)(obj)) ++ ++/** ++ * OBJECT_CLASS: ++ * @class: A derivative of #ObjectClass. ++ * ++ * Converts a class to an #ObjectClass. Since all objects are #Objects, ++ * this function will always succeed. ++ */ ++#define OBJECT_CLASS(class) \ ++ ((ObjectClass *)(class)) ++ ++/** ++ * OBJECT_CHECK: ++ * @type: The C type to use for the return value. ++ * @obj: A derivative of @type to cast. ++ * @name: The QOM typename of @type ++ * ++ * A type safe version of @object_dynamic_cast_assert. Typically each class ++ * will define a macro based on this type to perform type safe dynamic_casts to ++ * this object type. ++ * ++ * If an invalid object is passed to this function, a run time assert will be ++ * generated. ++ */ ++#define OBJECT_CHECK(type, obj, name) \ ++ ((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \ ++ __FILE__, __LINE__, __func__)) ++ ++/** ++ * OBJECT_CLASS_CHECK: ++ * @class_type: The C type to use for the return value. ++ * @class: A derivative class of @class_type to cast. ++ * @name: the QOM typename of @class_type. ++ * ++ * A type safe version of @object_class_dynamic_cast_assert. This macro is ++ * typically wrapped by each type to perform type safe casts of a class to a ++ * specific class type. ++ */ ++#define OBJECT_CLASS_CHECK(class_type, class, name) \ ++ ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \ ++ __FILE__, __LINE__, __func__)) ++ ++/** ++ * OBJECT_GET_CLASS: ++ * @class: The C type to use for the return value. ++ * @obj: The object to obtain the class for. ++ * @name: The QOM typename of @obj. ++ * ++ * This function will return a specific class for a given object. Its generally ++ * used by each type to provide a type safe macro to get a specific class type ++ * from an object. ++ */ ++#define OBJECT_GET_CLASS(class, obj, name) \ ++ OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name) ++ ++/** ++ * struct InterfaceInfo: ++ * @type: The name of the interface. ++ * ++ * The information associated with an interface. ++ */ ++struct InterfaceInfo { ++ const char *type; ++}; ++ ++/** ++ * struct InterfaceClass: ++ * @parent_class: the base class ++ * ++ * The class for all interfaces. Subclasses of this class should only add ++ * virtual methods. ++ */ ++struct InterfaceClass ++{ ++ ObjectClass parent_class; ++ /* private: */ ++ ObjectClass *concrete_class; ++ Type interface_type; ++}; ++ ++#define TYPE_INTERFACE "interface" ++ ++/** ++ * INTERFACE_CLASS: ++ * @klass: class to cast from ++ * Returns: An #InterfaceClass or raise an error if cast is invalid ++ */ ++#define INTERFACE_CLASS(klass) \ ++ OBJECT_CLASS_CHECK(InterfaceClass, klass, TYPE_INTERFACE) ++ ++/** ++ * INTERFACE_CHECK: ++ * @interface: the type to return ++ * @obj: the object to convert to an interface ++ * @name: the interface type name ++ * ++ * Returns: @obj casted to @interface if cast is valid, otherwise raise error. ++ */ ++#define INTERFACE_CHECK(interface, obj, name) \ ++ ((interface *)object_dynamic_cast_assert(OBJECT((obj)), (name), \ ++ __FILE__, __LINE__, __func__)) ++ ++/** ++ * object_new_with_class: ++ * @klass: The class to instantiate. ++ * ++ * This function will initialize a new object using heap allocated memory. ++ * The returned object has a reference count of 1, and will be freed when ++ * the last reference is dropped. ++ * ++ * Returns: The newly allocated and instantiated object. ++ */ ++Object *object_new_with_class(ObjectClass *klass); ++ ++/** ++ * object_new: ++ * @typename: The name of the type of the object to instantiate. ++ * ++ * This function will initialize a new object using heap allocated memory. ++ * The returned object has a reference count of 1, and will be freed when ++ * the last reference is dropped. ++ * ++ * Returns: The newly allocated and instantiated object. ++ */ ++Object *object_new(const char *typename); ++ ++/** ++ * object_new_with_props: ++ * @typename: The name of the type of the object to instantiate. ++ * @parent: the parent object ++ * @id: The unique ID of the object ++ * @errp: pointer to error object ++ * @...: list of property names and values ++ * ++ * This function will initialize a new object using heap allocated memory. ++ * The returned object has a reference count of 1, and will be freed when ++ * the last reference is dropped. ++ * ++ * The @id parameter will be used when registering the object as a ++ * child of @parent in the composition tree. ++ * ++ * The variadic parameters are a list of pairs of (propname, propvalue) ++ * strings. The propname of %NULL indicates the end of the property ++ * list. If the object implements the user creatable interface, the ++ * object will be marked complete once all the properties have been ++ * processed. ++ * ++ * .. code-block:: c ++ * :caption: Creating an object with properties ++ * ++ * Error *err = NULL; ++ * Object *obj; ++ * ++ * obj = object_new_with_props(TYPE_MEMORY_BACKEND_FILE, ++ * object_get_objects_root(), ++ * "hostmem0", ++ * &err, ++ * "share", "yes", ++ * "mem-path", "/dev/shm/somefile", ++ * "prealloc", "yes", ++ * "size", "1048576", ++ * NULL); ++ * ++ * if (!obj) { ++ * error_reportf_err(err, "Cannot create memory backend: "); ++ * } ++ * ++ * The returned object will have one stable reference maintained ++ * for as long as it is present in the object hierarchy. ++ * ++ * Returns: The newly allocated, instantiated & initialized object. ++ */ ++Object *object_new_with_props(const char *typename, ++ Object *parent, ++ const char *id, ++ Error **errp, ++ ...) G_GNUC_NULL_TERMINATED; ++ ++/** ++ * object_new_with_propv: ++ * @typename: The name of the type of the object to instantiate. ++ * @parent: the parent object ++ * @id: The unique ID of the object ++ * @errp: pointer to error object ++ * @vargs: list of property names and values ++ * ++ * See object_new_with_props() for documentation. ++ */ ++Object *object_new_with_propv(const char *typename, ++ Object *parent, ++ const char *id, ++ Error **errp, ++ va_list vargs); ++ ++bool object_apply_global_props(Object *obj, const GPtrArray *props, ++ Error **errp); ++void object_set_machine_compat_props(GPtrArray *compat_props); ++void object_set_accelerator_compat_props(GPtrArray *compat_props); ++void object_register_sugar_prop(const char *driver, const char *prop, ++ const char *value, bool optional); ++void object_apply_compat_props(Object *obj); ++ ++/** ++ * object_set_props: ++ * @obj: the object instance to set properties on ++ * @errp: pointer to error object ++ * @...: list of property names and values ++ * ++ * This function will set a list of properties on an existing object ++ * instance. ++ * ++ * The variadic parameters are a list of pairs of (propname, propvalue) ++ * strings. The propname of %NULL indicates the end of the property ++ * list. ++ * ++ * .. code-block:: c ++ * :caption: Update an object's properties ++ * ++ * Error *err = NULL; ++ * Object *obj = ...get / create object...; ++ * ++ * if (!object_set_props(obj, ++ * &err, ++ * "share", "yes", ++ * "mem-path", "/dev/shm/somefile", ++ * "prealloc", "yes", ++ * "size", "1048576", ++ * NULL)) { ++ * error_reportf_err(err, "Cannot set properties: "); ++ * } ++ * ++ * The returned object will have one stable reference maintained ++ * for as long as it is present in the object hierarchy. ++ * ++ * Returns: %true on success, %false on error. ++ */ ++bool object_set_props(Object *obj, Error **errp, ...) G_GNUC_NULL_TERMINATED; ++ ++/** ++ * object_set_propv: ++ * @obj: the object instance to set properties on ++ * @errp: pointer to error object ++ * @vargs: list of property names and values ++ * ++ * See object_set_props() for documentation. ++ * ++ * Returns: %true on success, %false on error. ++ */ ++bool object_set_propv(Object *obj, Error **errp, va_list vargs); ++ ++/** ++ * object_initialize: ++ * @obj: A pointer to the memory to be used for the object. ++ * @size: The maximum size available at @obj for the object. ++ * @typename: The name of the type of the object to instantiate. ++ * ++ * This function will initialize an object. The memory for the object should ++ * have already been allocated. The returned object has a reference count of 1, ++ * and will be finalized when the last reference is dropped. ++ */ ++void object_initialize(void *obj, size_t size, const char *typename); ++ ++/** ++ * object_initialize_child_with_props: ++ * @parentobj: The parent object to add a property to ++ * @propname: The name of the property ++ * @childobj: A pointer to the memory to be used for the object. ++ * @size: The maximum size available at @childobj for the object. ++ * @type: The name of the type of the object to instantiate. ++ * @errp: If an error occurs, a pointer to an area to store the error ++ * @...: list of property names and values ++ * ++ * This function will initialize an object. The memory for the object should ++ * have already been allocated. The object will then be added as child property ++ * to a parent with object_property_add_child() function. The returned object ++ * has a reference count of 1 (for the "child<...>" property from the parent), ++ * so the object will be finalized automatically when the parent gets removed. ++ * ++ * The variadic parameters are a list of pairs of (propname, propvalue) ++ * strings. The propname of %NULL indicates the end of the property list. ++ * If the object implements the user creatable interface, the object will ++ * be marked complete once all the properties have been processed. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_initialize_child_with_props(Object *parentobj, ++ const char *propname, ++ void *childobj, size_t size, const char *type, ++ Error **errp, ...) G_GNUC_NULL_TERMINATED; ++ ++/** ++ * object_initialize_child_with_propsv: ++ * @parentobj: The parent object to add a property to ++ * @propname: The name of the property ++ * @childobj: A pointer to the memory to be used for the object. ++ * @size: The maximum size available at @childobj for the object. ++ * @type: The name of the type of the object to instantiate. ++ * @errp: If an error occurs, a pointer to an area to store the error ++ * @vargs: list of property names and values ++ * ++ * See object_initialize_child() for documentation. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_initialize_child_with_propsv(Object *parentobj, ++ const char *propname, ++ void *childobj, size_t size, const char *type, ++ Error **errp, va_list vargs); ++ ++/** ++ * object_initialize_child: ++ * @parent: The parent object to add a property to ++ * @propname: The name of the property ++ * @child: A precisely typed pointer to the memory to be used for the ++ * object. ++ * @type: The name of the type of the object to instantiate. ++ * ++ * This is like:: ++ * ++ * object_initialize_child_with_props(parent, propname, ++ * child, sizeof(*child), type, ++ * &error_abort, NULL) ++ */ ++#define object_initialize_child(parent, propname, child, type) \ ++ object_initialize_child_internal((parent), (propname), \ ++ (child), sizeof(*(child)), (type)) ++void object_initialize_child_internal(Object *parent, const char *propname, ++ void *child, size_t size, ++ const char *type); ++ ++/** ++ * object_dynamic_cast: ++ * @obj: The object to cast. ++ * @typename: The @typename to cast to. ++ * ++ * This function will determine if @obj is-a @typename. @obj can refer to an ++ * object or an interface associated with an object. ++ * ++ * Returns: This function returns @obj on success or #NULL on failure. ++ */ ++Object *object_dynamic_cast(Object *obj, const char *typename); ++ ++/** ++ * object_dynamic_cast_assert: ++ * @obj: The object to cast. ++ * @typename: The @typename to cast to. ++ * @file: Source code file where function was called ++ * @line: Source code line where function was called ++ * @func: Name of function where this function was called ++ * ++ * See object_dynamic_cast() for a description of the parameters of this ++ * function. The only difference in behavior is that this function asserts ++ * instead of returning #NULL on failure if QOM cast debugging is enabled. ++ * This function is not meant to be called directly, but only through ++ * the wrapper macro OBJECT_CHECK. ++ */ ++Object *object_dynamic_cast_assert(Object *obj, const char *typename, ++ const char *file, int line, const char *func); ++ ++/** ++ * object_get_class: ++ * @obj: A derivative of #Object ++ * ++ * Returns: The #ObjectClass of the type associated with @obj. ++ */ ++ObjectClass *object_get_class(Object *obj); ++ ++/** ++ * object_get_typename: ++ * @obj: A derivative of #Object. ++ * ++ * Returns: The QOM typename of @obj. ++ */ ++const char *object_get_typename(const Object *obj); ++ ++/** ++ * type_register_static: ++ * @info: The #TypeInfo of the new type. ++ * ++ * @info and all of the strings it points to should exist for the life time ++ * that the type is registered. ++ * ++ * Returns: the new #Type. ++ */ ++Type type_register_static(const TypeInfo *info); ++ ++/** ++ * type_register: ++ * @info: The #TypeInfo of the new type ++ * ++ * Unlike type_register_static(), this call does not require @info or its ++ * string members to continue to exist after the call returns. ++ * ++ * Returns: the new #Type. ++ */ ++Type type_register(const TypeInfo *info); ++ ++/** ++ * type_register_static_array: ++ * @infos: The array of the new type #TypeInfo structures. ++ * @nr_infos: number of entries in @infos ++ * ++ * @infos and all of the strings it points to should exist for the life time ++ * that the type is registered. ++ */ ++void type_register_static_array(const TypeInfo *infos, int nr_infos); ++ ++/** ++ * DEFINE_TYPES: ++ * @type_array: The array containing #TypeInfo structures to register ++ * ++ * @type_array should be static constant that exists for the life time ++ * that the type is registered. ++ */ ++#define DEFINE_TYPES(type_array) \ ++static void do_qemu_init_ ## type_array(void) \ ++{ \ ++ type_register_static_array(type_array, ARRAY_SIZE(type_array)); \ ++} \ ++type_init(do_qemu_init_ ## type_array) ++ ++/** ++ * type_print_class_properties: ++ * @type: a QOM class name ++ * ++ * Print the object's class properties to stdout or the monitor. ++ * Return whether an object was found. ++ */ ++bool type_print_class_properties(const char *type); ++ ++/** ++ * object_set_properties_from_keyval: ++ * @obj: a QOM object ++ * @qdict: a dictionary with the properties to be set ++ * @from_json: true if leaf values of @qdict are typed, false if they ++ * are strings ++ * @errp: pointer to error object ++ * ++ * For each key in the dictionary, parse the value string if needed, ++ * then set the corresponding property in @obj. ++ */ ++void object_set_properties_from_keyval(Object *obj, const QDict *qdict, ++ bool from_json, Error **errp); ++ ++/** ++ * object_class_dynamic_cast_assert: ++ * @klass: The #ObjectClass to attempt to cast. ++ * @typename: The QOM typename of the class to cast to. ++ * @file: Source code file where function was called ++ * @line: Source code line where function was called ++ * @func: Name of function where this function was called ++ * ++ * See object_class_dynamic_cast() for a description of the parameters ++ * of this function. The only difference in behavior is that this function ++ * asserts instead of returning #NULL on failure if QOM cast debugging is ++ * enabled. This function is not meant to be called directly, but only through ++ * the wrapper macro OBJECT_CLASS_CHECK. ++ */ ++ObjectClass *object_class_dynamic_cast_assert(ObjectClass *klass, ++ const char *typename, ++ const char *file, int line, ++ const char *func); ++ ++/** ++ * object_class_dynamic_cast: ++ * @klass: The #ObjectClass to attempt to cast. ++ * @typename: The QOM typename of the class to cast to. ++ * ++ * Returns: If @typename is a class, this function returns @klass if ++ * @typename is a subtype of @klass, else returns #NULL. ++ * ++ * If @typename is an interface, this function returns the interface ++ * definition for @klass if @klass implements it unambiguously; #NULL ++ * is returned if @klass does not implement the interface or if multiple ++ * classes or interfaces on the hierarchy leading to @klass implement ++ * it. (FIXME: perhaps this can be detected at type definition time?) ++ */ ++ObjectClass *object_class_dynamic_cast(ObjectClass *klass, ++ const char *typename); ++ ++/** ++ * object_class_get_parent: ++ * @klass: The class to obtain the parent for. ++ * ++ * Returns: The parent for @klass or %NULL if none. ++ */ ++ObjectClass *object_class_get_parent(ObjectClass *klass); ++ ++/** ++ * object_class_get_name: ++ * @klass: The class to obtain the QOM typename for. ++ * ++ * Returns: The QOM typename for @klass. ++ */ ++const char *object_class_get_name(ObjectClass *klass); ++ ++/** ++ * object_class_is_abstract: ++ * @klass: The class to obtain the abstractness for. ++ * ++ * Returns: %true if @klass is abstract, %false otherwise. ++ */ ++bool object_class_is_abstract(ObjectClass *klass); ++ ++/** ++ * object_class_by_name: ++ * @typename: The QOM typename to obtain the class for. ++ * ++ * Returns: The class for @typename or %NULL if not found. ++ */ ++ObjectClass *object_class_by_name(const char *typename); ++ ++/** ++ * module_object_class_by_name: ++ * @typename: The QOM typename to obtain the class for. ++ * ++ * For objects which might be provided by a module. Behaves like ++ * object_class_by_name, but additionally tries to load the module ++ * needed in case the class is not available. ++ * ++ * Returns: The class for @typename or %NULL if not found. ++ */ ++ObjectClass *module_object_class_by_name(const char *typename); ++ ++void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque), ++ const char *implements_type, bool include_abstract, ++ void *opaque); ++ ++/** ++ * object_class_get_list: ++ * @implements_type: The type to filter for, including its derivatives. ++ * @include_abstract: Whether to include abstract classes. ++ * ++ * Returns: A singly-linked list of the classes in reverse hashtable order. ++ */ ++GSList *object_class_get_list(const char *implements_type, ++ bool include_abstract); ++ ++/** ++ * object_class_get_list_sorted: ++ * @implements_type: The type to filter for, including its derivatives. ++ * @include_abstract: Whether to include abstract classes. ++ * ++ * Returns: A singly-linked list of the classes in alphabetical ++ * case-insensitive order. ++ */ ++GSList *object_class_get_list_sorted(const char *implements_type, ++ bool include_abstract); ++ ++/** ++ * object_ref: ++ * @obj: the object ++ * ++ * Increase the reference count of a object. A object cannot be freed as long ++ * as its reference count is greater than zero. ++ * Returns: @obj ++ */ ++Object *object_ref(void *obj); ++ ++/** ++ * object_unref: ++ * @obj: the object ++ * ++ * Decrease the reference count of a object. A object cannot be freed as long ++ * as its reference count is greater than zero. ++ */ ++void object_unref(void *obj); ++ ++/** ++ * object_property_try_add: ++ * @obj: the object to add a property to ++ * @name: the name of the property. This can contain any character except for ++ * a forward slash. In general, you should use hyphens '-' instead of ++ * underscores '_' when naming properties. ++ * @type: the type name of the property. This namespace is pretty loosely ++ * defined. Sub namespaces are constructed by using a prefix and then ++ * to angle brackets. For instance, the type 'virtio-net-pci' in the ++ * 'link' namespace would be 'link'. ++ * @get: The getter to be called to read a property. If this is NULL, then ++ * the property cannot be read. ++ * @set: the setter to be called to write a property. If this is NULL, ++ * then the property cannot be written. ++ * @release: called when the property is removed from the object. This is ++ * meant to allow a property to free its opaque upon object ++ * destruction. This may be NULL. ++ * @opaque: an opaque pointer to pass to the callbacks for the property ++ * @errp: pointer to error object ++ * ++ * Returns: The #ObjectProperty; this can be used to set the @resolve ++ * callback for child and link properties. ++ */ ++ObjectProperty *object_property_try_add(Object *obj, const char *name, ++ const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque, Error **errp); ++ ++/** ++ * object_property_add: ++ * Same as object_property_try_add() with @errp hardcoded to ++ * &error_abort. ++ * ++ * @obj: the object to add a property to ++ * @name: the name of the property. This can contain any character except for ++ * a forward slash. In general, you should use hyphens '-' instead of ++ * underscores '_' when naming properties. ++ * @type: the type name of the property. This namespace is pretty loosely ++ * defined. Sub namespaces are constructed by using a prefix and then ++ * to angle brackets. For instance, the type 'virtio-net-pci' in the ++ * 'link' namespace would be 'link'. ++ * @get: The getter to be called to read a property. If this is NULL, then ++ * the property cannot be read. ++ * @set: the setter to be called to write a property. If this is NULL, ++ * then the property cannot be written. ++ * @release: called when the property is removed from the object. This is ++ * meant to allow a property to free its opaque upon object ++ * destruction. This may be NULL. ++ * @opaque: an opaque pointer to pass to the callbacks for the property ++ */ ++ObjectProperty *object_property_add(Object *obj, const char *name, ++ const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque); ++ ++void object_property_del(Object *obj, const char *name); ++ ++ObjectProperty *object_class_property_add(ObjectClass *klass, const char *name, ++ const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque); ++ ++/** ++ * object_property_set_default_bool: ++ * @prop: the property to set ++ * @value: the value to be written to the property ++ * ++ * Set the property default value. ++ */ ++void object_property_set_default_bool(ObjectProperty *prop, bool value); ++ ++/** ++ * object_property_set_default_str: ++ * @prop: the property to set ++ * @value: the value to be written to the property ++ * ++ * Set the property default value. ++ */ ++void object_property_set_default_str(ObjectProperty *prop, const char *value); ++ ++/** ++ * object_property_set_default_list: ++ * @prop: the property to set ++ * ++ * Set the property default value to be an empty list. ++ */ ++void object_property_set_default_list(ObjectProperty *prop); ++ ++/** ++ * object_property_set_default_int: ++ * @prop: the property to set ++ * @value: the value to be written to the property ++ * ++ * Set the property default value. ++ */ ++void object_property_set_default_int(ObjectProperty *prop, int64_t value); ++ ++/** ++ * object_property_set_default_uint: ++ * @prop: the property to set ++ * @value: the value to be written to the property ++ * ++ * Set the property default value. ++ */ ++void object_property_set_default_uint(ObjectProperty *prop, uint64_t value); ++ ++/** ++ * object_property_find: ++ * @obj: the object ++ * @name: the name of the property ++ * ++ * Look up a property for an object. ++ * ++ * Return its #ObjectProperty if found, or NULL. ++ */ ++ObjectProperty *object_property_find(Object *obj, const char *name); ++ ++/** ++ * object_property_find_err: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Look up a property for an object. ++ * ++ * Return its #ObjectProperty if found, or NULL. ++ */ ++ObjectProperty *object_property_find_err(Object *obj, ++ const char *name, ++ Error **errp); ++ ++/** ++ * object_class_property_find: ++ * @klass: the object class ++ * @name: the name of the property ++ * ++ * Look up a property for an object class. ++ * ++ * Return its #ObjectProperty if found, or NULL. ++ */ ++ObjectProperty *object_class_property_find(ObjectClass *klass, ++ const char *name); ++ ++/** ++ * object_class_property_find_err: ++ * @klass: the object class ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Look up a property for an object class. ++ * ++ * Return its #ObjectProperty if found, or NULL. ++ */ ++ObjectProperty *object_class_property_find_err(ObjectClass *klass, ++ const char *name, ++ Error **errp); ++ ++typedef struct ObjectPropertyIterator { ++ ObjectClass *nextclass; ++ GHashTableIter iter; ++} ObjectPropertyIterator; ++ ++/** ++ * object_property_iter_init: ++ * @iter: the iterator instance ++ * @obj: the object ++ * ++ * Initializes an iterator for traversing all properties ++ * registered against an object instance, its class and all parent classes. ++ * ++ * It is forbidden to modify the property list while iterating, ++ * whether removing or adding properties. ++ * ++ * Typical usage pattern would be ++ * ++ * .. code-block:: c ++ * :caption: Using object property iterators ++ * ++ * ObjectProperty *prop; ++ * ObjectPropertyIterator iter; ++ * ++ * object_property_iter_init(&iter, obj); ++ * while ((prop = object_property_iter_next(&iter))) { ++ * ... do something with prop ... ++ * } ++ */ ++void object_property_iter_init(ObjectPropertyIterator *iter, ++ Object *obj); ++ ++/** ++ * object_class_property_iter_init: ++ * @iter: the iterator instance ++ * @klass: the class ++ * ++ * Initializes an iterator for traversing all properties ++ * registered against an object class and all parent classes. ++ * ++ * It is forbidden to modify the property list while iterating, ++ * whether removing or adding properties. ++ * ++ * This can be used on abstract classes as it does not create a temporary ++ * instance. ++ */ ++void object_class_property_iter_init(ObjectPropertyIterator *iter, ++ ObjectClass *klass); ++ ++/** ++ * object_property_iter_next: ++ * @iter: the iterator instance ++ * ++ * Return the next available property. If no further properties ++ * are available, a %NULL value will be returned and the @iter ++ * pointer should not be used again after this point without ++ * re-initializing it. ++ * ++ * Returns: the next property, or %NULL when all properties ++ * have been traversed. ++ */ ++ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter); ++ ++void object_unparent(Object *obj); ++ ++/** ++ * object_property_get: ++ * @obj: the object ++ * @name: the name of the property ++ * @v: the visitor that will receive the property value. This should be an ++ * Output visitor and the data will be written with @name as the name. ++ * @errp: returns an error if this function fails ++ * ++ * Reads a property from a object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_get(Object *obj, const char *name, Visitor *v, ++ Error **errp); ++ ++/** ++ * object_property_set_str: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes a string value to a property. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_str(Object *obj, const char *name, ++ const char *value, Error **errp); ++ ++/** ++ * object_property_get_str: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to a C string, or NULL if ++ * an error occurs (including when the property value is not a string). ++ * The caller should free the string. ++ */ ++char *object_property_get_str(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_set_link: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes an object's canonical path to a property. ++ * ++ * If the link property was created with ++ * %OBJ_PROP_LINK_STRONG bit, the old target object is ++ * unreferenced, and a reference is added to the new target object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_link(Object *obj, const char *name, ++ Object *value, Error **errp); ++ ++/** ++ * object_property_get_link: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, resolved from a path to an Object, ++ * or NULL if an error occurs (including when the property value is not a ++ * string or not a valid object path). ++ */ ++Object *object_property_get_link(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_set_bool: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes a bool value to a property. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_bool(Object *obj, const char *name, ++ bool value, Error **errp); ++ ++/** ++ * object_property_get_bool: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to a boolean, or false if ++ * an error occurs (including when the property value is not a bool). ++ */ ++bool object_property_get_bool(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_set_int: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes an integer value to a property. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_int(Object *obj, const char *name, ++ int64_t value, Error **errp); ++ ++/** ++ * object_property_get_int: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to an integer, or -1 if ++ * an error occurs (including when the property value is not an integer). ++ */ ++int64_t object_property_get_int(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_set_uint: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: the value to be written to the property ++ * @errp: returns an error if this function fails ++ * ++ * Writes an unsigned integer value to a property. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_uint(Object *obj, const char *name, ++ uint64_t value, Error **errp); ++ ++/** ++ * object_property_get_uint: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to an unsigned integer, or 0 ++ * an error occurs (including when the property value is not an integer). ++ */ ++uint64_t object_property_get_uint(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_property_get_enum: ++ * @obj: the object ++ * @name: the name of the property ++ * @typename: the name of the enum data type ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to an integer (which ++ * can't be negative), or -1 on error (including when the property ++ * value is not an enum). ++ */ ++int object_property_get_enum(Object *obj, const char *name, ++ const char *typename, Error **errp); ++ ++/** ++ * object_property_set: ++ * @obj: the object ++ * @name: the name of the property ++ * @v: the visitor that will be used to write the property value. This should ++ * be an Input visitor and the data will be first read with @name as the ++ * name and then written as the property value. ++ * @errp: returns an error if this function fails ++ * ++ * Writes a property to a object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set(Object *obj, const char *name, Visitor *v, ++ Error **errp); ++ ++/** ++ * object_property_parse: ++ * @obj: the object ++ * @name: the name of the property ++ * @string: the string that will be used to parse the property value. ++ * @errp: returns an error if this function fails ++ * ++ * Parses a string and writes the result into a property of an object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_parse(Object *obj, const char *name, ++ const char *string, Error **errp); ++ ++/** ++ * object_property_print: ++ * @obj: the object ++ * @name: the name of the property ++ * @human: if true, print for human consumption ++ * @errp: returns an error if this function fails ++ * ++ * Returns a string representation of the value of the property. The ++ * caller shall free the string. ++ */ ++char *object_property_print(Object *obj, const char *name, bool human, ++ Error **errp); ++ ++/** ++ * object_property_get_type: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: The type name of the property. ++ */ ++const char *object_property_get_type(Object *obj, const char *name, ++ Error **errp); ++ ++/** ++ * object_get_root: ++ * ++ * Returns: the root object of the composition tree ++ */ ++Object *object_get_root(void); ++ ++ ++/** ++ * object_get_objects_root: ++ * ++ * Get the container object that holds user created ++ * object instances. This is the object at path ++ * "/objects" ++ * ++ * Returns: the user object container ++ */ ++Object *object_get_objects_root(void); ++ ++/** ++ * object_get_internal_root: ++ * ++ * Get the container object that holds internally used object ++ * instances. Any object which is put into this container must not be ++ * user visible, and it will not be exposed in the QOM tree. ++ * ++ * Returns: the internal object container ++ */ ++Object *object_get_internal_root(void); ++ ++/** ++ * object_get_canonical_path_component: ++ * @obj: the object ++ * ++ * Returns: The final component in the object's canonical path. The canonical ++ * path is the path within the composition tree starting from the root. ++ * %NULL if the object doesn't have a parent (and thus a canonical path). ++ */ ++const char *object_get_canonical_path_component(const Object *obj); ++ ++/** ++ * object_get_canonical_path: ++ * @obj: the object ++ * ++ * Returns: The canonical path for a object, newly allocated. This is ++ * the path within the composition tree starting from the root. Use ++ * g_free() to free it. ++ */ ++char *object_get_canonical_path(const Object *obj); ++ ++/** ++ * object_resolve_path: ++ * @path: the path to resolve ++ * @ambiguous: returns true if the path resolution failed because of an ++ * ambiguous match ++ * ++ * There are two types of supported paths--absolute paths and partial paths. ++ * ++ * Absolute paths are derived from the root object and can follow child<> or ++ * link<> properties. Since they can follow link<> properties, they can be ++ * arbitrarily long. Absolute paths look like absolute filenames and are ++ * prefixed with a leading slash. ++ * ++ * Partial paths look like relative filenames. They do not begin with a ++ * prefix. The matching rules for partial paths are subtle but designed to make ++ * specifying objects easy. At each level of the composition tree, the partial ++ * path is matched as an absolute path. The first match is not returned. At ++ * least two matches are searched for. A successful result is only returned if ++ * only one match is found. If more than one match is found, a flag is ++ * returned to indicate that the match was ambiguous. ++ * ++ * Returns: The matched object or NULL on path lookup failure. ++ */ ++Object *object_resolve_path(const char *path, bool *ambiguous); ++ ++/** ++ * object_resolve_path_type: ++ * @path: the path to resolve ++ * @typename: the type to look for. ++ * @ambiguous: returns true if the path resolution failed because of an ++ * ambiguous match ++ * ++ * This is similar to object_resolve_path. However, when looking for a ++ * partial path only matches that implement the given type are considered. ++ * This restricts the search and avoids spuriously flagging matches as ++ * ambiguous. ++ * ++ * For both partial and absolute paths, the return value goes through ++ * a dynamic cast to @typename. This is important if either the link, ++ * or the typename itself are of interface types. ++ * ++ * Returns: The matched object or NULL on path lookup failure. ++ */ ++Object *object_resolve_path_type(const char *path, const char *typename, ++ bool *ambiguous); ++ ++/** ++ * object_resolve_type_unambiguous: ++ * @typename: the type to look for ++ * @errp: pointer to error object ++ * ++ * Return the only object in the QOM tree of type @typename. ++ * If no match or more than one match is found, an error is ++ * returned. ++ * ++ * Returns: The matched object or NULL on path lookup failure. ++ */ ++Object *object_resolve_type_unambiguous(const char *typename, Error **errp); ++ ++/** ++ * object_resolve_path_at: ++ * @parent: the object in which to resolve the path ++ * @path: the path to resolve ++ * ++ * This is like object_resolve_path(), except paths not starting with ++ * a slash are relative to @parent. ++ * ++ * Returns: The resolved object or NULL on path lookup failure. ++ */ ++Object *object_resolve_path_at(Object *parent, const char *path); ++ ++/** ++ * object_resolve_path_component: ++ * @parent: the object in which to resolve the path ++ * @part: the component to resolve. ++ * ++ * This is similar to object_resolve_path with an absolute path, but it ++ * only resolves one element (@part) and takes the others from @parent. ++ * ++ * Returns: The resolved object or NULL on path lookup failure. ++ */ ++Object *object_resolve_path_component(Object *parent, const char *part); ++ ++/** ++ * object_property_try_add_child: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @child: the child object ++ * @errp: pointer to error object ++ * ++ * Child properties form the composition tree. All objects need to be a child ++ * of another object. Objects can only be a child of one object. ++ * ++ * There is no way for a child to determine what its parent is. It is not ++ * a bidirectional relationship. This is by design. ++ * ++ * The value of a child property as a C string will be the child object's ++ * canonical path. It can be retrieved using object_property_get_str(). ++ * The child object itself can be retrieved using object_property_get_link(). ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_try_add_child(Object *obj, const char *name, ++ Object *child, Error **errp); ++ ++/** ++ * object_property_add_child: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @child: the child object ++ * ++ * Same as object_property_try_add_child() with @errp hardcoded to ++ * &error_abort ++ */ ++ObjectProperty *object_property_add_child(Object *obj, const char *name, ++ Object *child); ++ ++typedef enum { ++ /* Unref the link pointer when the property is deleted */ ++ OBJ_PROP_LINK_STRONG = 0x1, ++ ++ /* private */ ++ OBJ_PROP_LINK_DIRECT = 0x2, ++ OBJ_PROP_LINK_CLASS = 0x4, ++} ObjectPropertyLinkFlags; ++ ++/** ++ * object_property_allow_set_link: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @child: the child object ++ * @errp: pointer to error object ++ * ++ * The default implementation of the object_property_add_link() check() ++ * callback function. It allows the link property to be set and never returns ++ * an error. ++ */ ++void object_property_allow_set_link(const Object *obj, const char *name, ++ Object *child, Error **errp); ++ ++/** ++ * object_property_add_link: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @type: the qobj type of the link ++ * @targetp: a pointer to where the link object reference is stored ++ * @check: callback to veto setting or NULL if the property is read-only ++ * @flags: additional options for the link ++ * ++ * Links establish relationships between objects. Links are unidirectional ++ * although two links can be combined to form a bidirectional relationship ++ * between objects. ++ * ++ * Links form the graph in the object model. ++ * ++ * The @check() callback is invoked when ++ * object_property_set_link() is called and can raise an error to prevent the ++ * link being set. If @check is NULL, the property is read-only ++ * and cannot be set. ++ * ++ * Ownership of the pointer that @child points to is transferred to the ++ * link property. The reference count for *@child is ++ * managed by the property from after the function returns till the ++ * property is deleted with object_property_del(). If the ++ * @flags %OBJ_PROP_LINK_STRONG bit is set, ++ * the reference count is decremented when the property is deleted or ++ * modified. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_link(Object *obj, const char *name, ++ const char *type, Object **targetp, ++ void (*check)(const Object *obj, const char *name, ++ Object *val, Error **errp), ++ ObjectPropertyLinkFlags flags); ++ ++ObjectProperty *object_class_property_add_link(ObjectClass *oc, ++ const char *name, ++ const char *type, ptrdiff_t offset, ++ void (*check)(const Object *obj, const char *name, ++ Object *val, Error **errp), ++ ObjectPropertyLinkFlags flags); ++ ++/** ++ * object_property_add_str: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @get: the getter or NULL if the property is write-only. This function must ++ * return a string to be freed by g_free(). ++ * @set: the setter or NULL if the property is read-only ++ * ++ * Add a string property using getters/setters. This function will add a ++ * property of type 'string'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_str(Object *obj, const char *name, ++ char *(*get)(Object *, Error **), ++ void (*set)(Object *, const char *, Error **)); ++ ++ObjectProperty *object_class_property_add_str(ObjectClass *klass, ++ const char *name, ++ char *(*get)(Object *, Error **), ++ void (*set)(Object *, const char *, ++ Error **)); ++ ++/** ++ * object_property_add_bool: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @get: the getter or NULL if the property is write-only. ++ * @set: the setter or NULL if the property is read-only ++ * ++ * Add a bool property using getters/setters. This function will add a ++ * property of type 'bool'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_bool(Object *obj, const char *name, ++ bool (*get)(Object *, Error **), ++ void (*set)(Object *, bool, Error **)); ++ ++ObjectProperty *object_class_property_add_bool(ObjectClass *klass, ++ const char *name, ++ bool (*get)(Object *, Error **), ++ void (*set)(Object *, bool, Error **)); ++ ++/** ++ * object_property_add_enum: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @typename: the name of the enum data type ++ * @lookup: enum value namelookup table ++ * @get: the getter or %NULL if the property is write-only. ++ * @set: the setter or %NULL if the property is read-only ++ * ++ * Add an enum property using getters/setters. This function will add a ++ * property of type '@typename'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_enum(Object *obj, const char *name, ++ const char *typename, ++ const QEnumLookup *lookup, ++ int (*get)(Object *, Error **), ++ void (*set)(Object *, int, Error **)); ++ ++ObjectProperty *object_class_property_add_enum(ObjectClass *klass, ++ const char *name, ++ const char *typename, ++ const QEnumLookup *lookup, ++ int (*get)(Object *, Error **), ++ void (*set)(Object *, int, Error **)); ++ ++/** ++ * object_property_add_tm: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @get: the getter or NULL if the property is write-only. ++ * ++ * Add a read-only struct tm valued property using a getter function. ++ * This function will add a property of type 'struct tm'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_tm(Object *obj, const char *name, ++ void (*get)(Object *, struct tm *, Error **)); ++ ++ObjectProperty *object_class_property_add_tm(ObjectClass *klass, ++ const char *name, ++ void (*get)(Object *, struct tm *, Error **)); ++ ++typedef enum { ++ /* Automatically add a getter to the property */ ++ OBJ_PROP_FLAG_READ = 1 << 0, ++ /* Automatically add a setter to the property */ ++ OBJ_PROP_FLAG_WRITE = 1 << 1, ++ /* Automatically add a getter and a setter to the property */ ++ OBJ_PROP_FLAG_READWRITE = (OBJ_PROP_FLAG_READ | OBJ_PROP_FLAG_WRITE), ++} ObjectPropertyFlags; ++ ++/** ++ * object_property_add_uint8_ptr: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @v: pointer to value ++ * @flags: bitwise-or'd ObjectPropertyFlags ++ * ++ * Add an integer property in memory. This function will add a ++ * property of type 'uint8'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_uint8_ptr(Object *obj, const char *name, ++ const uint8_t *v, ++ ObjectPropertyFlags flags); ++ ++ObjectProperty *object_class_property_add_uint8_ptr(ObjectClass *klass, ++ const char *name, ++ const uint8_t *v, ++ ObjectPropertyFlags flags); ++ ++/** ++ * object_property_add_uint16_ptr: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @v: pointer to value ++ * @flags: bitwise-or'd ObjectPropertyFlags ++ * ++ * Add an integer property in memory. This function will add a ++ * property of type 'uint16'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_uint16_ptr(Object *obj, const char *name, ++ const uint16_t *v, ++ ObjectPropertyFlags flags); ++ ++ObjectProperty *object_class_property_add_uint16_ptr(ObjectClass *klass, ++ const char *name, ++ const uint16_t *v, ++ ObjectPropertyFlags flags); ++ ++/** ++ * object_property_add_uint32_ptr: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @v: pointer to value ++ * @flags: bitwise-or'd ObjectPropertyFlags ++ * ++ * Add an integer property in memory. This function will add a ++ * property of type 'uint32'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_uint32_ptr(Object *obj, const char *name, ++ const uint32_t *v, ++ ObjectPropertyFlags flags); ++ ++ObjectProperty *object_class_property_add_uint32_ptr(ObjectClass *klass, ++ const char *name, ++ const uint32_t *v, ++ ObjectPropertyFlags flags); ++ ++/** ++ * object_property_add_uint64_ptr: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @v: pointer to value ++ * @flags: bitwise-or'd ObjectPropertyFlags ++ * ++ * Add an integer property in memory. This function will add a ++ * property of type 'uint64'. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_uint64_ptr(Object *obj, const char *name, ++ const uint64_t *v, ++ ObjectPropertyFlags flags); ++ ++ObjectProperty *object_class_property_add_uint64_ptr(ObjectClass *klass, ++ const char *name, ++ const uint64_t *v, ++ ObjectPropertyFlags flags); ++ ++/** ++ * object_property_add_alias: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @target_obj: the object to forward property access to ++ * @target_name: the name of the property on the forwarded object ++ * ++ * Add an alias for a property on an object. This function will add a property ++ * of the same type as the forwarded property. ++ * ++ * The caller must ensure that @target_obj stays alive as long as ++ * this property exists. In the case of a child object or an alias on the same ++ * object this will be the case. For aliases to other objects the caller is ++ * responsible for taking a reference. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_alias(Object *obj, const char *name, ++ Object *target_obj, const char *target_name); ++ ++/** ++ * object_property_add_const_link: ++ * @obj: the object to add a property to ++ * @name: the name of the property ++ * @target: the object to be referred by the link ++ * ++ * Add an unmodifiable link for a property on an object. This function will ++ * add a property of type link where TYPE is the type of @target. ++ * ++ * The caller must ensure that @target stays alive as long as ++ * this property exists. In the case @target is a child of @obj, ++ * this will be the case. Otherwise, the caller is responsible for ++ * taking a reference. ++ * ++ * Returns: The newly added property on success, or %NULL on failure. ++ */ ++ObjectProperty *object_property_add_const_link(Object *obj, const char *name, ++ Object *target); ++ ++/** ++ * object_property_set_description: ++ * @obj: the object owning the property ++ * @name: the name of the property ++ * @description: the description of the property on the object ++ * ++ * Set an object property's description. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++void object_property_set_description(Object *obj, const char *name, ++ const char *description); ++void object_class_property_set_description(ObjectClass *klass, const char *name, ++ const char *description); ++ ++/** ++ * object_child_foreach: ++ * @obj: the object whose children will be navigated ++ * @fn: the iterator function to be called ++ * @opaque: an opaque value that will be passed to the iterator ++ * ++ * Call @fn passing each child of @obj and @opaque to it, until @fn returns ++ * non-zero. ++ * ++ * It is forbidden to add or remove children from @obj from the @fn ++ * callback. ++ * ++ * Returns: The last value returned by @fn, or 0 if there is no child. ++ */ ++int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), ++ void *opaque); ++ ++/** ++ * object_child_foreach_recursive: ++ * @obj: the object whose children will be navigated ++ * @fn: the iterator function to be called ++ * @opaque: an opaque value that will be passed to the iterator ++ * ++ * Call @fn passing each child of @obj and @opaque to it, until @fn returns ++ * non-zero. Calls recursively, all child nodes of @obj will also be passed ++ * all the way down to the leaf nodes of the tree. Depth first ordering. ++ * ++ * It is forbidden to add or remove children from @obj (or its ++ * child nodes) from the @fn callback. ++ * ++ * Returns: The last value returned by @fn, or 0 if there is no child. ++ */ ++int object_child_foreach_recursive(Object *obj, ++ int (*fn)(Object *child, void *opaque), ++ void *opaque); ++/** ++ * container_get: ++ * @root: root of the #path, e.g., object_get_root() ++ * @path: path to the container ++ * ++ * Return a container object whose path is @path. Create more containers ++ * along the path if necessary. ++ * ++ * Returns: the container object. ++ */ ++Object *container_get(Object *root, const char *path); ++ ++/** ++ * object_type_get_instance_size: ++ * @typename: Name of the Type whose instance_size is required ++ * ++ * Returns the instance_size of the given @typename. ++ */ ++size_t object_type_get_instance_size(const char *typename); ++ ++/** ++ * object_property_help: ++ * @name: the name of the property ++ * @type: the type of the property ++ * @defval: the default value ++ * @description: description of the property ++ * ++ * Returns: a user-friendly formatted string describing the property ++ * for help purposes. ++ */ ++char *object_property_help(const char *name, const char *type, ++ QObject *defval, const char *description); ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(Object, object_unref) ++ ++#endif +diff --git a/include/qom/qom-qobject.h b/include/qom/qom-qobject.h +new file mode 100644 +index 00000000..73e4e0e4 +--- /dev/null ++++ b/include/qom/qom-qobject.h +@@ -0,0 +1,43 @@ ++/* ++ * QEMU Object Model - QObject wrappers ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#ifndef QEMU_QOM_QOBJECT_H ++#define QEMU_QOM_QOBJECT_H ++ ++/* ++ * object_property_get_qobject: ++ * @obj: the object ++ * @name: the name of the property ++ * @errp: returns an error if this function fails ++ * ++ * Returns: the value of the property, converted to QObject, or NULL if ++ * an error occurs. ++ */ ++struct QObject *object_property_get_qobject(Object *obj, const char *name, ++ struct Error **errp); ++ ++/** ++ * object_property_set_qobject: ++ * @obj: the object ++ * @name: the name of the property ++ * @value: The value that will be written to the property. ++ * @errp: returns an error if this function fails ++ * ++ * Writes a property to a object. ++ * ++ * Returns: %true on success, %false on failure. ++ */ ++bool object_property_set_qobject(Object *obj, ++ const char *name, struct QObject *value, ++ struct Error **errp); ++ ++#endif +diff --git a/include/sysemu/block-backend-common.h b/include/sysemu/block-backend-common.h +new file mode 100644 +index 00000000..780cea73 +--- /dev/null ++++ b/include/sysemu/block-backend-common.h +@@ -0,0 +1,103 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_BACKEND_COMMON_H ++#define BLOCK_BACKEND_COMMON_H ++ ++#include "qemu/iov.h" ++#include "block/throttle-groups.h" ++ ++/* ++ * TODO Have to include block/block.h for a bunch of block layer ++ * types. Unfortunately, this pulls in the whole BlockDriverState ++ * API, which we don't want used by many BlockBackend users. Some of ++ * the types belong here, and the rest should be split into a common ++ * header and one for the BlockDriverState API. ++ */ ++#include "block/block.h" ++ ++/* Callbacks for block device models */ ++typedef struct BlockDevOps { ++ ++ /* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++ /* ++ * Runs when virtual media changed (monitor commands eject, change) ++ * Argument load is true on load and false on eject. ++ * Beware: doesn't run when a host device's physical media ++ * changes. Sure would be useful if it did. ++ * Device models with removable media must implement this callback. ++ */ ++ void (*change_media_cb)(void *opaque, bool load, Error **errp); ++ /* ++ * Runs when an eject request is issued from the monitor, the tray ++ * is closed, and the medium is locked. ++ * Device models that do not implement is_medium_locked will not need ++ * this callback. Device models that can lock the medium or tray might ++ * want to implement the callback and unlock the tray when "force" is ++ * true, even if they do not support eject requests. ++ */ ++ void (*eject_request_cb)(void *opaque, bool force); ++ ++ /* ++ * Is the virtual medium locked into the device? ++ * Device models implement this only when device has such a lock. ++ */ ++ bool (*is_medium_locked)(void *opaque); ++ ++ /* ++ * Runs when the backend receives a drain request. ++ */ ++ void (*drained_begin)(void *opaque); ++ /* ++ * Runs when the backend's last drain request ends. ++ */ ++ void (*drained_end)(void *opaque); ++ /* ++ * Is the device still busy? ++ */ ++ bool (*drained_poll)(void *opaque); ++ ++ /* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++ /* ++ * Is the virtual tray open? ++ * Device models implement this only when the device has a tray. ++ */ ++ bool (*is_tray_open)(void *opaque); ++ ++ /* ++ * Runs when the size changed (e.g. monitor command block_resize) ++ */ ++ void (*resize_cb)(void *opaque); ++} BlockDevOps; ++ ++/* ++ * This struct is embedded in (the private) BlockBackend struct and contains ++ * fields that must be public. This is in particular for QLIST_ENTRY() and ++ * friends so that BlockBackends can be kept in lists outside block-backend.c ++ */ ++typedef struct BlockBackendPublic { ++ ThrottleGroupMember throttle_group_member; ++} BlockBackendPublic; ++ ++#endif /* BLOCK_BACKEND_COMMON_H */ +diff --git a/include/sysemu/block-backend-global-state.h b/include/sysemu/block-backend-global-state.h +new file mode 100644 +index 00000000..49c12b0f +--- /dev/null ++++ b/include/sysemu/block-backend-global-state.h +@@ -0,0 +1,133 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_BACKEND_GLOBAL_STATE_H ++#define BLOCK_BACKEND_GLOBAL_STATE_H ++ ++#include "block-backend-common.h" ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm); ++ ++BlockBackend * no_coroutine_fn ++blk_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, ++ Error **errp); ++ ++BlockBackend * coroutine_fn no_co_wrapper ++blk_co_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, ++ Error **errp); ++ ++BlockBackend * no_coroutine_fn ++blk_new_open(const char *filename, const char *reference, QDict *options, ++ int flags, Error **errp); ++ ++BlockBackend * coroutine_fn no_co_wrapper ++blk_co_new_open(const char *filename, const char *reference, QDict *options, ++ int flags, Error **errp); ++ ++int blk_get_refcnt(BlockBackend *blk); ++void blk_ref(BlockBackend *blk); ++ ++void no_coroutine_fn blk_unref(BlockBackend *blk); ++void coroutine_fn no_co_wrapper blk_co_unref(BlockBackend *blk); ++ ++void blk_remove_all_bs(void); ++BlockBackend *blk_by_name(const char *name); ++BlockBackend *blk_next(BlockBackend *blk); ++BlockBackend *blk_all_next(BlockBackend *blk); ++bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp); ++void monitor_remove_blk(BlockBackend *blk); ++ ++BlockBackendPublic *blk_get_public(BlockBackend *blk); ++BlockBackend *blk_by_public(BlockBackendPublic *public); ++ ++void blk_remove_bs(BlockBackend *blk); ++int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); ++int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp); ++bool GRAPH_RDLOCK bdrv_has_blk(BlockDriverState *bs); ++bool GRAPH_RDLOCK bdrv_is_root_node(BlockDriverState *bs); ++int GRAPH_UNLOCKED blk_set_perm(BlockBackend *blk, uint64_t perm, ++ uint64_t shared_perm, Error **errp); ++void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm); ++ ++void blk_iostatus_enable(BlockBackend *blk); ++BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk); ++void blk_iostatus_disable(BlockBackend *blk); ++void blk_iostatus_reset(BlockBackend *blk); ++int blk_attach_dev(BlockBackend *blk, DeviceState *dev); ++void blk_detach_dev(BlockBackend *blk, DeviceState *dev); ++DeviceState *blk_get_attached_dev(BlockBackend *blk); ++BlockBackend *blk_by_dev(void *dev); ++BlockBackend *blk_by_qdev_id(const char *id, Error **errp); ++void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque); ++ ++void blk_activate(BlockBackend *blk, Error **errp); ++ ++int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); ++void blk_aio_cancel(BlockAIOCB *acb); ++int blk_commit_all(void); ++bool blk_in_drain(BlockBackend *blk); ++void blk_drain(BlockBackend *blk); ++void blk_drain_all(void); ++void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, ++ BlockdevOnError on_write_error); ++bool blk_supports_write_perm(BlockBackend *blk); ++bool blk_is_sg(BlockBackend *blk); ++void blk_set_enable_write_cache(BlockBackend *blk, bool wce); ++int blk_get_flags(BlockBackend *blk); ++bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp); ++void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason); ++void blk_op_block_all(BlockBackend *blk, Error *reason); ++void blk_op_unblock_all(BlockBackend *blk, Error *reason); ++int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, ++ Error **errp); ++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); ++void blk_remove_aio_context_notifier(BlockBackend *blk, ++ void (*attached_aio_context)(AioContext *, ++ void *), ++ void (*detach_aio_context)(void *), ++ void *opaque); ++void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify); ++void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify); ++BlockBackendRootState *blk_get_root_state(BlockBackend *blk); ++void blk_update_root_state(BlockBackend *blk); ++bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk); ++int blk_get_open_flags_from_root_state(BlockBackend *blk); ++ ++int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, ++ int64_t pos, int size); ++int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); ++int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); ++int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); ++ ++void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); ++void blk_io_limits_disable(BlockBackend *blk); ++void blk_io_limits_enable(BlockBackend *blk, const char *group); ++void blk_io_limits_update_group(BlockBackend *blk, const char *group); ++void blk_set_force_allow_inactivate(BlockBackend *blk); ++ ++bool blk_register_buf(BlockBackend *blk, void *host, size_t size, Error **errp); ++void blk_unregister_buf(BlockBackend *blk, void *host, size_t size); ++ ++const BdrvChild *blk_root(BlockBackend *blk); ++ ++int blk_make_empty(BlockBackend *blk, Error **errp); ++ ++#endif /* BLOCK_BACKEND_GLOBAL_STATE_H */ +diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h +new file mode 100644 +index 00000000..d174275a +--- /dev/null ++++ b/include/sysemu/block-backend-io.h +@@ -0,0 +1,230 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_BACKEND_IO_H ++#define BLOCK_BACKEND_IO_H ++ ++#include "block-backend-common.h" ++#include "block/accounting.h" ++ ++/* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++const char *blk_name(const BlockBackend *blk); ++ ++BlockDriverState *blk_bs(BlockBackend *blk); ++ ++void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow); ++void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow); ++void blk_set_disable_request_queuing(BlockBackend *blk, bool disable); ++bool blk_iostatus_is_enabled(const BlockBackend *blk); ++ ++char *blk_get_attached_dev_id(BlockBackend *blk); ++ ++BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque); ++ ++BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_flush(BlockBackend *blk, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, ++ BlockCompletionFunc *cb, void *opaque); ++void blk_aio_cancel_async(BlockAIOCB *acb); ++BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, ++ BlockCompletionFunc *cb, void *opaque); ++ ++void blk_inc_in_flight(BlockBackend *blk); ++void blk_dec_in_flight(BlockBackend *blk); ++ ++bool coroutine_fn GRAPH_RDLOCK blk_co_is_inserted(BlockBackend *blk); ++bool co_wrapper_mixed_bdrv_rdlock blk_is_inserted(BlockBackend *blk); ++ ++bool coroutine_fn GRAPH_RDLOCK blk_co_is_available(BlockBackend *blk); ++bool co_wrapper_mixed_bdrv_rdlock blk_is_available(BlockBackend *blk); ++ ++void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked); ++void co_wrapper blk_lock_medium(BlockBackend *blk, bool locked); ++ ++void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag); ++void co_wrapper blk_eject(BlockBackend *blk, bool eject_flag); ++ ++int64_t coroutine_fn blk_co_getlength(BlockBackend *blk); ++int64_t co_wrapper_mixed blk_getlength(BlockBackend *blk); ++ ++void coroutine_fn blk_co_get_geometry(BlockBackend *blk, ++ uint64_t *nb_sectors_ptr); ++void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr); ++ ++int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk); ++int64_t blk_nb_sectors(BlockBackend *blk); ++ ++void *blk_try_blockalign(BlockBackend *blk, size_t size); ++void *blk_blockalign(BlockBackend *blk, size_t size); ++bool blk_is_writable(BlockBackend *blk); ++bool blk_enable_write_cache(BlockBackend *blk); ++BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); ++BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, ++ int error); ++void blk_error_action(BlockBackend *blk, BlockErrorAction action, ++ bool is_read, int error); ++void blk_iostatus_set_err(BlockBackend *blk, int error); ++int blk_get_max_iov(BlockBackend *blk); ++int blk_get_max_hw_iov(BlockBackend *blk); ++ ++AioContext *blk_get_aio_context(BlockBackend *blk); ++BlockAcctStats *blk_get_stats(BlockBackend *blk); ++void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, ++ BlockCompletionFunc *cb, void *opaque); ++BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, ++ BlockCompletionFunc *cb, ++ void *opaque, int ret); ++ ++uint32_t blk_get_request_alignment(BlockBackend *blk); ++uint32_t blk_get_max_transfer(BlockBackend *blk); ++uint64_t blk_get_max_hw_transfer(BlockBackend *blk); ++ ++int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, ++ BlockBackend *blk_out, int64_t off_out, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags); ++ ++int coroutine_fn blk_co_block_status_above(BlockBackend *blk, ++ BlockDriverState *base, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, ++ BlockDriverState **file); ++int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk, ++ BlockDriverState *base, ++ bool include_base, int64_t offset, ++ int64_t bytes, int64_t *pnum); ++ ++/* ++ * "I/O or GS" API functions. These functions can run without ++ * the BQL, but only in one specific iothread/main loop. ++ * ++ * See include/block/block-io.h for more information about ++ * the "I/O or GS" API. ++ */ ++ ++int co_wrapper_mixed blk_pread(BlockBackend *blk, int64_t offset, ++ int64_t bytes, void *buf, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes, ++ void *buf, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_preadv(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_preadv_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pwrite(BlockBackend *blk, int64_t offset, ++ int64_t bytes, const void *buf, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, ++ const void *buf, BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pwritev(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pwritev_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pwrite_compressed(BlockBackend *blk, ++ int64_t offset, int64_t bytes, ++ const void *buf); ++int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset, ++ int64_t bytes, const void *buf); ++ ++int co_wrapper_mixed blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, ++ BdrvRequestFlags flags); ++int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags); ++ ++int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones); ++int co_wrapper_mixed blk_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones); ++int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len); ++int co_wrapper_mixed blk_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len); ++int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++int co_wrapper_mixed blk_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags); ++ ++int co_wrapper_mixed blk_pdiscard(BlockBackend *blk, int64_t offset, ++ int64_t bytes); ++int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, ++ int64_t bytes); ++ ++int co_wrapper_mixed blk_flush(BlockBackend *blk); ++int coroutine_fn blk_co_flush(BlockBackend *blk); ++ ++int co_wrapper_mixed blk_ioctl(BlockBackend *blk, unsigned long int req, ++ void *buf); ++int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req, ++ void *buf); ++ ++int co_wrapper_mixed blk_truncate(BlockBackend *blk, int64_t offset, ++ bool exact, PreallocMode prealloc, ++ BdrvRequestFlags flags, Error **errp); ++int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, ++ Error **errp); ++ ++#endif /* BLOCK_BACKEND_IO_H */ +diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h +new file mode 100644 +index 00000000..038be9fc +--- /dev/null ++++ b/include/sysemu/block-backend.h +@@ -0,0 +1,21 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef BLOCK_BACKEND_H ++#define BLOCK_BACKEND_H ++ ++#include "block-backend-global-state.h" ++#include "block-backend-io.h" ++ ++/* DO NOT ADD ANYTHING IN HERE. USE ONE OF THE HEADERS INCLUDED ABOVE */ ++ ++#endif +diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h +new file mode 100644 +index 00000000..3211b165 +--- /dev/null ++++ b/include/sysemu/blockdev.h +@@ -0,0 +1,64 @@ ++/* ++ * QEMU host block devices ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef BLOCKDEV_H ++#define BLOCKDEV_H ++ ++#include "block/block.h" ++#include "qemu/queue.h" ++ ++typedef enum { ++ IF_DEFAULT = -1, /* for use with drive_add() only */ ++ /* ++ * IF_NONE must be zero, because we want MachineClass member ++ * block_default_type to default-initialize to IF_NONE ++ */ ++ IF_NONE = 0, ++ IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, ++ IF_COUNT ++} BlockInterfaceType; ++ ++struct DriveInfo { ++ BlockInterfaceType type; ++ int bus; ++ int unit; ++ int auto_del; /* see blockdev_mark_auto_del() */ ++ bool is_default; /* Added by default_drive() ? */ ++ int media_cd; ++ QemuOpts *opts; ++ QTAILQ_ENTRY(DriveInfo) next; ++}; ++ ++/* ++ * Global state (GS) API. These functions run under the BQL. ++ * ++ * See include/block/block-global-state.h for more information about ++ * the GS API. ++ */ ++ ++void blockdev_mark_auto_del(BlockBackend *blk); ++void blockdev_auto_del(BlockBackend *blk); ++ ++DriveInfo *blk_legacy_dinfo(BlockBackend *blk); ++DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo); ++BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo); ++ ++void override_max_devs(BlockInterfaceType type, int max_devs); ++ ++DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); ++void drive_check_orphaned(void); ++DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); ++int drive_get_max_bus(BlockInterfaceType type); ++ ++QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, ++ const char *optstr); ++DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type, ++ Error **errp); ++ ++#endif +diff --git a/include/sysemu/cpu-timers.h b/include/sysemu/cpu-timers.h +new file mode 100644 +index 00000000..7bfa960f +--- /dev/null ++++ b/include/sysemu/cpu-timers.h +@@ -0,0 +1,104 @@ ++/* ++ * CPU timers state API ++ * ++ * Copyright 2020 SUSE LLC ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++#ifndef SYSEMU_CPU_TIMERS_H ++#define SYSEMU_CPU_TIMERS_H ++ ++#include "qemu/timer.h" ++ ++/* init the whole cpu timers API, including icount, ticks, and cpu_throttle */ ++void cpu_timers_init(void); ++ ++/* icount - Instruction Counter API */ ++ ++/** ++ * ICountMode: icount enablement state: ++ * ++ * @ICOUNT_DISABLED: Disabled - Do not count executed instructions. ++ * @ICOUNT_PRECISE: Enabled - Fixed conversion of insn to ns via "shift" option ++ * @ICOUNT_ADAPTATIVE: Enabled - Runtime adaptive algorithm to compute shift ++ */ ++typedef enum { ++ ICOUNT_DISABLED = 0, ++ ICOUNT_PRECISE, ++ ICOUNT_ADAPTATIVE, ++} ICountMode; ++ ++#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) ++extern ICountMode use_icount; ++#define icount_enabled() (use_icount) ++#else ++#define icount_enabled() ICOUNT_DISABLED ++#endif ++ ++/* ++ * Update the icount with the executed instructions. Called by ++ * cpus-tcg vCPU thread so the main-loop can see time has moved forward. ++ */ ++void icount_update(CPUState *cpu); ++ ++/* get raw icount value */ ++int64_t icount_get_raw(void); ++ ++/* return the virtual CPU time in ns, based on the instruction counter. */ ++int64_t icount_get(void); ++/* ++ * convert an instruction counter value to ns, based on the icount shift. ++ * This shift is set as a fixed value with the icount "shift" option ++ * (precise mode), or it is constantly approximated and corrected at ++ * runtime in adaptive mode. ++ */ ++int64_t icount_to_ns(int64_t icount); ++ ++/** ++ * icount_configure: configure the icount options, including "shift" ++ * @opts: Options to parse ++ * @errp: pointer to a NULL-initialized error object ++ * ++ * Return: true on success, else false setting @errp with error ++ */ ++bool icount_configure(QemuOpts *opts, Error **errp); ++ ++/* used by tcg vcpu thread to calc icount budget */ ++int64_t icount_round(int64_t count); ++ ++/* if the CPUs are idle, start accounting real time to virtual clock. */ ++void icount_start_warp_timer(void); ++void icount_account_warp_timer(void); ++void icount_notify_exit(void); ++ ++/* ++ * CPU Ticks and Clock ++ */ ++ ++/* Caller must hold BQL */ ++void cpu_enable_ticks(void); ++/* Caller must hold BQL */ ++void cpu_disable_ticks(void); ++ ++/* ++ * return the time elapsed in VM between vm_start and vm_stop. ++ * cpu_get_ticks() uses units of the host CPU cycle counter. ++ */ ++int64_t cpu_get_ticks(void); ++ ++/* ++ * Returns the monotonic time elapsed in VM, i.e., ++ * the time between vm_start and vm_stop ++ */ ++int64_t cpu_get_clock(void); ++ ++void qemu_timer_notify_cb(void *opaque, QEMUClockType type); ++ ++/* get/set VIRTUAL clock and VM elapsed ticks via the cpus accel interface */ ++int64_t cpus_get_virtual_clock(void); ++void cpus_set_virtual_clock(int64_t new_time); ++int64_t cpus_get_elapsed_ticks(void); ++ ++#endif /* SYSEMU_CPU_TIMERS_H */ +diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h +new file mode 100644 +index 00000000..b881ac6c +--- /dev/null ++++ b/include/sysemu/os-posix.h +@@ -0,0 +1,101 @@ ++/* ++ * posix specific declarations ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2010 Jes Sorensen ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef QEMU_OS_POSIX_H ++#define QEMU_OS_POSIX_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_SYSMACROS ++#include ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++void os_set_line_buffering(void); ++void os_setup_early_signal_handling(void); ++void os_set_proc_name(const char *s); ++void os_setup_signal_handling(void); ++int os_set_daemonize(bool d); ++bool is_daemonized(void); ++void os_daemonize(void); ++bool os_set_runas(const char *user_id); ++void os_set_chroot(const char *path); ++void os_setup_limits(void); ++void os_setup_post(void); ++int os_mlock(void); ++ ++/** ++ * qemu_alloc_stack: ++ * @sz: pointer to a size_t holding the requested usable stack size ++ * ++ * Allocate memory that can be used as a stack, for instance for ++ * coroutines. If the memory cannot be allocated, this function ++ * will abort (like g_malloc()). This function also inserts an ++ * additional guard page to catch a potential stack overflow. ++ * Note that the memory required for the guard page and alignment ++ * and minimal stack size restrictions will increase the value of sz. ++ * ++ * The allocated stack must be freed with qemu_free_stack(). ++ * ++ * Returns: pointer to (the lowest address of) the stack memory. ++ */ ++void *qemu_alloc_stack(size_t *sz); ++ ++/** ++ * qemu_free_stack: ++ * @stack: stack to free ++ * @sz: size of stack in bytes ++ * ++ * Free a stack allocated via qemu_alloc_stack(). Note that sz must ++ * be exactly the adjusted stack size returned by qemu_alloc_stack. ++ */ ++void qemu_free_stack(void *stack, size_t sz); ++ ++/* POSIX and Mingw32 differ in the name of the stdio lock functions. */ ++ ++static inline void qemu_flockfile(FILE *f) ++{ ++ flockfile(f); ++} ++ ++static inline void qemu_funlockfile(FILE *f) ++{ ++ funlockfile(f); ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/qcow2/LICENSE-gpl-2.0.txt b/qcow2/LICENSE-gpl-2.0.txt +new file mode 100644 +index 00000000..9efa6fbc +--- /dev/null ++++ b/qcow2/LICENSE-gpl-2.0.txt +@@ -0,0 +1,338 @@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc., ++ ++ Everyone is permitted to copy and distribute verbatim copies ++ of this license document, but changing it is not allowed. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Lesser General Public License instead.) You can apply it to ++your programs, too. ++ ++ When we speak of free software, we are referring to freedom, not ++price. Our General Public Licenses are designed to make sure that you ++have the freedom to distribute copies of free software (and charge for ++this service if you wish), that you receive source code or can get it ++if you want it, that you can change the software or use pieces of it ++in new free programs; and that you know you can do these things. ++ ++ To protect your rights, we need to make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. You must make sure that they, too, receive or can get the ++source code. And you must show them these terms so they know their ++rights. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute verbatim copies of the Program's ++source code as you receive it, in any medium, provided that you ++conspicuously and appropriately publish on each copy an appropriate ++copyright notice and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program is ++void, and will automatically terminate your rights under this License. ++However, parties who have received copies, or rights, from you under ++this License will not have their licenses terminated so long as such ++parties remain in full compliance. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++conditions are imposed on you (whether by court order, agreement or ++otherwise) that contradict the conditions of this License, they do not ++excuse you from the conditions of this License. If you cannot ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this License. ++ ++ 8. If the distribution and/or use of the Program is restricted in ++certain countries either by patents or by copyrighted interfaces, the ++original copyright holder who places the Program under this License ++may add an explicit geographical distribution limitation excluding ++those countries, so that distribution is permitted only in or among ++countries not thus excluded. In such case, this License incorporates ++the limitation as if written in the body of this License. ++ ++ 9. The Free Software Foundation may publish revised and/or new versions ++of the General Public License from time to time. Such new versions will ++be similar in spirit to the present version, but may differ in detail to ++address new problems or concerns. ++ ++Each version is given a distinguishing version number. If the Program ++specifies a version number of this License which applies to it and "any ++later version", you have the option of following the terms and conditions ++either of that version or of any later version published by the Free ++Software Foundation. If the Program does not specify a version number of ++this License, you may choose any version ever published by the Free Software ++Foundation. ++ ++ 10. If you wish to incorporate parts of the Program into other free ++programs whose distribution conditions are different, write to the author ++to ask for permission. For software which is copyrighted by the Free ++Software Foundation, write to the Free Software Foundation; we sometimes ++make exceptions for this. Our decision will be guided by the two goals ++of preserving the free status of all derivatives of our free software and ++of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED ++OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS ++TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ++PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, ++REPAIR OR CORRECTION. ++ ++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, ++INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING ++OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED ++TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ++YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER ++PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE ++POSSIBILITY OF SUCH DAMAGES. ++ ++ END OF TERMS AND CONDITIONS ++ ++ How to Apply These Terms to Your New Programs ++ ++ If you develop a new program, and you want it to be of the greatest ++possible use to the public, the best way to achieve this is to make it ++free software which everyone can redistribute and change under these terms. ++ ++ To do so, attach the following notices to the program. It is safest ++to attach them to the start of each source file to most effectively ++convey the exclusion of warranty; and each file should have at least ++the "copyright" line and a pointer to where the full notice is found. ++ ++ ++ Copyright (C) ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License along ++ with this program; if not, see . ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this ++when it starts in an interactive mode: ++ ++ Gnomovision version 69, Copyright (C) year name of author ++ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. ++ This is free software, and you are welcome to redistribute it ++ under certain conditions; type `show c' for details. ++ ++The hypothetical commands `show w' and `show c' should show the appropriate ++parts of the General Public License. Of course, the commands you use may ++be called something other than `show w' and `show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++ `Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++ , 1 April 1989 ++ Moe Ghoul, President of Vice ++ ++This General Public License does not permit incorporating your program into ++proprietary programs. If your program is a subroutine library, you may ++consider it more useful to permit linking proprietary applications with the ++library. If this is what you want to do, use the GNU Lesser General ++Public License instead of this License. +diff --git a/qcow2/lib/authz/base.c b/qcow2/lib/authz/base.c +new file mode 100644 +index 00000000..f2b7fbe9 +--- /dev/null ++++ b/qcow2/lib/authz/base.c +@@ -0,0 +1,83 @@ ++/* ++ * QEMU authorization framework base class ++ * ++ * Copyright (c) 2018 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "authz/base.h" ++#include "qemu/module.h" ++#include "trace.h" ++ ++bool qauthz_is_allowed(QAuthZ *authz, ++ const char *identity, ++ Error **errp) ++{ ++ QAuthZClass *cls = QAUTHZ_GET_CLASS(authz); ++ bool allowed; ++ ++ allowed = cls->is_allowed(authz, identity, errp); ++ trace_qauthz_is_allowed(authz, identity, allowed); ++ ++ return allowed; ++} ++ ++ ++bool qauthz_is_allowed_by_id(const char *authzid, ++ const char *identity, ++ Error **errp) ++{ ++ QAuthZ *authz; ++ Object *obj; ++ Object *container; ++ ++ container = object_get_objects_root(); ++ obj = object_resolve_path_component(container, ++ authzid); ++ if (!obj) { ++ error_setg(errp, "Cannot find QAuthZ object ID %s", ++ authzid); ++ return false; ++ } ++ ++ if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) { ++ error_setg(errp, "Object '%s' is not a QAuthZ subclass", ++ authzid); ++ return false; ++ } ++ ++ authz = QAUTHZ(obj); ++ ++ return qauthz_is_allowed(authz, identity, errp); ++} ++ ++ ++static const TypeInfo authz_info = { ++ .parent = TYPE_OBJECT, ++ .name = TYPE_QAUTHZ, ++ .instance_size = sizeof(QAuthZ), ++ .class_size = sizeof(QAuthZClass), ++ .abstract = true, ++}; ++ ++static void qauthz_register_types(void) ++{ ++ type_register_static(&authz_info); ++} ++ ++type_init(qauthz_register_types) ++ +diff --git a/qcow2/lib/block.c b/qcow2/lib/block.c +new file mode 100644 +index 00000000..c317de9e +--- /dev/null ++++ b/qcow2/lib/block.c +@@ -0,0 +1,8432 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2020 Virtuozzo International GmbH. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/trace.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "block/dirty-bitmap.h" ++#include "block/fuse.h" ++#include "block/nbd.h" ++#include "block/qdict.h" ++#include "qemu/error-report.h" ++#include "block/module_block.h" ++#include "qemu/main-loop.h" ++#include "qemu/module.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "sysemu/block-backend.h" ++#include "qemu/notify.h" ++#include "qemu/option.h" ++#include "qemu/coroutine.h" ++#include "block/qapi.h" ++#include "qemu/timer.h" ++#include "qemu/cutils.h" ++#include "qemu/id.h" ++#include "qemu/range.h" ++#include "qemu/rcu.h" ++#include "block/coroutines.h" ++ ++#ifdef CONFIG_BSD ++#include ++#include ++#if defined(HAVE_SYS_DISK_H) ++#include ++#endif ++#endif ++ ++#ifdef _WIN32 ++#include ++#endif ++ ++#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ ++ ++/* Protected by BQL */ ++static QTAILQ_HEAD(, BlockDriverState) graph_bdrv_states = ++ QTAILQ_HEAD_INITIALIZER(graph_bdrv_states); ++ ++/* Protected by BQL */ ++static QTAILQ_HEAD(, BlockDriverState) all_bdrv_states = ++ QTAILQ_HEAD_INITIALIZER(all_bdrv_states); ++ ++/* Protected by BQL */ ++static QLIST_HEAD(, BlockDriver) bdrv_drivers = ++ QLIST_HEAD_INITIALIZER(bdrv_drivers); ++ ++static BlockDriverState *bdrv_open_inherit(const char *filename, ++ const char *reference, ++ QDict *options, int flags, ++ BlockDriverState *parent, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ bool parse_filename, ++ Error **errp); ++ ++static bool bdrv_recurse_has_child(BlockDriverState *bs, ++ BlockDriverState *child); ++ ++static void GRAPH_WRLOCK ++bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs); ++ ++static void GRAPH_WRLOCK ++bdrv_remove_child(BdrvChild *child, Transaction *tran); ++ ++static int bdrv_reopen_prepare(BDRVReopenState *reopen_state, ++ BlockReopenQueue *queue, ++ Transaction *change_child_tran, Error **errp); ++static void bdrv_reopen_commit(BDRVReopenState *reopen_state); ++static void bdrv_reopen_abort(BDRVReopenState *reopen_state); ++ ++static bool bdrv_backing_overridden(BlockDriverState *bs); ++ ++static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp); ++ ++/* If non-zero, use only whitelisted block drivers */ ++static int use_bdrv_whitelist; ++ ++#ifdef _WIN32 ++static int is_windows_drive_prefix(const char *filename) ++{ ++ return (((filename[0] >= 'a' && filename[0] <= 'z') || ++ (filename[0] >= 'A' && filename[0] <= 'Z')) && ++ filename[1] == ':'); ++} ++ ++int is_windows_drive(const char *filename) ++{ ++ if (is_windows_drive_prefix(filename) && ++ filename[2] == '\0') ++ return 1; ++ if (strstart(filename, "\\\\.\\", NULL) || ++ strstart(filename, "//./", NULL)) ++ return 1; ++ return 0; ++} ++#endif ++ ++size_t bdrv_opt_mem_align(BlockDriverState *bs) ++{ ++ if (!bs || !bs->drv) { ++ /* page size or 4k (hdd sector size) should be on the safe side */ ++ return MAX(4096, qemu_real_host_page_size()); ++ } ++ IO_CODE(); ++ ++ return bs->bl.opt_mem_alignment; ++} ++ ++size_t bdrv_min_mem_align(BlockDriverState *bs) ++{ ++ if (!bs || !bs->drv) { ++ /* page size or 4k (hdd sector size) should be on the safe side */ ++ return MAX(4096, qemu_real_host_page_size()); ++ } ++ IO_CODE(); ++ ++ return bs->bl.min_mem_alignment; ++} ++ ++/* check if the path starts with ":" */ ++int path_has_protocol(const char *path) ++{ ++ const char *p; ++ ++#ifdef _WIN32 ++ if (is_windows_drive(path) || ++ is_windows_drive_prefix(path)) { ++ return 0; ++ } ++ p = path + strcspn(path, ":/\\"); ++#else ++ p = path + strcspn(path, ":/"); ++#endif ++ ++ return *p == ':'; ++} ++ ++int path_is_absolute(const char *path) ++{ ++#ifdef _WIN32 ++ /* specific case for names like: "\\.\d:" */ ++ if (is_windows_drive(path) || is_windows_drive_prefix(path)) { ++ return 1; ++ } ++ return (*path == '/' || *path == '\\'); ++#else ++ return (*path == '/'); ++#endif ++} ++ ++/* if filename is absolute, just return its duplicate. Otherwise, build a ++ path to it by considering it is relative to base_path. URL are ++ supported. */ ++char *path_combine(const char *base_path, const char *filename) ++{ ++ const char *protocol_stripped = NULL; ++ const char *p, *p1; ++ char *result; ++ int len; ++ ++ if (path_is_absolute(filename)) { ++ return g_strdup(filename); ++ } ++ ++ if (path_has_protocol(base_path)) { ++ protocol_stripped = strchr(base_path, ':'); ++ if (protocol_stripped) { ++ protocol_stripped++; ++ } ++ } ++ p = protocol_stripped ?: base_path; ++ ++ p1 = strrchr(base_path, '/'); ++#ifdef _WIN32 ++ { ++ const char *p2; ++ p2 = strrchr(base_path, '\\'); ++ if (!p1 || p2 > p1) { ++ p1 = p2; ++ } ++ } ++#endif ++ if (p1) { ++ p1++; ++ } else { ++ p1 = base_path; ++ } ++ if (p1 > p) { ++ p = p1; ++ } ++ len = p - base_path; ++ ++ result = g_malloc(len + strlen(filename) + 1); ++ memcpy(result, base_path, len); ++ strcpy(result + len, filename); ++ ++ return result; ++} ++ ++/* ++ * Helper function for bdrv_parse_filename() implementations to remove optional ++ * protocol prefixes (especially "file:") from a filename and for putting the ++ * stripped filename into the options QDict if there is such a prefix. ++ */ ++void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix, ++ QDict *options) ++{ ++ if (strstart(filename, prefix, &filename)) { ++ /* Stripping the explicit protocol prefix may result in a protocol ++ * prefix being (wrongly) detected (if the filename contains a colon) */ ++ if (path_has_protocol(filename)) { ++ GString *fat_filename; ++ ++ /* This means there is some colon before the first slash; therefore, ++ * this cannot be an absolute path */ ++ assert(!path_is_absolute(filename)); ++ ++ /* And we can thus fix the protocol detection issue by prefixing it ++ * by "./" */ ++ fat_filename = g_string_new("./"); ++ g_string_append(fat_filename, filename); ++ ++ assert(!path_has_protocol(fat_filename->str)); ++ ++ qdict_put(options, "filename", ++ qstring_from_gstring(fat_filename)); ++ } else { ++ /* If no protocol prefix was detected, we can use the shortened ++ * filename as-is */ ++ qdict_put_str(options, "filename", filename); ++ } ++ } ++} ++ ++ ++/* Returns whether the image file is opened as read-only. Note that this can ++ * return false and writing to the image file is still not possible because the ++ * image is inactivated. */ ++bool bdrv_is_read_only(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return !(bs->open_flags & BDRV_O_RDWR); ++} ++ ++static int GRAPH_RDLOCK ++bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, ++ bool ignore_allow_rdw, Error **errp) ++{ ++ IO_CODE(); ++ ++ /* Do not set read_only if copy_on_read is enabled */ ++ if (bs->copy_on_read && read_only) { ++ error_setg(errp, "Can't set node '%s' to r/o with copy-on-read enabled", ++ bdrv_get_device_or_node_name(bs)); ++ return -EINVAL; ++ } ++ ++ /* Do not clear read_only if it is prohibited */ ++ if (!read_only && !(bs->open_flags & BDRV_O_ALLOW_RDWR) && ++ !ignore_allow_rdw) ++ { ++ error_setg(errp, "Node '%s' is read only", ++ bdrv_get_device_or_node_name(bs)); ++ return -EPERM; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Called by a driver that can only provide a read-only image. ++ * ++ * Returns 0 if the node is already read-only or it could switch the node to ++ * read-only because BDRV_O_AUTO_RDONLY is set. ++ * ++ * Returns -EACCES if the node is read-write and BDRV_O_AUTO_RDONLY is not set ++ * or bdrv_can_set_read_only() forbids making the node read-only. If @errmsg ++ * is not NULL, it is used as the error message for the Error object. ++ */ ++int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, ++ Error **errp) ++{ ++ int ret = 0; ++ IO_CODE(); ++ ++ if (!(bs->open_flags & BDRV_O_RDWR)) { ++ return 0; ++ } ++ if (!(bs->open_flags & BDRV_O_AUTO_RDONLY)) { ++ goto fail; ++ } ++ ++ ret = bdrv_can_set_read_only(bs, true, false, NULL); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ bs->open_flags &= ~BDRV_O_RDWR; ++ ++ return 0; ++ ++fail: ++ error_setg(errp, "%s", errmsg ?: "Image is read-only"); ++ return -EACCES; ++} ++ ++/* ++ * If @backing is empty, this function returns NULL without setting ++ * @errp. In all other cases, NULL will only be returned with @errp ++ * set. ++ * ++ * Therefore, a return value of NULL without @errp set means that ++ * there is no backing file; if @errp is set, there is one but its ++ * absolute filename cannot be generated. ++ */ ++char *bdrv_get_full_backing_filename_from_filename(const char *backed, ++ const char *backing, ++ Error **errp) ++{ ++ if (backing[0] == '\0') { ++ return NULL; ++ } else if (path_has_protocol(backing) || path_is_absolute(backing)) { ++ return g_strdup(backing); ++ } else if (backed[0] == '\0' || strstart(backed, "json:", NULL)) { ++ error_setg(errp, "Cannot use relative backing file names for '%s'", ++ backed); ++ return NULL; ++ } else { ++ return path_combine(backed, backing); ++ } ++} ++ ++/* ++ * If @filename is empty or NULL, this function returns NULL without ++ * setting @errp. In all other cases, NULL will only be returned with ++ * @errp set. ++ */ ++static char * GRAPH_RDLOCK ++bdrv_make_absolute_filename(BlockDriverState *relative_to, ++ const char *filename, Error **errp) ++{ ++ char *dir, *full_name; ++ ++ if (!filename || filename[0] == '\0') { ++ return NULL; ++ } else if (path_has_protocol(filename) || path_is_absolute(filename)) { ++ return g_strdup(filename); ++ } ++ ++ dir = bdrv_dirname(relative_to, errp); ++ if (!dir) { ++ return NULL; ++ } ++ ++ full_name = g_strconcat(dir, filename, NULL); ++ g_free(dir); ++ return full_name; ++} ++ ++char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_make_absolute_filename(bs, bs->backing_file, errp); ++} ++ ++void bdrv_register(BlockDriver *bdrv) ++{ ++ assert(bdrv->format_name); ++ GLOBAL_STATE_CODE(); ++ QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list); ++} ++ ++BlockDriverState *bdrv_new(void) ++{ ++ BlockDriverState *bs; ++ int i; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bs = g_new0(BlockDriverState, 1); ++ QLIST_INIT(&bs->dirty_bitmaps); ++ for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { ++ QLIST_INIT(&bs->op_blockers[i]); ++ } ++ qemu_mutex_init(&bs->reqs_lock); ++ qemu_mutex_init(&bs->dirty_bitmap_mutex); ++ bs->refcnt = 1; ++ bs->aio_context = qemu_get_aio_context(); ++ ++ qemu_co_queue_init(&bs->flush_queue); ++ ++ qemu_co_mutex_init(&bs->bsc_modify_lock); ++ bs->block_status_cache = g_new0(BdrvBlockStatusCache, 1); ++ ++ for (i = 0; i < bdrv_drain_all_count; i++) { ++ bdrv_drained_begin(bs); ++ } ++ ++ QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); ++ ++ return bs; ++} ++ ++static BlockDriver *bdrv_do_find_format(const char *format_name) ++{ ++ BlockDriver *drv1; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(drv1, &bdrv_drivers, list) { ++ if (!strcmp(drv1->format_name, format_name)) { ++ return drv1; ++ } ++ } ++ ++ return NULL; ++} ++ ++BlockDriver *bdrv_find_format(const char *format_name) ++{ ++ BlockDriver *drv1; ++ int i; ++ ++ GLOBAL_STATE_CODE(); ++ ++ drv1 = bdrv_do_find_format(format_name); ++ if (drv1) { ++ return drv1; ++ } ++ ++ /* The driver isn't registered, maybe we need to load a module */ ++ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) { ++ if (!strcmp(block_driver_modules[i].format_name, format_name)) { ++ Error *local_err = NULL; ++ int rv = block_module_load(block_driver_modules[i].library_name, ++ &local_err); ++ if (rv > 0) { ++ return bdrv_do_find_format(format_name); ++ } else if (rv < 0) { ++ error_report_err(local_err); ++ } ++ break; ++ } ++ } ++ return NULL; ++} ++ ++static int bdrv_format_is_whitelisted(const char *format_name, bool read_only) ++{ ++ static const char *whitelist_rw[] = { ++ CONFIG_BDRV_RW_WHITELIST ++ NULL ++ }; ++ static const char *whitelist_ro[] = { ++ CONFIG_BDRV_RO_WHITELIST ++ NULL ++ }; ++ const char **p; ++ ++ if (!whitelist_rw[0] && !whitelist_ro[0]) { ++ return 1; /* no whitelist, anything goes */ ++ } ++ ++ for (p = whitelist_rw; *p; p++) { ++ if (!strcmp(format_name, *p)) { ++ return 1; ++ } ++ } ++ if (read_only) { ++ for (p = whitelist_ro; *p; p++) { ++ if (!strcmp(format_name, *p)) { ++ return 1; ++ } ++ } ++ } ++ return 0; ++} ++ ++int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_format_is_whitelisted(drv->format_name, read_only); ++} ++ ++bool bdrv_uses_whitelist(void) ++{ ++ return use_bdrv_whitelist; ++} ++ ++typedef struct CreateCo { ++ BlockDriver *drv; ++ char *filename; ++ QemuOpts *opts; ++ int ret; ++ Error *err; ++} CreateCo; ++ ++int coroutine_fn bdrv_co_create(BlockDriver *drv, const char *filename, ++ QemuOpts *opts, Error **errp) ++{ ++ ERRP_GUARD(); ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv->bdrv_co_create_opts) { ++ error_setg(errp, "Driver '%s' does not support image creation", ++ drv->format_name); ++ return -ENOTSUP; ++ } ++ ++ ret = drv->bdrv_co_create_opts(drv, filename, opts, errp); ++ if (ret < 0 && !*errp) { ++ error_setg_errno(errp, -ret, "Could not create image"); ++ } ++ ++ return ret; ++} ++ ++/** ++ * Helper function for bdrv_create_file_fallback(): Resize @blk to at ++ * least the given @minimum_size. ++ * ++ * On success, return @blk's actual length. ++ * Otherwise, return -errno. ++ */ ++static int64_t coroutine_fn GRAPH_UNLOCKED ++create_file_fallback_truncate(BlockBackend *blk, int64_t minimum_size, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ int64_t size; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ ret = blk_co_truncate(blk, minimum_size, false, PREALLOC_MODE_OFF, 0, ++ &local_err); ++ if (ret < 0 && ret != -ENOTSUP) { ++ error_propagate(errp, local_err); ++ return ret; ++ } ++ ++ size = blk_co_getlength(blk); ++ if (size < 0) { ++ error_free(local_err); ++ error_setg_errno(errp, -size, ++ "Failed to inquire the new image file's length"); ++ return size; ++ } ++ ++ if (size < minimum_size) { ++ /* Need to grow the image, but we failed to do that */ ++ error_propagate(errp, local_err); ++ return -ENOTSUP; ++ } ++ ++ error_free(local_err); ++ local_err = NULL; ++ ++ return size; ++} ++ ++/** ++ * Helper function for bdrv_create_file_fallback(): Zero the first ++ * sector to remove any potentially pre-existing image header. ++ */ ++static int coroutine_fn ++create_file_fallback_zero_first_sector(BlockBackend *blk, ++ int64_t current_size, ++ Error **errp) ++{ ++ int64_t bytes_to_clear; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bytes_to_clear = MIN(current_size, BDRV_SECTOR_SIZE); ++ if (bytes_to_clear) { ++ 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"); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * Simple implementation of bdrv_co_create_opts for protocol drivers ++ * which only support creation via opening a file ++ * (usually existing raw storage device) ++ */ ++int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, ++ const char *filename, ++ QemuOpts *opts, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ BlockBackend *blk; ++ QDict *options; ++ int64_t size = 0; ++ char *buf = NULL; ++ PreallocMode prealloc; ++ Error *local_err = NULL; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); ++ buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); ++ prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, ++ PREALLOC_MODE_OFF, &local_err); ++ g_free(buf); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ if (prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, "Unsupported preallocation mode '%s'", ++ PreallocMode_str(prealloc)); ++ return -ENOTSUP; ++ } ++ ++ options = qdict_new(); ++ qdict_put_str(options, "driver", drv->format_name); ++ ++ blk = blk_co_new_open(filename, NULL, options, ++ BDRV_O_RDWR | BDRV_O_RESIZE, errp); ++ if (!blk) { ++ error_prepend(errp, "Protocol driver '%s' does not support creating " ++ "new images, so an existing image must be selected as " ++ "the target; however, opening the given target as an " ++ "existing image failed: ", ++ drv->format_name); ++ return -EINVAL; ++ } ++ ++ size = create_file_fallback_truncate(blk, size, errp); ++ if (size < 0) { ++ ret = size; ++ goto out; ++ } ++ ++ ret = create_file_fallback_zero_first_sector(blk, size, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = 0; ++out: ++ blk_co_unref(blk); ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_create_file(const char *filename, QemuOpts *opts, ++ Error **errp) ++{ ++ QemuOpts *protocol_opts; ++ BlockDriver *drv; ++ QDict *qdict; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ drv = bdrv_find_protocol(filename, true, errp); ++ if (drv == NULL) { ++ return -ENOENT; ++ } ++ ++ if (!drv->create_opts) { ++ error_setg(errp, "Driver '%s' does not support image creation", ++ drv->format_name); ++ return -ENOTSUP; ++ } ++ ++ /* ++ * 'opts' contains a QemuOptsList with a combination of format and protocol ++ * default values. ++ * ++ * The format properly removes its options, but the default values remain ++ * in 'opts->list'. So if the protocol has options with the same name ++ * (e.g. rbd has 'cluster_size' as qcow2), it will see the default values ++ * of the format, since for overlapping options, the format wins. ++ * ++ * To avoid this issue, lets convert QemuOpts to QDict, in this way we take ++ * only the set options, and then convert it back to QemuOpts, using the ++ * create_opts of the protocol. So the new QemuOpts, will contain only the ++ * protocol defaults. ++ */ ++ qdict = qemu_opts_to_qdict(opts, NULL); ++ protocol_opts = qemu_opts_from_qdict(drv->create_opts, qdict, errp); ++ if (protocol_opts == NULL) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = bdrv_co_create(drv, filename, protocol_opts, errp); ++out: ++ qemu_opts_del(protocol_opts); ++ qobject_unref(qdict); ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_delete_file(BlockDriverState *bs, Error **errp) ++{ ++ Error *local_err = NULL; ++ int ret; ++ ++ IO_CODE(); ++ assert(bs != NULL); ++ assert_bdrv_graph_readable(); ++ ++ if (!bs->drv) { ++ error_setg(errp, "Block node '%s' is not opened", bs->filename); ++ return -ENOMEDIUM; ++ } ++ ++ if (!bs->drv->bdrv_co_delete_file) { ++ error_setg(errp, "Driver '%s' does not support image deletion", ++ bs->drv->format_name); ++ return -ENOTSUP; ++ } ++ ++ ret = bs->drv->bdrv_co_delete_file(bs, &local_err); ++ if (ret < 0) { ++ error_propagate(errp, local_err); ++ } ++ ++ return ret; ++} ++ ++void coroutine_fn bdrv_co_delete_file_noerr(BlockDriverState *bs) ++{ ++ Error *local_err = NULL; ++ int ret; ++ IO_CODE(); ++ ++ if (!bs) { ++ return; ++ } ++ ++ ret = bdrv_co_delete_file(bs, &local_err); ++ /* ++ * ENOTSUP will happen if the block driver doesn't support ++ * the 'bdrv_co_delete_file' interface. This is a predictable ++ * scenario and shouldn't be reported back to the user. ++ */ ++ if (ret == -ENOTSUP) { ++ error_free(local_err); ++ } else if (ret < 0) { ++ error_report_err(local_err); ++ } ++} ++ ++/** ++ * Try to get @bs's logical and physical block size. ++ * On success, store them in @bsz struct and return 0. ++ * On failure return -errno. ++ * @bs must not be empty. ++ */ ++int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *filtered = bdrv_filter_bs(bs); ++ GLOBAL_STATE_CODE(); ++ ++ if (drv && drv->bdrv_probe_blocksizes) { ++ return drv->bdrv_probe_blocksizes(bs, bsz); ++ } else if (filtered) { ++ return bdrv_probe_blocksizes(filtered, bsz); ++ } ++ ++ return -ENOTSUP; ++} ++ ++/** ++ * Try to get @bs's geometry (cyls, heads, sectors). ++ * On success, store them in @geo struct and return 0. ++ * On failure return -errno. ++ * @bs must not be empty. ++ */ ++int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *filtered; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (drv && drv->bdrv_probe_geometry) { ++ return drv->bdrv_probe_geometry(bs, geo); ++ } ++ ++ filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ return bdrv_probe_geometry(filtered, geo); ++ } ++ ++ return -ENOTSUP; ++} ++ ++/* ++ * Create a uniquely-named empty temporary file. ++ * 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. ++ */ ++char *create_tmp_file(Error **errp) ++{ ++ int fd; ++ const char *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"; ++ } ++#endif ++ ++ filename = g_strdup_printf("%s/vl.XXXXXX", tmpdir); ++ fd = g_mkstemp(filename); ++ if (fd < 0) { ++ error_setg_errno(errp, errno, "Could not open temporary file '%s'", ++ filename); ++ return NULL; ++ } ++ close(fd); ++ ++ return g_steal_pointer(&filename); ++} ++ ++/* ++ * Detect host devices. By convention, /dev/cdrom[N] is always ++ * recognized as a host CDROM. ++ */ ++static BlockDriver *find_hdev_driver(const char *filename) ++{ ++ int score_max = 0, score; ++ BlockDriver *drv = NULL, *d; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(d, &bdrv_drivers, list) { ++ if (d->bdrv_probe_device) { ++ score = d->bdrv_probe_device(filename); ++ if (score > score_max) { ++ score_max = score; ++ drv = d; ++ } ++ } ++ } ++ ++ return drv; ++} ++ ++static BlockDriver *bdrv_do_find_protocol(const char *protocol) ++{ ++ BlockDriver *drv1; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(drv1, &bdrv_drivers, list) { ++ if (drv1->protocol_name && !strcmp(drv1->protocol_name, protocol)) { ++ return drv1; ++ } ++ } ++ ++ return NULL; ++} ++ ++BlockDriver *bdrv_find_protocol(const char *filename, ++ bool allow_protocol_prefix, ++ Error **errp) ++{ ++ BlockDriver *drv1; ++ char protocol[128]; ++ int len; ++ const char *p; ++ int i; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * XXX(hch): we really should not let host device detection ++ * override an explicit protocol specification, but moving this ++ * later breaks access to device names with colons in them. ++ * Thanks to the brain-dead persistent naming schemes on udev- ++ * based Linux systems those actually are quite common. ++ */ ++ drv1 = find_hdev_driver(filename); ++ if (drv1) { ++ return drv1; ++ } ++ ++ if (!path_has_protocol(filename) || !allow_protocol_prefix) { ++ return &bdrv_file; ++ } ++ ++ p = strchr(filename, ':'); ++ assert(p != NULL); ++ len = p - filename; ++ if (len > sizeof(protocol) - 1) ++ len = sizeof(protocol) - 1; ++ memcpy(protocol, filename, len); ++ protocol[len] = '\0'; ++ ++ drv1 = bdrv_do_find_protocol(protocol); ++ if (drv1) { ++ return drv1; ++ } ++ ++ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); ++i) { ++ if (block_driver_modules[i].protocol_name && ++ !strcmp(block_driver_modules[i].protocol_name, protocol)) { ++ int rv = block_module_load(block_driver_modules[i].library_name, errp); ++ if (rv > 0) { ++ drv1 = bdrv_do_find_protocol(protocol); ++ } else if (rv < 0) { ++ return NULL; ++ } ++ break; ++ } ++ } ++ ++ if (!drv1) { ++ error_setg(errp, "Unknown protocol '%s'", protocol); ++ } ++ return drv1; ++} ++ ++/* ++ * Guess image format by probing its contents. ++ * This is not a good idea when your image is raw (CVE-2008-2004), but ++ * we do it anyway for backward compatibility. ++ * ++ * @buf contains the image's first @buf_size bytes. ++ * @buf_size is the buffer size in bytes (generally BLOCK_PROBE_BUF_SIZE, ++ * but can be smaller if the image file is smaller) ++ * @filename is its filename. ++ * ++ * For all block drivers, call the bdrv_probe() method to get its ++ * probing score. ++ * Return the first block driver with the highest probing score. ++ */ ++BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size, ++ const char *filename) ++{ ++ int score_max = 0, score; ++ BlockDriver *drv = NULL, *d; ++ IO_CODE(); ++ ++ QLIST_FOREACH(d, &bdrv_drivers, list) { ++ if (d->bdrv_probe) { ++ score = d->bdrv_probe(buf, buf_size, filename); ++ if (score > score_max) { ++ score_max = score; ++ drv = d; ++ } ++ } ++ } ++ ++ return drv; ++} ++ ++static int find_image_format(BlockBackend *file, const char *filename, ++ BlockDriver **pdrv, Error **errp) ++{ ++ BlockDriver *drv; ++ uint8_t buf[BLOCK_PROBE_BUF_SIZE]; ++ int ret = 0; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ ++ if (blk_is_sg(file) || !blk_is_inserted(file) || blk_getlength(file) == 0) { ++ *pdrv = &bdrv_raw; ++ return ret; ++ } ++ ++ ret = blk_pread(file, 0, sizeof(buf), buf, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read image for determining its " ++ "format"); ++ *pdrv = NULL; ++ return ret; ++ } ++ ++ drv = bdrv_probe_all(buf, sizeof(buf), filename); ++ if (!drv) { ++ error_setg(errp, "Could not determine image format: No compatible " ++ "driver found"); ++ *pdrv = NULL; ++ return -ENOENT; ++ } ++ ++ *pdrv = drv; ++ return 0; ++} ++ ++/** ++ * Set the current 'total_sectors' value ++ * Return 0 on success, -errno on error. ++ */ ++int coroutine_fn bdrv_co_refresh_total_sectors(BlockDriverState *bs, ++ int64_t hint) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* Do not attempt drv->bdrv_co_getlength() on scsi-generic devices */ ++ if (bdrv_is_sg(bs)) ++ return 0; ++ ++ /* query actual device if possible, otherwise just trust the hint */ ++ if (drv->bdrv_co_getlength) { ++ int64_t length = drv->bdrv_co_getlength(bs); ++ if (length < 0) { ++ return length; ++ } ++ hint = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE); ++ } ++ ++ bs->total_sectors = hint; ++ ++ if (bs->total_sectors * BDRV_SECTOR_SIZE > BDRV_MAX_LENGTH) { ++ return -EFBIG; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Combines a QDict of new block driver @options with any missing options taken ++ * from @old_options, so that leaving out an option defaults to its old value. ++ */ ++static void bdrv_join_options(BlockDriverState *bs, QDict *options, ++ QDict *old_options) ++{ ++ GLOBAL_STATE_CODE(); ++ if (bs->drv && bs->drv->bdrv_join_options) { ++ bs->drv->bdrv_join_options(options, old_options); ++ } else { ++ qdict_join(options, old_options, false); ++ } ++} ++ ++static BlockdevDetectZeroesOptions bdrv_parse_detect_zeroes(QemuOpts *opts, ++ int open_flags, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ char *value = qemu_opt_get_del(opts, "detect-zeroes"); ++ BlockdevDetectZeroesOptions detect_zeroes = ++ qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, value, ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err); ++ GLOBAL_STATE_CODE(); ++ g_free(value); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return detect_zeroes; ++ } ++ ++ if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && ++ !(open_flags & BDRV_O_UNMAP)) ++ { ++ error_setg(errp, "setting detect-zeroes to unmap is not allowed " ++ "without setting discard operation to unmap"); ++ } ++ ++ return detect_zeroes; ++} ++ ++/** ++ * Set open flags for aio engine ++ * ++ * Return 0 on success, -1 if the engine specified is invalid ++ */ ++int bdrv_parse_aio(const char *mode, int *flags) ++{ ++ if (!strcmp(mode, "threads")) { ++ /* do nothing, default */ ++ } else if (!strcmp(mode, "native")) { ++ *flags |= BDRV_O_NATIVE_AIO; ++#ifdef CONFIG_LINUX_IO_URING ++ } else if (!strcmp(mode, "io_uring")) { ++ *flags |= BDRV_O_IO_URING; ++#endif ++ } else { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Set open flags for a given discard mode ++ * ++ * Return 0 on success, -1 if the discard mode was invalid. ++ */ ++int bdrv_parse_discard_flags(const char *mode, int *flags) ++{ ++ *flags &= ~BDRV_O_UNMAP; ++ ++ if (!strcmp(mode, "off") || !strcmp(mode, "ignore")) { ++ /* do nothing */ ++ } else if (!strcmp(mode, "on") || !strcmp(mode, "unmap")) { ++ *flags |= BDRV_O_UNMAP; ++ } else { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Set open flags for a given cache mode ++ * ++ * Return 0 on success, -1 if the cache mode was invalid. ++ */ ++int bdrv_parse_cache_mode(const char *mode, int *flags, bool *writethrough) ++{ ++ *flags &= ~BDRV_O_CACHE_MASK; ++ ++ if (!strcmp(mode, "off") || !strcmp(mode, "none")) { ++ *writethrough = false; ++ *flags |= BDRV_O_NOCACHE; ++ } else if (!strcmp(mode, "directsync")) { ++ *writethrough = true; ++ *flags |= BDRV_O_NOCACHE; ++ } else if (!strcmp(mode, "writeback")) { ++ *writethrough = false; ++ } else if (!strcmp(mode, "unsafe")) { ++ *writethrough = false; ++ *flags |= BDRV_O_NO_FLUSH; ++ } else if (!strcmp(mode, "writethrough")) { ++ *writethrough = true; ++ } else { ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static char *bdrv_child_get_parent_desc(BdrvChild *c) ++{ ++ BlockDriverState *parent = c->opaque; ++ return g_strdup_printf("node '%s'", bdrv_get_node_name(parent)); ++} ++ ++static void GRAPH_RDLOCK bdrv_child_cb_drained_begin(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ bdrv_do_drained_begin_quiesce(bs, NULL); ++} ++ ++static bool GRAPH_RDLOCK bdrv_child_cb_drained_poll(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ return bdrv_drain_poll(bs, NULL, false); ++} ++ ++static void GRAPH_RDLOCK bdrv_child_cb_drained_end(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ bdrv_drained_end(bs); ++} ++ ++static int bdrv_child_cb_inactivate(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ GLOBAL_STATE_CODE(); ++ assert(bs->open_flags & BDRV_O_INACTIVE); ++ 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); ++} ++ ++/* ++ * Returns the options and flags that a temporary snapshot should get, based on ++ * the originally requested flags (the originally requested image will have ++ * flags like a backing file) ++ */ ++static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options, ++ int parent_flags, QDict *parent_options) ++{ ++ GLOBAL_STATE_CODE(); ++ *child_flags = (parent_flags & ~BDRV_O_SNAPSHOT) | BDRV_O_TEMPORARY; ++ ++ /* For temporary files, unconditional cache=unsafe is fine */ ++ qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off"); ++ qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); ++ ++ /* Copy the read-only and discard options from the parent */ ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_DISCARD); ++ ++ /* aio=native doesn't work for cache.direct=off, so disable it for the ++ * temporary snapshot */ ++ *child_flags &= ~BDRV_O_NATIVE_AIO; ++} ++ ++static void GRAPH_WRLOCK bdrv_backing_attach(BdrvChild *c) ++{ ++ BlockDriverState *parent = c->opaque; ++ BlockDriverState *backing_hd = c->bs; ++ ++ GLOBAL_STATE_CODE(); ++ assert(!parent->backing_blocker); ++ error_setg(&parent->backing_blocker, ++ "node is used as backing hd of '%s'", ++ bdrv_get_device_or_node_name(parent)); ++ ++ bdrv_refresh_filename(backing_hd); ++ ++ parent->open_flags &= ~BDRV_O_NO_BACKING; ++ ++ bdrv_op_block_all(backing_hd, parent->backing_blocker); ++ /* Otherwise we won't be able to commit or stream */ ++ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_COMMIT_TARGET, ++ parent->backing_blocker); ++ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_STREAM, ++ parent->backing_blocker); ++ /* ++ * We do backup in 3 ways: ++ * 1. drive backup ++ * The target bs is new opened, and the source is top BDS ++ * 2. blockdev backup ++ * Both the source and the target are top BDSes. ++ * 3. internal backup(used for block replication) ++ * Both the source and the target are backing file ++ * ++ * In case 1 and 2, neither the source nor the target is the backing file. ++ * In case 3, we will block the top BDS, so there is only one block job ++ * for the top BDS and its backing chain. ++ */ ++ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_SOURCE, ++ parent->backing_blocker); ++ bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET, ++ parent->backing_blocker); ++} ++ ++static void bdrv_backing_detach(BdrvChild *c) ++{ ++ BlockDriverState *parent = c->opaque; ++ ++ GLOBAL_STATE_CODE(); ++ assert(parent->backing_blocker); ++ bdrv_op_unblock_all(c->bs, parent->backing_blocker); ++ error_free(parent->backing_blocker); ++ parent->backing_blocker = NULL; ++} ++ ++static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, ++ const char *filename, ++ bool backing_mask_protocol, ++ Error **errp) ++{ ++ BlockDriverState *parent = c->opaque; ++ bool read_only = bdrv_is_read_only(parent); ++ int ret; ++ const char *format_name; ++ GLOBAL_STATE_CODE(); ++ ++ if (read_only) { ++ ret = bdrv_reopen_set_read_only(parent, false, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (base->drv) { ++ /* ++ * If the new base image doesn't have a format driver layer, which we ++ * detect by the fact that @base is a protocol driver, we record ++ * 'raw' as the format instead of putting the protocol name as the ++ * backing format ++ */ ++ if (backing_mask_protocol && base->drv->protocol_name) { ++ format_name = "raw"; ++ } else { ++ format_name = base->drv->format_name; ++ } ++ } else { ++ format_name = ""; ++ } ++ ++ ret = bdrv_change_backing_file(parent, filename, format_name, false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not update backing file link"); ++ } ++ ++ if (read_only) { ++ bdrv_reopen_set_read_only(parent, true, NULL); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Returns the options and flags that a generic child of a BDS should ++ * get, based on the given options and flags for the parent BDS. ++ */ ++static void bdrv_inherited_options(BdrvChildRole role, bool parent_is_format, ++ int *child_flags, QDict *child_options, ++ int parent_flags, QDict *parent_options) ++{ ++ int flags = parent_flags; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * First, decide whether to set, clear, or leave BDRV_O_PROTOCOL. ++ * Generally, the question to answer is: Should this child be ++ * format-probed by default? ++ */ ++ ++ /* ++ * Pure and non-filtered data children of non-format nodes should ++ * be probed by default (even when the node itself has BDRV_O_PROTOCOL ++ * set). This only affects a very limited set of drivers (namely ++ * quorum and blkverify when this comment was written). ++ * Force-clear BDRV_O_PROTOCOL then. ++ */ ++ if (!parent_is_format && ++ (role & BDRV_CHILD_DATA) && ++ !(role & (BDRV_CHILD_METADATA | BDRV_CHILD_FILTERED))) ++ { ++ flags &= ~BDRV_O_PROTOCOL; ++ } ++ ++ /* ++ * All children of format nodes (except for COW children) and all ++ * metadata children in general should never be format-probed. ++ * Force-set BDRV_O_PROTOCOL then. ++ */ ++ if ((parent_is_format && !(role & BDRV_CHILD_COW)) || ++ (role & BDRV_CHILD_METADATA)) ++ { ++ flags |= BDRV_O_PROTOCOL; ++ } ++ ++ /* ++ * If the cache mode isn't explicitly set, inherit direct and no-flush from ++ * the parent. ++ */ ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE); ++ ++ if (role & BDRV_CHILD_COW) { ++ /* backing files are opened read-only by default */ ++ qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on"); ++ qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off"); ++ } else { ++ /* Inherit the read-only option from the parent if it's not set */ ++ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); ++ qdict_copy_default(child_options, parent_options, ++ BDRV_OPT_AUTO_READ_ONLY); ++ } ++ ++ /* ++ * bdrv_co_pdiscard() respects unmap policy for the parent, so we ++ * can default to enable it on lower layers regardless of the ++ * parent option. ++ */ ++ qdict_set_default_str(child_options, BDRV_OPT_DISCARD, "unmap"); ++ ++ /* Clear flags that only apply to the top layer */ ++ flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ); ++ ++ if (role & BDRV_CHILD_METADATA) { ++ flags &= ~BDRV_O_NO_IO; ++ } ++ if (role & BDRV_CHILD_COW) { ++ flags &= ~BDRV_O_TEMPORARY; ++ } ++ ++ *child_flags = flags; ++} ++ ++static void GRAPH_WRLOCK bdrv_child_cb_attach(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ ++ assert_bdrv_graph_writable(); ++ 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 (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; ++ } ++} ++ ++static void GRAPH_WRLOCK bdrv_child_cb_detach(BdrvChild *child) ++{ ++ BlockDriverState *bs = child->opaque; ++ ++ if (child->role & BDRV_CHILD_COW) { ++ bdrv_backing_detach(child); ++ } ++ ++ assert_bdrv_graph_writable(); ++ 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, ++ const char *filename, ++ bool backing_mask_protocol, ++ Error **errp) ++{ ++ if (c->role & BDRV_CHILD_COW) { ++ return bdrv_backing_update_filename(c, base, filename, ++ backing_mask_protocol, ++ errp); ++ } ++ return 0; ++} ++ ++AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c) ++{ ++ BlockDriverState *bs = c->opaque; ++ IO_CODE(); ++ ++ return bdrv_get_aio_context(bs); ++} ++ ++const BdrvChildClass child_of_bds = { ++ .parent_is_bds = true, ++ .get_parent_desc = bdrv_child_get_parent_desc, ++ .inherit_options = bdrv_inherited_options, ++ .drained_begin = bdrv_child_cb_drained_begin, ++ .drained_poll = bdrv_child_cb_drained_poll, ++ .drained_end = bdrv_child_cb_drained_end, ++ .attach = bdrv_child_cb_attach, ++ .detach = bdrv_child_cb_detach, ++ .inactivate = bdrv_child_cb_inactivate, ++ .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, ++}; ++ ++AioContext *bdrv_child_get_parent_aio_context(BdrvChild *c) ++{ ++ IO_CODE(); ++ return c->klass->get_parent_aio_context(c); ++} ++ ++static int bdrv_open_flags(BlockDriverState *bs, int flags) ++{ ++ int open_flags = flags; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * Clear flags that are internal to the block layer before opening the ++ * image. ++ */ ++ open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL); ++ ++ return open_flags; ++} ++ ++static void update_flags_from_options(int *flags, QemuOpts *opts) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ *flags &= ~(BDRV_O_CACHE_MASK | BDRV_O_RDWR | BDRV_O_AUTO_RDONLY); ++ ++ if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { ++ *flags |= BDRV_O_NO_FLUSH; ++ } ++ ++ if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_DIRECT, false)) { ++ *flags |= BDRV_O_NOCACHE; ++ } ++ ++ if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) { ++ *flags |= BDRV_O_RDWR; ++ } ++ ++ if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) { ++ *flags |= BDRV_O_AUTO_RDONLY; ++ } ++} ++ ++static void update_options_from_flags(QDict *options, int flags) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!qdict_haskey(options, BDRV_OPT_CACHE_DIRECT)) { ++ qdict_put_bool(options, BDRV_OPT_CACHE_DIRECT, flags & BDRV_O_NOCACHE); ++ } ++ if (!qdict_haskey(options, BDRV_OPT_CACHE_NO_FLUSH)) { ++ qdict_put_bool(options, BDRV_OPT_CACHE_NO_FLUSH, ++ flags & BDRV_O_NO_FLUSH); ++ } ++ if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) { ++ qdict_put_bool(options, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR)); ++ } ++ if (!qdict_haskey(options, BDRV_OPT_AUTO_READ_ONLY)) { ++ qdict_put_bool(options, BDRV_OPT_AUTO_READ_ONLY, ++ flags & BDRV_O_AUTO_RDONLY); ++ } ++} ++ ++static void bdrv_assign_node_name(BlockDriverState *bs, ++ const char *node_name, ++ Error **errp) ++{ ++ char *gen_node_name = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ if (!node_name) { ++ node_name = gen_node_name = id_generate(ID_BLOCK); ++ } else if (!id_wellformed(node_name)) { ++ /* ++ * Check for empty string or invalid characters, but not if it is ++ * generated (generated names use characters not available to the user) ++ */ ++ error_setg(errp, "Invalid node-name: '%s'", node_name); ++ return; ++ } ++ ++ /* takes care of avoiding namespaces collisions */ ++ if (blk_by_name(node_name)) { ++ error_setg(errp, "node-name=%s is conflicting with a device id", ++ node_name); ++ goto out; ++ } ++ ++ /* takes care of avoiding duplicates node names */ ++ if (bdrv_find_node(node_name)) { ++ error_setg(errp, "Duplicate nodes with node-name='%s'", node_name); ++ goto out; ++ } ++ ++ /* Make sure that the node name isn't truncated */ ++ if (strlen(node_name) >= sizeof(bs->node_name)) { ++ error_setg(errp, "Node name too long"); ++ goto out; ++ } ++ ++ /* copy node name into the bs and insert it into the graph list */ ++ pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); ++ QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); ++out: ++ g_free(gen_node_name); ++} ++ ++static int no_coroutine_fn GRAPH_UNLOCKED ++bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, ++ QDict *options, int open_flags, Error **errp) ++{ ++ Error *local_err = NULL; ++ int i, ret; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_assign_node_name(bs, node_name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ bs->drv = drv; ++ bs->opaque = g_malloc0(drv->instance_size); ++ ++ assert(!drv->bdrv_needs_filename || bs->filename[0]); ++ if (drv->bdrv_open) { ++ ret = drv->bdrv_open(bs, options, open_flags, &local_err); ++ } else { ++ ret = 0; ++ } ++ ++ if (ret < 0) { ++ if (local_err) { ++ error_propagate(errp, local_err); ++ } else if (bs->filename[0]) { ++ error_setg_errno(errp, -ret, "Could not open '%s'", bs->filename); ++ } else { ++ error_setg_errno(errp, -ret, "Could not open image"); ++ } ++ goto open_failed; ++ } ++ ++ assert(!(bs->supported_read_flags & ~BDRV_REQ_MASK)); ++ assert(!(bs->supported_write_flags & ~BDRV_REQ_MASK)); ++ ++ /* ++ * Always allow the BDRV_REQ_REGISTERED_BUF optimization hint. This saves ++ * drivers that pass read/write requests through to a child the trouble of ++ * declaring support explicitly. ++ * ++ * Drivers must not propagate this flag accidentally when they initiate I/O ++ * to a bounce buffer. That case should be rare though. ++ */ ++ bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF; ++ bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF; ++ ++ ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not refresh total sector count"); ++ return ret; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_refresh_limits(bs, NULL, &local_err); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ assert(bdrv_opt_mem_align(bs) != 0); ++ assert(bdrv_min_mem_align(bs) != 0); ++ assert(is_power_of_2(bs->bl.request_alignment)); ++ ++ for (i = 0; i < bs->quiesce_counter; i++) { ++ if (drv->bdrv_drain_begin) { ++ drv->bdrv_drain_begin(bs); ++ } ++ } ++ ++ return 0; ++open_failed: ++ bs->drv = NULL; ++ ++ bdrv_graph_wrlock(); ++ if (bs->file != NULL) { ++ bdrv_unref_child(bs, bs->file); ++ assert(!bs->file); ++ } ++ bdrv_graph_wrunlock(); ++ ++ g_free(bs->opaque); ++ bs->opaque = NULL; ++ return ret; ++} ++ ++/* ++ * Create and open a block node. ++ * ++ * @options is a QDict of options to pass to the block drivers, or NULL for an ++ * empty set of options. The reference to the QDict belongs to the block layer ++ * after the call (even on failure), so if the caller intends to reuse the ++ * dictionary, it needs to use qobject_ref() before calling bdrv_open. ++ */ ++BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, ++ const char *node_name, ++ QDict *options, int flags, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bs = bdrv_new(); ++ bs->open_flags = flags; ++ bs->options = options ?: qdict_new(); ++ bs->explicit_options = qdict_clone_shallow(bs->options); ++ bs->opaque = NULL; ++ ++ update_options_from_flags(bs->options, flags); ++ ++ ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); ++ if (ret < 0) { ++ qobject_unref(bs->explicit_options); ++ bs->explicit_options = NULL; ++ qobject_unref(bs->options); ++ bs->options = NULL; ++ bdrv_unref(bs); ++ return NULL; ++ } ++ ++ return bs; ++} ++ ++/* Create and open a block node. */ ++BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, ++ int flags, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_new_open_driver_opts(drv, node_name, NULL, flags, errp); ++} ++ ++QemuOptsList bdrv_runtime_opts = { ++ .name = "bdrv_common", ++ .head = QTAILQ_HEAD_INITIALIZER(bdrv_runtime_opts.head), ++ .desc = { ++ { ++ .name = "node-name", ++ .type = QEMU_OPT_STRING, ++ .help = "Node name of the block device node", ++ }, ++ { ++ .name = "driver", ++ .type = QEMU_OPT_STRING, ++ .help = "Block driver to use for the node", ++ }, ++ { ++ .name = BDRV_OPT_CACHE_DIRECT, ++ .type = QEMU_OPT_BOOL, ++ .help = "Bypass software writeback cache on the host", ++ }, ++ { ++ .name = BDRV_OPT_CACHE_NO_FLUSH, ++ .type = QEMU_OPT_BOOL, ++ .help = "Ignore flush requests", ++ }, ++ { ++ .name = BDRV_OPT_READ_ONLY, ++ .type = QEMU_OPT_BOOL, ++ .help = "Node is opened in read-only mode", ++ }, ++ { ++ .name = BDRV_OPT_AUTO_READ_ONLY, ++ .type = QEMU_OPT_BOOL, ++ .help = "Node can become read-only if opening read-write fails", ++ }, ++ { ++ .name = "detect-zeroes", ++ .type = QEMU_OPT_STRING, ++ .help = "try to optimize zero writes (off, on, unmap)", ++ }, ++ { ++ .name = BDRV_OPT_DISCARD, ++ .type = QEMU_OPT_STRING, ++ .help = "discard operation (ignore/off, unmap/on)", ++ }, ++ { ++ .name = BDRV_OPT_FORCE_SHARE, ++ .type = QEMU_OPT_BOOL, ++ .help = "always accept other writers (default: off)", ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++QemuOptsList bdrv_create_opts_simple = { ++ .name = "simple-create-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(bdrv_create_opts_simple.head), ++ .desc = { ++ { ++ .name = BLOCK_OPT_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Virtual disk size" ++ }, ++ { ++ .name = BLOCK_OPT_PREALLOC, ++ .type = QEMU_OPT_STRING, ++ .help = "Preallocation mode (allowed values: off)" ++ }, ++ { /* end of list */ } ++ } ++}; ++ ++/* ++ * Common part for opening disk images and files ++ * ++ * Removes all processed options from *options. ++ */ ++static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, ++ QDict *options, Error **errp) ++{ ++ int ret, open_flags; ++ const char *filename; ++ const char *driver_name = NULL; ++ const char *node_name = NULL; ++ const char *discard; ++ QemuOpts *opts; ++ BlockDriver *drv; ++ Error *local_err = NULL; ++ bool ro; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ assert(bs->file == NULL); ++ assert(options != NULL && bs->options != options); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ ++ update_flags_from_options(&bs->open_flags, opts); ++ ++ driver_name = qemu_opt_get(opts, "driver"); ++ drv = bdrv_find_format(driver_name); ++ assert(drv != NULL); ++ ++ bs->force_share = qemu_opt_get_bool(opts, BDRV_OPT_FORCE_SHARE, false); ++ ++ if (bs->force_share && (bs->open_flags & BDRV_O_RDWR)) { ++ error_setg(errp, ++ BDRV_OPT_FORCE_SHARE ++ "=on can only be used with read-only images"); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ ++ if (file != NULL) { ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_refresh_filename(blk_bs(file)); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ filename = blk_bs(file)->filename; ++ } else { ++ /* ++ * Caution: while qdict_get_try_str() is fine, getting ++ * non-string types would require more care. When @options ++ * come from -blockdev or blockdev_add, its members are typed ++ * according to the QAPI schema, but when they come from ++ * -drive, they're all QString. ++ */ ++ filename = qdict_get_try_str(options, "filename"); ++ } ++ ++ if (drv->bdrv_needs_filename && (!filename || !filename[0])) { ++ error_setg(errp, "The '%s' block driver requires a file name", ++ drv->format_name); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ ++ trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, ++ drv->format_name); ++ ++ ro = bdrv_is_read_only(bs); ++ ++ if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, ro)) { ++ if (!ro && bdrv_is_whitelisted(drv, true)) { ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_apply_auto_read_only(bs, NULL, NULL); ++ bdrv_graph_rdunlock_main_loop(); ++ } else { ++ ret = -ENOTSUP; ++ } ++ if (ret < 0) { ++ error_setg(errp, ++ !ro && bdrv_is_whitelisted(drv, true) ++ ? "Driver '%s' can only be used for read-only devices" ++ : "Driver '%s' is not whitelisted", ++ drv->format_name); ++ goto fail_opts; ++ } ++ } ++ ++ /* bdrv_new() and bdrv_close() make it so */ ++ assert(qatomic_read(&bs->copy_on_read) == 0); ++ ++ if (bs->open_flags & BDRV_O_COPY_ON_READ) { ++ if (!ro) { ++ bdrv_enable_copy_on_read(bs); ++ } else { ++ error_setg(errp, "Can't use copy-on-read on read-only device"); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ } ++ ++ discard = qemu_opt_get(opts, BDRV_OPT_DISCARD); ++ if (discard != NULL) { ++ if (bdrv_parse_discard_flags(discard, &bs->open_flags) != 0) { ++ error_setg(errp, "Invalid discard option"); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ } ++ ++ bs->detect_zeroes = ++ bdrv_parse_detect_zeroes(opts, bs->open_flags, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail_opts; ++ } ++ ++ if (filename != NULL) { ++ pstrcpy(bs->filename, sizeof(bs->filename), filename); ++ } else { ++ bs->filename[0] = '\0'; ++ } ++ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->filename); ++ ++ /* Open the image, either directly or using a protocol */ ++ open_flags = bdrv_open_flags(bs, bs->open_flags); ++ node_name = qemu_opt_get(opts, "node-name"); ++ ++ assert(!drv->protocol_name || file == NULL); ++ ret = bdrv_open_driver(bs, drv, node_name, options, open_flags, errp); ++ if (ret < 0) { ++ goto fail_opts; ++ } ++ ++ qemu_opts_del(opts); ++ return 0; ++ ++fail_opts: ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static QDict *parse_json_filename(const char *filename, Error **errp) ++{ ++ ERRP_GUARD(); ++ QObject *options_obj; ++ QDict *options; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ ret = strstart(filename, "json:", &filename); ++ assert(ret); ++ ++ options_obj = qobject_from_json(filename, errp); ++ if (!options_obj) { ++ error_prepend(errp, "Could not parse the JSON options: "); ++ return NULL; ++ } ++ ++ options = qobject_to(QDict, options_obj); ++ if (!options) { ++ qobject_unref(options_obj); ++ error_setg(errp, "Invalid JSON object given"); ++ return NULL; ++ } ++ ++ qdict_flatten(options); ++ ++ return options; ++} ++ ++static void parse_json_protocol(QDict *options, const char **pfilename, ++ Error **errp) ++{ ++ QDict *json_options; ++ Error *local_err = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ /* Parse json: pseudo-protocol */ ++ if (!*pfilename || !g_str_has_prefix(*pfilename, "json:")) { ++ return; ++ } ++ ++ json_options = parse_json_filename(*pfilename, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ /* Options given in the filename have lower priority than options ++ * specified directly */ ++ qdict_join(options, json_options, false); ++ qobject_unref(json_options); ++ *pfilename = NULL; ++} ++ ++/* ++ * Fills in default options for opening images and converts the legacy ++ * filename/flags pair to option QDict entries. ++ * The BDRV_O_PROTOCOL flag in *flags will be set or cleared accordingly if a ++ * block driver has been specified explicitly. ++ */ ++static int bdrv_fill_options(QDict **options, const char *filename, ++ int *flags, bool allow_parse_filename, ++ Error **errp) ++{ ++ const char *drvname; ++ bool protocol = *flags & BDRV_O_PROTOCOL; ++ bool parse_filename = false; ++ BlockDriver *drv = NULL; ++ Error *local_err = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * Caution: while qdict_get_try_str() is fine, getting non-string ++ * types would require more care. When @options come from ++ * -blockdev or blockdev_add, its members are typed according to ++ * the QAPI schema, but when they come from -drive, they're all ++ * QString. ++ */ ++ drvname = qdict_get_try_str(*options, "driver"); ++ if (drvname) { ++ drv = bdrv_find_format(drvname); ++ if (!drv) { ++ error_setg(errp, "Unknown driver '%s'", drvname); ++ return -ENOENT; ++ } ++ /* If the user has explicitly specified the driver, this choice should ++ * override the BDRV_O_PROTOCOL flag */ ++ protocol = drv->protocol_name; ++ } ++ ++ if (protocol) { ++ *flags |= BDRV_O_PROTOCOL; ++ } else { ++ *flags &= ~BDRV_O_PROTOCOL; ++ } ++ ++ /* Translate cache options from flags into options */ ++ update_options_from_flags(*options, *flags); ++ ++ /* Fetch the file name from the options QDict if necessary */ ++ if (protocol && filename) { ++ if (!qdict_haskey(*options, "filename")) { ++ qdict_put_str(*options, "filename", filename); ++ parse_filename = allow_parse_filename; ++ } else { ++ error_setg(errp, "Can't specify 'file' and 'filename' options at " ++ "the same time"); ++ return -EINVAL; ++ } ++ } ++ ++ /* Find the right block driver */ ++ /* See cautionary note on accessing @options above */ ++ filename = qdict_get_try_str(*options, "filename"); ++ ++ if (!drvname && protocol) { ++ if (filename) { ++ drv = bdrv_find_protocol(filename, parse_filename, errp); ++ if (!drv) { ++ return -EINVAL; ++ } ++ ++ drvname = drv->format_name; ++ qdict_put_str(*options, "driver", drvname); ++ } else { ++ error_setg(errp, "Must specify either driver or file"); ++ return -EINVAL; ++ } ++ } ++ ++ assert(drv || !protocol); ++ ++ /* Driver-specific filename parsing */ ++ if (drv && drv->bdrv_parse_filename && parse_filename) { ++ drv->bdrv_parse_filename(filename, *options, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ if (!drv->bdrv_needs_filename) { ++ qdict_del(*options, "filename"); ++ } ++ } ++ ++ return 0; ++} ++ ++typedef struct BlockReopenQueueEntry { ++ bool prepared; ++ BDRVReopenState state; ++ QTAILQ_ENTRY(BlockReopenQueueEntry) entry; ++} BlockReopenQueueEntry; ++ ++/* ++ * Return the flags that @bs will have after the reopens in @q have ++ * successfully completed. If @q is NULL (or @bs is not contained in @q), ++ * return the current flags. ++ */ ++static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs) ++{ ++ BlockReopenQueueEntry *entry; ++ ++ if (q != NULL) { ++ QTAILQ_FOREACH(entry, q, entry) { ++ if (entry->state.bs == bs) { ++ return entry->state.flags; ++ } ++ } ++ } ++ ++ return bs->open_flags; ++} ++ ++/* Returns whether the image file can be written to after the reopen queue @q ++ * has been successfully applied, or right now if @q is NULL. */ ++static bool bdrv_is_writable_after_reopen(BlockDriverState *bs, ++ BlockReopenQueue *q) ++{ ++ int flags = bdrv_reopen_get_flags(q, bs); ++ ++ return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR; ++} ++ ++/* ++ * Return whether the BDS can be written to. This is not necessarily ++ * the same as !bdrv_is_read_only(bs), as inactivated images may not ++ * be written to but do not count as read-only images. ++ */ ++bool bdrv_is_writable(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_is_writable_after_reopen(bs, NULL); ++} ++ ++static char *bdrv_child_user_desc(BdrvChild *c) ++{ ++ GLOBAL_STATE_CODE(); ++ return c->klass->get_parent_desc(c); ++} ++ ++/* ++ * Check that @a allows everything that @b needs. @a and @b must reference same ++ * child node. ++ */ ++static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp) ++{ ++ const char *child_bs_name; ++ g_autofree char *a_user = NULL; ++ g_autofree char *b_user = NULL; ++ g_autofree char *perms = NULL; ++ ++ assert(a->bs); ++ assert(a->bs == b->bs); ++ GLOBAL_STATE_CODE(); ++ ++ if ((b->perm & a->shared_perm) == b->perm) { ++ return true; ++ } ++ ++ child_bs_name = bdrv_get_node_name(b->bs); ++ a_user = bdrv_child_user_desc(a); ++ b_user = bdrv_child_user_desc(b); ++ perms = bdrv_perm_names(b->perm & ~a->shared_perm); ++ ++ error_setg(errp, "Permission conflict on node '%s': permissions '%s' are " ++ "both required by %s (uses node '%s' as '%s' child) and " ++ "unshared by %s (uses node '%s' as '%s' child).", ++ child_bs_name, perms, ++ b_user, child_bs_name, b->name, ++ a_user, child_bs_name, a->name); ++ ++ return false; ++} ++ ++static bool GRAPH_RDLOCK ++bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp) ++{ ++ BdrvChild *a, *b; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * During the loop we'll look at each pair twice. That's correct because ++ * bdrv_a_allow_b() is asymmetric and we should check each pair in both ++ * directions. ++ */ ++ QLIST_FOREACH(a, &bs->parents, next_parent) { ++ QLIST_FOREACH(b, &bs->parents, next_parent) { ++ if (a == b) { ++ continue; ++ } ++ ++ if (!bdrv_a_allow_b(a, b, errp)) { ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++static void GRAPH_RDLOCK ++bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, ++ BdrvChild *c, BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t parent_perm, uint64_t parent_shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ assert(bs->drv && bs->drv->bdrv_child_perm); ++ GLOBAL_STATE_CODE(); ++ bs->drv->bdrv_child_perm(bs, c, role, reopen_queue, ++ parent_perm, parent_shared, ++ nperm, nshared); ++ /* TODO Take force_share from reopen_queue */ ++ if (child_bs && child_bs->force_share) { ++ *nshared = BLK_PERM_ALL; ++ } ++} ++ ++/* ++ * Adds the whole subtree of @bs (including @bs itself) to the @list (except for ++ * nodes that are already in the @list, of course) so that final list is ++ * topologically sorted. Return the result (GSList @list object is updated, so ++ * don't use old reference after function call). ++ * ++ * On function start @list must be already topologically sorted and for any node ++ * in the @list the whole subtree of the node must be in the @list as well. The ++ * simplest way to satisfy this criteria: use only result of ++ * bdrv_topological_dfs() or NULL as @list parameter. ++ */ ++static GSList * GRAPH_RDLOCK ++bdrv_topological_dfs(GSList *list, GHashTable *found, BlockDriverState *bs) ++{ ++ BdrvChild *child; ++ g_autoptr(GHashTable) local_found = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!found) { ++ assert(!list); ++ found = local_found = g_hash_table_new(NULL, NULL); ++ } ++ ++ if (g_hash_table_contains(found, bs)) { ++ return list; ++ } ++ g_hash_table_add(found, bs); ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ list = bdrv_topological_dfs(list, found, child->bs); ++ } ++ ++ return g_slist_prepend(list, bs); ++} ++ ++typedef struct BdrvChildSetPermState { ++ BdrvChild *child; ++ uint64_t old_perm; ++ uint64_t old_shared_perm; ++} BdrvChildSetPermState; ++ ++static void bdrv_child_set_perm_abort(void *opaque) ++{ ++ BdrvChildSetPermState *s = opaque; ++ ++ GLOBAL_STATE_CODE(); ++ ++ s->child->perm = s->old_perm; ++ s->child->shared_perm = s->old_shared_perm; ++} ++ ++static TransactionActionDrv bdrv_child_set_pem_drv = { ++ .abort = bdrv_child_set_perm_abort, ++ .clean = g_free, ++}; ++ ++static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, ++ uint64_t shared, Transaction *tran) ++{ ++ BdrvChildSetPermState *s = g_new(BdrvChildSetPermState, 1); ++ GLOBAL_STATE_CODE(); ++ ++ *s = (BdrvChildSetPermState) { ++ .child = c, ++ .old_perm = c->perm, ++ .old_shared_perm = c->shared_perm, ++ }; ++ ++ c->perm = perm; ++ c->shared_perm = shared; ++ ++ tran_add(tran, &bdrv_child_set_pem_drv, s); ++} ++ ++static void GRAPH_RDLOCK bdrv_drv_set_perm_commit(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ uint64_t cumulative_perms, cumulative_shared_perms; ++ GLOBAL_STATE_CODE(); ++ ++ if (bs->drv->bdrv_set_perm) { ++ bdrv_get_cumulative_perm(bs, &cumulative_perms, ++ &cumulative_shared_perms); ++ bs->drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms); ++ } ++} ++ ++static void GRAPH_RDLOCK bdrv_drv_set_perm_abort(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ GLOBAL_STATE_CODE(); ++ ++ if (bs->drv->bdrv_abort_perm_update) { ++ bs->drv->bdrv_abort_perm_update(bs); ++ } ++} ++ ++TransactionActionDrv bdrv_drv_set_perm_drv = { ++ .abort = bdrv_drv_set_perm_abort, ++ .commit = bdrv_drv_set_perm_commit, ++}; ++ ++/* ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_drv_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, ++ Transaction *tran, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs->drv) { ++ return 0; ++ } ++ ++ if (bs->drv->bdrv_check_perm) { ++ int ret = bs->drv->bdrv_check_perm(bs, perm, shared_perm, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (tran) { ++ tran_add(tran, &bdrv_drv_set_perm_drv, bs); ++ } ++ ++ return 0; ++} ++ ++typedef struct BdrvReplaceChildState { ++ BdrvChild *child; ++ BlockDriverState *old_bs; ++} BdrvReplaceChildState; ++ ++static void GRAPH_WRLOCK bdrv_replace_child_commit(void *opaque) ++{ ++ BdrvReplaceChildState *s = opaque; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_schedule_unref(s->old_bs); ++} ++ ++static void GRAPH_WRLOCK bdrv_replace_child_abort(void *opaque) ++{ ++ BdrvReplaceChildState *s = opaque; ++ BlockDriverState *new_bs = s->child->bs; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_writable(); ++ ++ /* old_bs reference is transparently moved from @s to @s->child */ ++ if (!s->child->bs) { ++ /* ++ * The parents were undrained when removing old_bs from the child. New ++ * requests can't have been made, though, because the child was empty. ++ * ++ * TODO Make bdrv_replace_child_noperm() transactionable to avoid ++ * undraining the parent in the first place. Once this is done, having ++ * new_bs drained when calling bdrv_replace_child_tran() is not a ++ * requirement any more. ++ */ ++ bdrv_parent_drained_begin_single(s->child); ++ assert(!bdrv_parent_drained_poll_single(s->child)); ++ } ++ assert(s->child->quiesced_parent); ++ bdrv_replace_child_noperm(s->child, s->old_bs); ++ ++ bdrv_unref(new_bs); ++} ++ ++static TransactionActionDrv bdrv_replace_child_drv = { ++ .commit = bdrv_replace_child_commit, ++ .abort = bdrv_replace_child_abort, ++ .clean = g_free, ++}; ++ ++/* ++ * bdrv_replace_child_tran ++ * ++ * Note: real unref of old_bs is done only on commit. ++ * ++ * Both @child->bs and @new_bs (if non-NULL) must be drained. @new_bs must be ++ * kept drained until the transaction is completed. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ * ++ * The function doesn't update permissions, caller is responsible for this. ++ */ ++static void GRAPH_WRLOCK ++bdrv_replace_child_tran(BdrvChild *child, BlockDriverState *new_bs, ++ Transaction *tran) ++{ ++ BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1); ++ ++ assert(child->quiesced_parent); ++ assert(!new_bs || new_bs->quiesce_counter); ++ ++ *s = (BdrvReplaceChildState) { ++ .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(child, new_bs); ++ /* old_bs reference is transparently moved from @child to @s */ ++} ++ ++/* ++ * Refresh permissions in @bs subtree. The function is intended to be called ++ * after some graph modification that was done without permission update. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q, ++ Transaction *tran, Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ BdrvChild *c; ++ int ret; ++ uint64_t cumulative_perms, cumulative_shared_perms; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms); ++ ++ /* Write permissions never work with read-only images */ ++ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && ++ !bdrv_is_writable_after_reopen(bs, q)) ++ { ++ if (!bdrv_is_writable_after_reopen(bs, NULL)) { ++ error_setg(errp, "Block node is read-only"); ++ } else { ++ error_setg(errp, "Read-only block node '%s' cannot support " ++ "read-write users", bdrv_get_node_name(bs)); ++ } ++ ++ return -EPERM; ++ } ++ ++ /* ++ * Unaligned requests will automatically be aligned to bl.request_alignment ++ * and without RESIZE we can't extend requests to write to space beyond the ++ * end of the image, so it's required that the image size is aligned. ++ */ ++ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && ++ !(cumulative_perms & BLK_PERM_RESIZE)) ++ { ++ if ((bs->total_sectors * BDRV_SECTOR_SIZE) % bs->bl.request_alignment) { ++ error_setg(errp, "Cannot get 'write' permission without 'resize': " ++ "Image size is not a multiple of request " ++ "alignment"); ++ return -EPERM; ++ } ++ } ++ ++ /* Check this node */ ++ if (!drv) { ++ return 0; ++ } ++ ++ ret = bdrv_drv_set_perm(bs, cumulative_perms, cumulative_shared_perms, tran, ++ errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Drivers that never have children can omit .bdrv_child_perm() */ ++ if (!drv->bdrv_child_perm) { ++ assert(QLIST_EMPTY(&bs->children)); ++ return 0; ++ } ++ ++ /* Check all children */ ++ QLIST_FOREACH(c, &bs->children, next) { ++ uint64_t cur_perm, cur_shared; ++ ++ bdrv_child_perm(bs, c->bs, c, c->role, q, ++ cumulative_perms, cumulative_shared_perms, ++ &cur_perm, &cur_shared); ++ bdrv_child_set_perm(c, cur_perm, cur_shared, tran); ++ } ++ ++ return 0; ++} ++ ++/* ++ * @list is a product of bdrv_topological_dfs() (may be called several times) - ++ * a topologically sorted subgraph. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_do_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, ++ Error **errp) ++{ ++ int ret; ++ BlockDriverState *bs; ++ GLOBAL_STATE_CODE(); ++ ++ for ( ; list; list = list->next) { ++ bs = list->data; ++ ++ if (bdrv_parent_perms_conflict(bs, errp)) { ++ return -EINVAL; ++ } ++ ++ ret = bdrv_node_refresh_perm(bs, q, tran, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * @list is any list of nodes. List is completed by all subtrees and ++ * topologically sorted. It's not a problem if some node occurs in the @list ++ * several times. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q, Transaction *tran, ++ Error **errp) ++{ ++ g_autoptr(GHashTable) found = g_hash_table_new(NULL, NULL); ++ g_autoptr(GSList) refresh_list = NULL; ++ ++ for ( ; list; list = list->next) { ++ refresh_list = bdrv_topological_dfs(refresh_list, found, list->data); ++ } ++ ++ return bdrv_do_refresh_perms(refresh_list, q, tran, errp); ++} ++ ++void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, ++ uint64_t *shared_perm) ++{ ++ BdrvChild *c; ++ uint64_t cumulative_perms = 0; ++ uint64_t cumulative_shared_perms = BLK_PERM_ALL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ cumulative_perms |= c->perm; ++ cumulative_shared_perms &= c->shared_perm; ++ } ++ ++ *perm = cumulative_perms; ++ *shared_perm = cumulative_shared_perms; ++} ++ ++char *bdrv_perm_names(uint64_t perm) ++{ ++ struct perm_name { ++ uint64_t perm; ++ const char *name; ++ } permissions[] = { ++ { BLK_PERM_CONSISTENT_READ, "consistent read" }, ++ { BLK_PERM_WRITE, "write" }, ++ { BLK_PERM_WRITE_UNCHANGED, "write unchanged" }, ++ { BLK_PERM_RESIZE, "resize" }, ++ { 0, NULL } ++ }; ++ ++ GString *result = g_string_sized_new(30); ++ struct perm_name *p; ++ ++ for (p = permissions; p->name; p++) { ++ if (perm & p->perm) { ++ if (result->len > 0) { ++ g_string_append(result, ", "); ++ } ++ g_string_append(result, p->name); ++ } ++ } ++ ++ return g_string_free(result, FALSE); ++} ++ ++ ++/* ++ * @tran is allowed to be NULL. In this case no rollback is possible. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a reader lock for the graph. ++ */ ++static int GRAPH_RDLOCK ++bdrv_refresh_perms(BlockDriverState *bs, Transaction *tran, Error **errp) ++{ ++ int ret; ++ Transaction *local_tran = NULL; ++ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs); ++ GLOBAL_STATE_CODE(); ++ ++ if (!tran) { ++ tran = local_tran = tran_new(); ++ } ++ ++ ret = bdrv_do_refresh_perms(list, NULL, tran, errp); ++ ++ if (local_tran) { ++ tran_finalize(local_tran, ret); ++ } ++ ++ return ret; ++} ++ ++int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ Transaction *tran = tran_new(); ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_child_set_perm(c, perm, shared, tran); ++ ++ ret = bdrv_refresh_perms(c->bs, tran, &local_err); ++ ++ tran_finalize(tran, ret); ++ ++ if (ret < 0) { ++ if ((perm & ~c->perm) || (c->shared_perm & ~shared)) { ++ /* tighten permissions */ ++ error_propagate(errp, local_err); ++ } else { ++ /* ++ * Our caller may intend to only loosen restrictions and ++ * does not expect this function to fail. Errors are not ++ * fatal in such a case, so we can just hide them from our ++ * caller. ++ */ ++ error_free(local_err); ++ ret = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp) ++{ ++ uint64_t parent_perms, parent_shared; ++ uint64_t perms, shared; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_get_cumulative_perm(bs, &parent_perms, &parent_shared); ++ bdrv_child_perm(bs, c->bs, c, c->role, NULL, ++ parent_perms, parent_shared, &perms, &shared); ++ ++ return bdrv_child_try_set_perm(c, perms, shared, errp); ++} ++ ++/* ++ * Default implementation for .bdrv_child_perm() for block filters: ++ * Forward CONSISTENT_READ, WRITE, WRITE_UNCHANGED, and RESIZE to the ++ * filtered child. ++ */ ++static void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ GLOBAL_STATE_CODE(); ++ *nperm = perm & DEFAULT_PERM_PASSTHROUGH; ++ *nshared = (shared & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; ++} ++ ++static void bdrv_default_perms_for_cow(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ assert(role & BDRV_CHILD_COW); ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * We want consistent read from backing files if the parent needs it. ++ * No other operations are performed on backing files. ++ */ ++ perm &= BLK_PERM_CONSISTENT_READ; ++ ++ /* ++ * If the parent can deal with changing data, we're okay with a ++ * writable and resizable backing file. ++ * TODO Require !(perm & BLK_PERM_CONSISTENT_READ), too? ++ */ ++ if (shared & BLK_PERM_WRITE) { ++ shared = BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ } else { ++ shared = 0; ++ } ++ ++ shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; ++ ++ if (bs->open_flags & BDRV_O_INACTIVE) { ++ shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ } ++ ++ *nperm = perm; ++ *nshared = shared; ++} ++ ++static void bdrv_default_perms_for_storage(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ int flags; ++ ++ GLOBAL_STATE_CODE(); ++ assert(role & (BDRV_CHILD_METADATA | BDRV_CHILD_DATA)); ++ ++ flags = bdrv_reopen_get_flags(reopen_queue, bs); ++ ++ /* ++ * Apart from the modifications below, the same permissions are ++ * forwarded and left alone as for filters ++ */ ++ bdrv_filter_default_perms(bs, c, role, reopen_queue, ++ perm, shared, &perm, &shared); ++ ++ if (role & BDRV_CHILD_METADATA) { ++ /* Format drivers may touch metadata even if the guest doesn't write */ ++ if (bdrv_is_writable_after_reopen(bs, reopen_queue)) { ++ perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ } ++ ++ /* ++ * bs->file always needs to be consistent because of the ++ * metadata. We can never allow other users to resize or write ++ * to it. ++ */ ++ if (!(flags & BDRV_O_NO_IO)) { ++ perm |= BLK_PERM_CONSISTENT_READ; ++ } ++ shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); ++ } ++ ++ if (role & BDRV_CHILD_DATA) { ++ /* ++ * Technically, everything in this block is a subset of the ++ * BDRV_CHILD_METADATA path taken above, and so this could ++ * be an "else if" branch. However, that is not obvious, and ++ * this function is not performance critical, therefore we let ++ * this be an independent "if". ++ */ ++ ++ /* ++ * We cannot allow other users to resize the file because the ++ * format driver might have some assumptions about the size ++ * (e.g. because it is stored in metadata, or because the file ++ * is split into fixed-size data files). ++ */ ++ shared &= ~BLK_PERM_RESIZE; ++ ++ /* ++ * WRITE_UNCHANGED often cannot be performed as such on the ++ * data file. For example, the qcow2 driver may still need to ++ * write copied clusters on copy-on-read. ++ */ ++ if (perm & BLK_PERM_WRITE_UNCHANGED) { ++ perm |= BLK_PERM_WRITE; ++ } ++ ++ /* ++ * If the data file is written to, the format driver may ++ * expect to be able to resize it by writing beyond the EOF. ++ */ ++ if (perm & BLK_PERM_WRITE) { ++ perm |= BLK_PERM_RESIZE; ++ } ++ } ++ ++ if (bs->open_flags & BDRV_O_INACTIVE) { ++ shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ } ++ ++ *nperm = perm; ++ *nshared = shared; ++} ++ ++void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ GLOBAL_STATE_CODE(); ++ if (role & BDRV_CHILD_FILTERED) { ++ assert(!(role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | ++ BDRV_CHILD_COW))); ++ bdrv_filter_default_perms(bs, c, role, reopen_queue, ++ perm, shared, nperm, nshared); ++ } else if (role & BDRV_CHILD_COW) { ++ assert(!(role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA))); ++ bdrv_default_perms_for_cow(bs, c, role, reopen_queue, ++ perm, shared, nperm, nshared); ++ } else if (role & (BDRV_CHILD_METADATA | BDRV_CHILD_DATA)) { ++ bdrv_default_perms_for_storage(bs, c, role, reopen_queue, ++ perm, shared, nperm, nshared); ++ } else { ++ g_assert_not_reached(); ++ } ++} ++ ++uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm) ++{ ++ static const uint64_t permissions[] = { ++ [BLOCK_PERMISSION_CONSISTENT_READ] = BLK_PERM_CONSISTENT_READ, ++ [BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE, ++ [BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED, ++ [BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE, ++ }; ++ ++ QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX); ++ QEMU_BUILD_BUG_ON(1UL << ARRAY_SIZE(permissions) != BLK_PERM_ALL + 1); ++ ++ assert(qapi_perm < BLOCK_PERMISSION__MAX); ++ ++ return permissions[qapi_perm]; ++} ++ ++/* ++ * Replaces the node that a BdrvChild points to without updating permissions. ++ * ++ * If @new_bs is non-NULL, the parent of @child must already be drained through ++ * @child. ++ */ ++static void GRAPH_WRLOCK ++bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *new_bs) ++{ ++ BlockDriverState *old_bs = child->bs; ++ int new_bs_quiesce_counter; ++ ++ assert(!child->frozen); ++ ++ /* ++ * If we want to change the BdrvChild to point to a drained node as its new ++ * child->bs, we need to make sure that its new parent is drained, too. In ++ * other words, either child->quiesce_parent must already be true or we must ++ * be able to set it and keep the parent's quiesce_counter consistent with ++ * that, but without polling or starting new requests (this function ++ * guarantees that it doesn't poll, and starting new requests would be ++ * against the invariants of drain sections). ++ * ++ * To keep things simple, we pick the first option (child->quiesce_parent ++ * must already be true). We also generalise the rule a bit to make it ++ * easier to verify in callers and more likely to be covered in test cases: ++ * The parent must be quiesced through this child even if new_bs isn't ++ * currently drained. ++ * ++ * The only exception is for callers that always pass new_bs == NULL. In ++ * this case, we obviously never need to consider the case of a drained ++ * new_bs, so we can keep the callers simpler by allowing them not to drain ++ * the parent. ++ */ ++ assert(!new_bs || child->quiesced_parent); ++ assert(old_bs != new_bs); ++ GLOBAL_STATE_CODE(); ++ ++ if (old_bs && new_bs) { ++ assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); ++ } ++ ++ if (old_bs) { ++ if (child->klass->detach) { ++ child->klass->detach(child); ++ } ++ QLIST_REMOVE(child, next_parent); ++ } ++ ++ child->bs = new_bs; ++ ++ if (new_bs) { ++ QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); ++ if (child->klass->attach) { ++ child->klass->attach(child); ++ } ++ } ++ ++ /* ++ * If the parent was drained through this BdrvChild previously, but new_bs ++ * is not drained, allow requests to come in only after the new node has ++ * been attached. ++ */ ++ new_bs_quiesce_counter = (new_bs ? new_bs->quiesce_counter : 0); ++ if (!new_bs_quiesce_counter && child->quiesced_parent) { ++ bdrv_parent_drained_end_single(child); ++ } ++} ++ ++/** ++ * Free the given @child. ++ * ++ * The child must be empty (i.e. `child->bs == NULL`) and it must be ++ * unused (i.e. not in a children list). ++ */ ++static void bdrv_child_free(BdrvChild *child) ++{ ++ assert(!child->bs); ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ assert(!child->next.le_prev); /* not in children list */ ++ ++ g_free(child->name); ++ g_free(child); ++} ++ ++typedef struct BdrvAttachChildCommonState { ++ BdrvChild *child; ++ AioContext *old_parent_ctx; ++ AioContext *old_child_ctx; ++} BdrvAttachChildCommonState; ++ ++static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque) ++{ ++ BdrvAttachChildCommonState *s = opaque; ++ BlockDriverState *bs = s->child->bs; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_writable(); ++ ++ bdrv_replace_child_noperm(s->child, NULL); ++ ++ if (bdrv_get_aio_context(bs) != s->old_child_ctx) { ++ 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) { ++ Transaction *tran; ++ GHashTable *visited; ++ bool ret; ++ ++ tran = tran_new(); ++ ++ /* 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_schedule_unref(bs); ++ bdrv_child_free(s->child); ++} ++ ++static TransactionActionDrv bdrv_attach_child_common_drv = { ++ .abort = bdrv_attach_child_common_abort, ++ .clean = g_free, ++}; ++ ++/* ++ * Common part of attaching bdrv child to bs or to blk or to job ++ * ++ * Function doesn't update permissions, caller is responsible for this. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ * ++ * Returns new created child. ++ * ++ * Both @parent_bs and @child_bs can move to a different AioContext in this ++ * function. ++ */ ++static BdrvChild * GRAPH_WRLOCK ++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_class->get_parent_desc); ++ GLOBAL_STATE_CODE(); ++ ++ new_child = g_new(BdrvChild, 1); ++ *new_child = (BdrvChild) { ++ .bs = NULL, ++ .name = g_strdup(child_name), ++ .klass = child_class, ++ .role = child_role, ++ .perm = perm, ++ .shared_perm = shared_perm, ++ .opaque = opaque, ++ }; ++ ++ /* ++ * If the AioContexts don't match, first try to move the subtree of ++ * child_bs into the AioContext of the new parent. If this doesn't work, ++ * try moving the parent into the AioContext of child_bs instead. ++ */ ++ parent_ctx = bdrv_child_get_parent_aio_context(new_child); ++ if (child_ctx != parent_ctx) { ++ Error *local_err = NULL; ++ int ret = bdrv_try_change_aio_context(child_bs, parent_ctx, NULL, ++ &local_err); ++ ++ if (ret < 0 && child_class->change_aio_ctx) { ++ Transaction *aio_ctx_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, aio_ctx_tran, ++ NULL); ++ if (ret_child == true) { ++ error_free(local_err); ++ ret = 0; ++ } ++ tran_finalize(aio_ctx_tran, ret_child == true ? 0 : -1); ++ g_hash_table_destroy(visited); ++ } ++ ++ if (ret < 0) { ++ error_propagate(errp, local_err); ++ bdrv_child_free(new_child); ++ return NULL; ++ } ++ } ++ ++ bdrv_ref(child_bs); ++ /* ++ * Let every new BdrvChild start with a drained parent. Inserting the child ++ * in the graph with bdrv_replace_child_noperm() will undrain it if ++ * @child_bs is not drained. ++ * ++ * The child was only just created and is not yet visible in global state ++ * until bdrv_replace_child_noperm() inserts it into the graph, so nobody ++ * could have sent requests and polling is not necessary. ++ * ++ * Note that this means that the parent isn't fully drained yet, we only ++ * stop new requests from coming in. This is fine, we don't care about the ++ * old requests here, they are not for this child. If another place enters a ++ * drain section for the same parent, but wants it to be fully quiesced, it ++ * will not run most of the the code in .drained_begin() again (which is not ++ * a problem, we already did this), but it will still poll until the parent ++ * is fully quiesced, so it will not be negatively affected either. ++ */ ++ bdrv_parent_drained_begin_single(new_child); ++ bdrv_replace_child_noperm(new_child, child_bs); ++ ++ BdrvAttachChildCommonState *s = g_new(BdrvAttachChildCommonState, 1); ++ *s = (BdrvAttachChildCommonState) { ++ .child = new_child, ++ .old_parent_ctx = parent_ctx, ++ .old_child_ctx = child_ctx, ++ }; ++ tran_add(tran, &bdrv_attach_child_common_drv, s); ++ ++ return new_child; ++} ++ ++/* ++ * Function doesn't update permissions, caller is responsible for this. ++ * ++ * Both @parent_bs and @child_bs can move to a different AioContext in this ++ * function. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ */ ++static BdrvChild * GRAPH_WRLOCK ++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) ++{ ++ uint64_t perm, shared_perm; ++ ++ assert(parent_bs->drv); ++ GLOBAL_STATE_CODE(); ++ ++ 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 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); ++ ++ return bdrv_attach_child_common(child_bs, child_name, child_class, ++ child_role, perm, shared_perm, parent_bs, ++ tran, errp); ++} ++ ++/* ++ * This function steals the reference to child_bs from the caller. ++ * That reference is later dropped by bdrv_root_unref_child(). ++ * ++ * On failure NULL is returned, errp is set and the reference to ++ * child_bs is also dropped. ++ */ ++BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, ++ const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ uint64_t perm, uint64_t shared_perm, ++ void *opaque, Error **errp) ++{ ++ int ret; ++ BdrvChild *child; ++ Transaction *tran = tran_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ child = bdrv_attach_child_common(child_bs, child_name, child_class, ++ child_role, perm, shared_perm, opaque, ++ tran, errp); ++ if (!child) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = bdrv_refresh_perms(child_bs, tran, errp); ++ ++out: ++ tran_finalize(tran, ret); ++ ++ bdrv_schedule_unref(child_bs); ++ ++ return ret < 0 ? NULL : child; ++} ++ ++/* ++ * This function transfers the reference to child_bs from the caller ++ * to parent_bs. That reference is later dropped by parent_bs on ++ * bdrv_close() or if someone calls bdrv_unref_child(). ++ * ++ * On failure NULL is returned, errp is set and the reference to ++ * child_bs is also dropped. ++ */ ++BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, ++ BlockDriverState *child_bs, ++ const char *child_name, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ Error **errp) ++{ ++ int ret; ++ BdrvChild *child; ++ Transaction *tran = tran_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ child = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, ++ child_class, child_role, tran, errp); ++ if (!child) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = bdrv_refresh_perms(parent_bs, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++out: ++ tran_finalize(tran, ret); ++ ++ bdrv_schedule_unref(child_bs); ++ ++ return ret < 0 ? NULL : child; ++} ++ ++/* Callers must ensure that child->frozen is false. */ ++void bdrv_root_unref_child(BdrvChild *child) ++{ ++ BlockDriverState *child_bs = child->bs; ++ ++ GLOBAL_STATE_CODE(); ++ bdrv_replace_child_noperm(child, NULL); ++ bdrv_child_free(child); ++ ++ if (child_bs) { ++ /* ++ * Update permissions for old node. We're just taking a parent away, so ++ * we're loosening restrictions. Errors of permission update are not ++ * fatal in this case, ignore them. ++ */ ++ bdrv_refresh_perms(child_bs, NULL, NULL); ++ ++ /* ++ * When the parent requiring a non-default AioContext is removed, the ++ * node moves back to the main AioContext ++ */ ++ bdrv_try_change_aio_context(child_bs, qemu_get_aio_context(), NULL, ++ NULL); ++ } ++ ++ bdrv_schedule_unref(child_bs); ++} ++ ++typedef struct BdrvSetInheritsFrom { ++ BlockDriverState *bs; ++ BlockDriverState *old_inherits_from; ++} BdrvSetInheritsFrom; ++ ++static void bdrv_set_inherits_from_abort(void *opaque) ++{ ++ BdrvSetInheritsFrom *s = opaque; ++ ++ s->bs->inherits_from = s->old_inherits_from; ++} ++ ++static TransactionActionDrv bdrv_set_inherits_from_drv = { ++ .abort = bdrv_set_inherits_from_abort, ++ .clean = g_free, ++}; ++ ++/* @tran is allowed to be NULL. In this case no rollback is possible */ ++static void bdrv_set_inherits_from(BlockDriverState *bs, ++ BlockDriverState *new_inherits_from, ++ Transaction *tran) ++{ ++ if (tran) { ++ BdrvSetInheritsFrom *s = g_new(BdrvSetInheritsFrom, 1); ++ ++ *s = (BdrvSetInheritsFrom) { ++ .bs = bs, ++ .old_inherits_from = bs->inherits_from, ++ }; ++ ++ tran_add(tran, &bdrv_set_inherits_from_drv, s); ++ } ++ ++ bs->inherits_from = new_inherits_from; ++} ++ ++/** ++ * Clear all inherits_from pointers from children and grandchildren of ++ * @root that point to @root, where necessary. ++ * @tran is allowed to be NULL. In this case no rollback is possible ++ */ ++static void GRAPH_WRLOCK ++bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, ++ Transaction *tran) ++{ ++ BdrvChild *c; ++ ++ if (child->bs->inherits_from == root) { ++ /* ++ * Remove inherits_from only when the last reference between root and ++ * child->bs goes away. ++ */ ++ QLIST_FOREACH(c, &root->children, next) { ++ if (c != child && c->bs == child->bs) { ++ break; ++ } ++ } ++ if (c == NULL) { ++ bdrv_set_inherits_from(child->bs, NULL, tran); ++ } ++ } ++ ++ QLIST_FOREACH(c, &child->bs->children, next) { ++ bdrv_unset_inherits_from(root, c, tran); ++ } ++} ++ ++/* Callers must ensure that child->frozen is false. */ ++void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) ++{ ++ GLOBAL_STATE_CODE(); ++ if (child == NULL) { ++ return; ++ } ++ ++ bdrv_unset_inherits_from(parent, child, NULL); ++ bdrv_root_unref_child(child); ++} ++ ++ ++static void GRAPH_RDLOCK ++bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) ++{ ++ BdrvChild *c; ++ GLOBAL_STATE_CODE(); ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->klass->change_media) { ++ c->klass->change_media(c, load); ++ } ++ } ++} ++ ++/* Return true if you can reach parent going through child->inherits_from ++ * recursively. If parent or child are NULL, return false */ ++static bool bdrv_inherits_from_recursive(BlockDriverState *child, ++ BlockDriverState *parent) ++{ ++ while (child && child != parent) { ++ child = child->inherits_from; ++ } ++ ++ return child != NULL; ++} ++ ++/* ++ * Return the BdrvChildRole for @bs's backing child. bs->backing is ++ * mostly used for COW backing children (role = COW), but also for ++ * filtered children (role = FILTERED | PRIMARY). ++ */ ++static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) ++{ ++ if (bs->drv && bs->drv->is_filter) { ++ return BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY; ++ } else { ++ return BDRV_CHILD_COW; ++ } ++} ++ ++/* ++ * Sets the bs->backing or bs->file link of a BDS. A new reference is created; ++ * callers which don't need their own reference any more must call bdrv_unref(). ++ * ++ * If the respective child is already present (i.e. we're detaching a node), ++ * that child node must be drained. ++ * ++ * Function doesn't update permissions, caller is responsible for this. ++ * ++ * Both @parent_bs and @child_bs can move to a different AioContext in this ++ * function. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ */ ++static int GRAPH_WRLOCK ++bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, ++ BlockDriverState *child_bs, ++ bool is_backing, ++ Transaction *tran, Error **errp) ++{ ++ bool update_inherits_from = ++ bdrv_inherits_from_recursive(child_bs, parent_bs); ++ BdrvChild *child = is_backing ? parent_bs->backing : parent_bs->file; ++ BdrvChildRole role; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!parent_bs->drv) { ++ /* ++ * Node without drv is an object without a class :/. TODO: finally fix ++ * qcow2 driver to never clear bs->drv and implement format corruption ++ * handling in other way. ++ */ ++ error_setg(errp, "Node corrupted"); ++ return -EINVAL; ++ } ++ ++ if (child && child->frozen) { ++ error_setg(errp, "Cannot change frozen '%s' link from '%s' to '%s'", ++ child->name, parent_bs->node_name, child->bs->node_name); ++ return -EPERM; ++ } ++ ++ if (is_backing && !parent_bs->drv->is_filter && ++ !parent_bs->drv->supports_backing) ++ { ++ error_setg(errp, "Driver '%s' of node '%s' does not support backing " ++ "files", parent_bs->drv->format_name, parent_bs->node_name); ++ return -EINVAL; ++ } ++ ++ if (parent_bs->drv->is_filter) { ++ role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY; ++ } else if (is_backing) { ++ role = BDRV_CHILD_COW; ++ } else { ++ /* ++ * We only can use same role as it is in existing child. We don't have ++ * infrastructure to determine role of file child in generic way ++ */ ++ if (!child) { ++ error_setg(errp, "Cannot set file child to format node without " ++ "file child"); ++ return -EINVAL; ++ } ++ role = child->role; ++ } ++ ++ if (child) { ++ assert(child->bs->quiesce_counter); ++ bdrv_unset_inherits_from(parent_bs, child, tran); ++ bdrv_remove_child(child, tran); ++ } ++ ++ if (!child_bs) { ++ goto out; ++ } ++ ++ child = bdrv_attach_child_noperm(parent_bs, child_bs, ++ is_backing ? "backing" : "file", ++ &child_of_bds, role, ++ tran, errp); ++ if (!child) { ++ return -EINVAL; ++ } ++ ++ ++ /* ++ * If inherits_from pointed recursively to bs then let's update it to ++ * point directly to bs (else it will become NULL). ++ */ ++ if (update_inherits_from) { ++ bdrv_set_inherits_from(child_bs, parent_bs, tran); ++ } ++ ++out: ++ bdrv_refresh_limits(parent_bs, tran, NULL); ++ ++ return 0; ++} ++ ++/* ++ * Both @bs and @backing_hd can move to a different AioContext in this ++ * function. ++ * ++ * If a backing child is already present (i.e. we're detaching a node), that ++ * child node must be drained. ++ */ ++int bdrv_set_backing_hd_drained(BlockDriverState *bs, ++ BlockDriverState *backing_hd, ++ Error **errp) ++{ ++ int ret; ++ Transaction *tran = tran_new(); ++ ++ GLOBAL_STATE_CODE(); ++ assert(bs->quiesce_counter > 0); ++ if (bs->backing) { ++ assert(bs->backing->bs->quiesce_counter > 0); ++ } ++ ++ ret = bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = bdrv_refresh_perms(bs, tran, errp); ++out: ++ tran_finalize(tran, ret); ++ return ret; ++} ++ ++int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, ++ Error **errp) ++{ ++ BlockDriverState *drain_bs; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ drain_bs = bs->backing ? bs->backing->bs : bs; ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_ref(drain_bs); ++ bdrv_drained_begin(drain_bs); ++ bdrv_graph_wrlock(); ++ ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(drain_bs); ++ bdrv_unref(drain_bs); ++ ++ return ret; ++} ++ ++/* ++ * Opens the backing file for a BlockDriverState if not yet open ++ * ++ * bdref_key specifies the key for the image's BlockdevRef in the options QDict. ++ * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict ++ * itself, all options starting with "${bdref_key}." are considered part of the ++ * BlockdevRef. ++ * ++ * TODO Can this be unified with bdrv_open_image()? ++ */ ++int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, ++ const char *bdref_key, Error **errp) ++{ ++ ERRP_GUARD(); ++ char *backing_filename = NULL; ++ char *bdref_key_dot; ++ const char *reference = NULL; ++ int ret = 0; ++ bool implicit_backing = false; ++ BlockDriverState *backing_hd; ++ QDict *options; ++ QDict *tmp_parent_options = NULL; ++ Error *local_err = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bs->backing != NULL) { ++ goto free_exit; ++ } ++ ++ /* NULL means an empty set of options */ ++ if (parent_options == NULL) { ++ tmp_parent_options = qdict_new(); ++ parent_options = tmp_parent_options; ++ } ++ ++ bs->open_flags &= ~BDRV_O_NO_BACKING; ++ ++ bdref_key_dot = g_strdup_printf("%s.", bdref_key); ++ qdict_extract_subqdict(parent_options, &options, bdref_key_dot); ++ g_free(bdref_key_dot); ++ ++ /* ++ * Caution: while qdict_get_try_str() is fine, getting non-string ++ * types would require more care. When @parent_options come from ++ * -blockdev or blockdev_add, its members are typed according to ++ * the QAPI schema, but when they come from -drive, they're all ++ * QString. ++ */ ++ reference = qdict_get_try_str(parent_options, bdref_key); ++ if (reference || qdict_haskey(options, "file.filename")) { ++ /* keep backing_filename NULL */ ++ } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { ++ qobject_unref(options); ++ goto free_exit; ++ } else { ++ if (qdict_size(options) == 0) { ++ /* If the user specifies options that do not modify the ++ * backing file's behavior, we might still consider it the ++ * implicit backing file. But it's easier this way, and ++ * just specifying some of the backing BDS's options is ++ * only possible with -drive anyway (otherwise the QAPI ++ * schema forces the user to specify everything). */ ++ implicit_backing = !strcmp(bs->auto_backing_file, bs->backing_file); ++ } ++ ++ backing_filename = bdrv_get_full_backing_filename(bs, &local_err); ++ if (local_err) { ++ ret = -EINVAL; ++ error_propagate(errp, local_err); ++ qobject_unref(options); ++ goto free_exit; ++ } ++ } ++ ++ if (!bs->drv || !bs->drv->supports_backing) { ++ ret = -EINVAL; ++ error_setg(errp, "Driver doesn't support backing files"); ++ qobject_unref(options); ++ goto free_exit; ++ } ++ ++ if (!reference && ++ bs->backing_format[0] != '\0' && !qdict_haskey(options, "driver")) { ++ qdict_put_str(options, "driver", bs->backing_format); ++ } ++ ++ backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs, ++ &child_of_bds, bdrv_backing_role(bs), true, ++ errp); ++ if (!backing_hd) { ++ bs->open_flags |= BDRV_O_NO_BACKING; ++ error_prepend(errp, "Could not open backing file: "); ++ ret = -EINVAL; ++ goto free_exit; ++ } ++ ++ if (implicit_backing) { ++ bdrv_refresh_filename(backing_hd); ++ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), ++ backing_hd->filename); ++ } ++ ++ /* Hook up the backing file link; drop our reference, bs owns the ++ * backing_hd reference now */ ++ ret = bdrv_set_backing_hd(bs, backing_hd, errp); ++ bdrv_unref(backing_hd); ++ ++ if (ret < 0) { ++ goto free_exit; ++ } ++ ++ qdict_del(parent_options, bdref_key); ++ ++free_exit: ++ g_free(backing_filename); ++ qobject_unref(tmp_parent_options); ++ return ret; ++} ++ ++static BlockDriverState * ++bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, ++ BlockDriverState *parent, const BdrvChildClass *child_class, ++ BdrvChildRole child_role, bool allow_none, ++ bool parse_filename, Error **errp) ++{ ++ BlockDriverState *bs = NULL; ++ QDict *image_options; ++ char *bdref_key_dot; ++ const char *reference; ++ ++ assert(child_class != NULL); ++ ++ bdref_key_dot = g_strdup_printf("%s.", bdref_key); ++ qdict_extract_subqdict(options, &image_options, bdref_key_dot); ++ g_free(bdref_key_dot); ++ ++ /* ++ * Caution: while qdict_get_try_str() is fine, getting non-string ++ * types would require more care. When @options come from ++ * -blockdev or blockdev_add, its members are typed according to ++ * the QAPI schema, but when they come from -drive, they're all ++ * QString. ++ */ ++ reference = qdict_get_try_str(options, bdref_key); ++ if (!filename && !reference && !qdict_size(image_options)) { ++ if (!allow_none) { ++ error_setg(errp, "A block device must be specified for \"%s\"", ++ bdref_key); ++ } ++ qobject_unref(image_options); ++ goto done; ++ } ++ ++ bs = bdrv_open_inherit(filename, reference, image_options, 0, ++ parent, child_class, child_role, parse_filename, ++ errp); ++ if (!bs) { ++ goto done; ++ } ++ ++done: ++ qdict_del(options, bdref_key); ++ return bs; ++} ++ ++static BdrvChild *bdrv_open_child_common(const char *filename, ++ QDict *options, const char *bdref_key, ++ BlockDriverState *parent, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ bool allow_none, bool parse_filename, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class, ++ child_role, allow_none, parse_filename, errp); ++ if (bs == NULL) { ++ return NULL; ++ } ++ ++ bdrv_graph_wrlock(); ++ child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, ++ errp); ++ bdrv_graph_wrunlock(); ++ ++ return child; ++} ++ ++/* ++ * Opens a disk image whose options are given as BlockdevRef in another block ++ * device's options. ++ * ++ * If allow_none is true, no image will be opened if filename is false and no ++ * BlockdevRef is given. NULL will be returned, but errp remains unset. ++ * ++ * bdrev_key specifies the key for the image's BlockdevRef in the options QDict. ++ * That QDict has to be flattened; therefore, if the BlockdevRef is a QDict ++ * itself, all options starting with "${bdref_key}." are considered part of the ++ * BlockdevRef. ++ * ++ * The BlockdevRef will be removed from the options QDict. ++ * ++ * @parent can move to a different AioContext in this function. ++ */ ++BdrvChild *bdrv_open_child(const char *filename, ++ QDict *options, const char *bdref_key, ++ BlockDriverState *parent, ++ const BdrvChildClass *child_class, ++ BdrvChildRole child_role, ++ bool allow_none, Error **errp) ++{ ++ return bdrv_open_child_common(filename, options, bdref_key, parent, ++ child_class, child_role, allow_none, false, ++ errp); ++} ++ ++/* ++ * This does mostly the same as bdrv_open_child(), but for opening the primary ++ * child of a node. A notable difference from bdrv_open_child() is that it ++ * enables filename parsing for protocol names (including json:). ++ * ++ * @parent can move to a different AioContext in this function. ++ */ ++int bdrv_open_file_child(const char *filename, ++ QDict *options, const char *bdref_key, ++ BlockDriverState *parent, Error **errp) ++{ ++ BdrvChildRole role; ++ ++ /* commit_top and mirror_top don't use this function */ ++ assert(!parent->drv->filtered_child_is_backing); ++ role = parent->drv->is_filter ? ++ (BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY) : BDRV_CHILD_IMAGE; ++ ++ if (!bdrv_open_child_common(filename, options, bdref_key, parent, ++ &child_of_bds, role, false, true, errp)) ++ { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * TODO Future callers may need to specify parent/child_class in order for ++ * option inheritance to work. Existing callers use it for the root node. ++ */ ++BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) ++{ ++ BlockDriverState *bs = NULL; ++ QObject *obj = NULL; ++ QDict *qdict = NULL; ++ const char *reference = NULL; ++ Visitor *v = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (ref->type == QTYPE_QSTRING) { ++ reference = ref->u.reference; ++ } else { ++ BlockdevOptions *options = &ref->u.definition; ++ assert(ref->type == QTYPE_QDICT); ++ ++ v = qobject_output_visitor_new(&obj); ++ visit_type_BlockdevOptions(v, NULL, &options, &error_abort); ++ visit_complete(v, &obj); ++ ++ qdict = qobject_to(QDict, obj); ++ qdict_flatten(qdict); ++ ++ /* bdrv_open_inherit() defaults to the values in bdrv_flags (for ++ * compatibility with other callers) rather than what we want as the ++ * real defaults. Apply the defaults here instead. */ ++ qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); ++ qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); ++ qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off"); ++ qdict_set_default_str(qdict, BDRV_OPT_AUTO_READ_ONLY, "off"); ++ ++ } ++ ++ bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, false, ++ errp); ++ obj = NULL; ++ qobject_unref(obj); ++ visit_free(v); ++ return bs; ++} ++ ++static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, ++ int flags, ++ QDict *snapshot_options, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ g_autofree char *tmp_filename = NULL; ++ int64_t total_size; ++ QemuOpts *opts = NULL; ++ BlockDriverState *bs_snapshot = NULL; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* if snapshot, we create a temporary backing file and open it ++ instead of opening 'filename' directly */ ++ ++ /* Get the required size from the image */ ++ total_size = bdrv_getlength(bs); ++ ++ if (total_size < 0) { ++ error_setg_errno(errp, -total_size, "Could not get image size"); ++ goto out; ++ } ++ ++ /* Create the temporary image */ ++ tmp_filename = create_tmp_file(errp); ++ if (!tmp_filename) { ++ goto out; ++ } ++ ++ opts = qemu_opts_create(bdrv_qcow2.create_opts, NULL, 0, ++ &error_abort); ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_size, &error_abort); ++ ret = bdrv_create(&bdrv_qcow2, tmp_filename, opts, errp); ++ qemu_opts_del(opts); ++ if (ret < 0) { ++ error_prepend(errp, "Could not create temporary overlay '%s': ", ++ tmp_filename); ++ goto out; ++ } ++ ++ /* Prepare options QDict for the temporary file */ ++ qdict_put_str(snapshot_options, "file.driver", "file"); ++ qdict_put_str(snapshot_options, "file.filename", tmp_filename); ++ qdict_put_str(snapshot_options, "driver", "qcow2"); ++ ++ bs_snapshot = bdrv_open(NULL, NULL, snapshot_options, flags, errp); ++ snapshot_options = NULL; ++ if (!bs_snapshot) { ++ goto out; ++ } ++ ++ ret = bdrv_append(bs_snapshot, bs, errp); ++ if (ret < 0) { ++ bs_snapshot = NULL; ++ goto out; ++ } ++ ++out: ++ qobject_unref(snapshot_options); ++ return bs_snapshot; ++} ++ ++/* ++ * Opens a disk image (raw, qcow2, vmdk, ...) ++ * ++ * options is a QDict of options to pass to the block drivers, or NULL for an ++ * empty set of options. The reference to the QDict belongs to the block layer ++ * after the call (even on failure), so if the caller intends to reuse the ++ * dictionary, it needs to use qobject_ref() before calling bdrv_open. ++ * ++ * If *pbs is NULL, a new BDS will be created with a pointer to it stored there. ++ * If it is not NULL, the referenced BDS will be reused. ++ * ++ * The reference parameter may be used to specify an existing block device which ++ * should be opened. If specified, neither options nor a filename may be given, ++ * nor can an existing BDS be reused (that is, *pbs has to be NULL). ++ */ ++static BlockDriverState * no_coroutine_fn ++bdrv_open_inherit(const char *filename, const char *reference, QDict *options, ++ int flags, BlockDriverState *parent, ++ const BdrvChildClass *child_class, BdrvChildRole child_role, ++ bool parse_filename, Error **errp) ++{ ++ int ret; ++ BlockBackend *file = NULL; ++ BlockDriverState *bs; ++ BlockDriver *drv = NULL; ++ BdrvChild *child; ++ const char *drvname; ++ const char *backing; ++ Error *local_err = NULL; ++ QDict *snapshot_options = NULL; ++ int snapshot_flags = 0; ++ ++ assert(!child_class || !flags); ++ assert(!child_class == !parent); ++ GLOBAL_STATE_CODE(); ++ assert(!qemu_in_coroutine()); ++ ++ /* TODO We'll eventually have to take a writer lock in this function */ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (reference) { ++ bool options_non_empty = options ? qdict_size(options) : false; ++ qobject_unref(options); ++ ++ if (filename || options_non_empty) { ++ error_setg(errp, "Cannot reference an existing block device with " ++ "additional options or a new filename"); ++ return NULL; ++ } ++ ++ bs = bdrv_lookup_bs(reference, reference, errp); ++ if (!bs) { ++ return NULL; ++ } ++ ++ bdrv_ref(bs); ++ return bs; ++ } ++ ++ bs = bdrv_new(); ++ ++ /* NULL means an empty set of options */ ++ if (options == NULL) { ++ options = qdict_new(); ++ } ++ ++ /* json: syntax counts as explicit options, as if in the QDict */ ++ if (parse_filename) { ++ parse_json_protocol(options, &filename, &local_err); ++ if (local_err) { ++ goto fail; ++ } ++ } ++ ++ bs->explicit_options = qdict_clone_shallow(options); ++ ++ if (child_class) { ++ bool parent_is_format; ++ ++ if (parent->drv) { ++ parent_is_format = parent->drv->is_format; ++ } else { ++ /* ++ * parent->drv is not set yet because this node is opened for ++ * (potential) format probing. That means that @parent is going ++ * to be a format node. ++ */ ++ parent_is_format = true; ++ } ++ ++ bs->inherits_from = parent; ++ child_class->inherit_options(child_role, parent_is_format, ++ &flags, options, ++ parent->open_flags, parent->options); ++ } ++ ++ ret = bdrv_fill_options(&options, filename, &flags, parse_filename, ++ &local_err); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* ++ * Set the BDRV_O_RDWR and BDRV_O_ALLOW_RDWR flags. ++ * Caution: getting a boolean member of @options requires care. ++ * When @options come from -blockdev or blockdev_add, members are ++ * typed according to the QAPI schema, but when they come from ++ * -drive, they're all QString. ++ */ ++ if (g_strcmp0(qdict_get_try_str(options, BDRV_OPT_READ_ONLY), "on") && ++ !qdict_get_try_bool(options, BDRV_OPT_READ_ONLY, false)) { ++ flags |= (BDRV_O_RDWR | BDRV_O_ALLOW_RDWR); ++ } else { ++ flags &= ~BDRV_O_RDWR; ++ } ++ ++ if (flags & BDRV_O_SNAPSHOT) { ++ snapshot_options = qdict_new(); ++ bdrv_temp_snapshot_options(&snapshot_flags, snapshot_options, ++ flags, options); ++ /* Let bdrv_backing_options() override "read-only" */ ++ qdict_del(options, BDRV_OPT_READ_ONLY); ++ bdrv_inherited_options(BDRV_CHILD_COW, true, ++ &flags, options, flags, options); ++ } ++ ++ bs->open_flags = flags; ++ bs->options = options; ++ options = qdict_clone_shallow(options); ++ ++ /* Find the right image format driver */ ++ /* See cautionary note on accessing @options above */ ++ drvname = qdict_get_try_str(options, "driver"); ++ if (drvname) { ++ drv = bdrv_find_format(drvname); ++ if (!drv) { ++ error_setg(errp, "Unknown driver: '%s'", drvname); ++ goto fail; ++ } ++ } ++ ++ assert(drvname || !(flags & BDRV_O_PROTOCOL)); ++ ++ /* See cautionary note on accessing @options above */ ++ backing = qdict_get_try_str(options, "backing"); ++ if (qobject_to(QNull, qdict_get(options, "backing")) != NULL || ++ (backing && *backing == '\0')) ++ { ++ if (backing) { ++ warn_report("Use of \"backing\": \"\" is deprecated; " ++ "use \"backing\": null instead"); ++ } ++ flags |= BDRV_O_NO_BACKING; ++ qdict_del(bs->explicit_options, "backing"); ++ qdict_del(bs->options, "backing"); ++ qdict_del(options, "backing"); ++ } ++ ++ /* Open image file without format layer. This BlockBackend is only used for ++ * probing, the block drivers will do their own bdrv_open_child() for the ++ * same BDS, which is why we put the node name back into options. */ ++ if ((flags & BDRV_O_PROTOCOL) == 0) { ++ BlockDriverState *file_bs; ++ ++ file_bs = bdrv_open_child_bs(filename, options, "file", bs, ++ &child_of_bds, BDRV_CHILD_IMAGE, ++ true, true, &local_err); ++ if (local_err) { ++ goto fail; ++ } ++ if (file_bs != NULL) { ++ /* Not requesting BLK_PERM_CONSISTENT_READ because we're only ++ * looking at the header to guess the image format. This works even ++ * in cases where a guest would not see a consistent state. */ ++ AioContext *ctx = bdrv_get_aio_context(file_bs); ++ file = blk_new(ctx, 0, BLK_PERM_ALL); ++ blk_insert_bs(file, file_bs, &local_err); ++ bdrv_unref(file_bs); ++ ++ if (local_err) { ++ goto fail; ++ } ++ ++ qdict_put_str(options, "file", bdrv_get_node_name(file_bs)); ++ } ++ } ++ ++ /* Image format probing */ ++ bs->probed = !drv; ++ if (!drv && file) { ++ ret = find_image_format(file, filename, &drv, &local_err); ++ if (ret < 0) { ++ goto fail; ++ } ++ /* ++ * This option update would logically belong in bdrv_fill_options(), ++ * but we first need to open bs->file for the probing to work, while ++ * opening bs->file already requires the (mostly) final set of options ++ * so that cache mode etc. can be inherited. ++ * ++ * Adding the driver later is somewhat ugly, but it's not an option ++ * that would ever be inherited, so it's correct. We just need to make ++ * sure to update both bs->options (which has the full effective ++ * options for bs) and options (which has file.* already removed). ++ */ ++ qdict_put_str(bs->options, "driver", drv->format_name); ++ qdict_put_str(options, "driver", drv->format_name); ++ } else if (!drv) { ++ error_setg(errp, "Must specify either driver or file"); ++ goto fail; ++ } ++ ++ /* BDRV_O_PROTOCOL must be set iff a protocol BDS is about to be created */ ++ assert(!!(flags & BDRV_O_PROTOCOL) == !!drv->protocol_name); ++ /* file must be NULL if a protocol BDS is about to be created ++ * (the inverse results in an error message from bdrv_open_common()) */ ++ assert(!(flags & BDRV_O_PROTOCOL) || !file); ++ ++ /* Open the image */ ++ ret = bdrv_open_common(bs, file, options, &local_err); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (file) { ++ blk_unref(file); ++ file = NULL; ++ } ++ ++ /* If there is a backing file, use it */ ++ if ((flags & BDRV_O_NO_BACKING) == 0) { ++ ret = bdrv_open_backing_file(bs, options, "backing", &local_err); ++ if (ret < 0) { ++ goto close_and_fail; ++ } ++ } ++ ++ /* Remove all children options and references ++ * from bs->options and bs->explicit_options */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ char *child_key_dot; ++ child_key_dot = g_strdup_printf("%s.", child->name); ++ qdict_extract_subqdict(bs->explicit_options, NULL, child_key_dot); ++ qdict_extract_subqdict(bs->options, NULL, child_key_dot); ++ qdict_del(bs->explicit_options, child->name); ++ qdict_del(bs->options, child->name); ++ g_free(child_key_dot); ++ } ++ ++ /* Check if any unknown options were used */ ++ if (qdict_size(options) != 0) { ++ const QDictEntry *entry = qdict_first(options); ++ if (flags & BDRV_O_PROTOCOL) { ++ error_setg(errp, "Block protocol '%s' doesn't support the option " ++ "'%s'", drv->format_name, entry->key); ++ } else { ++ error_setg(errp, ++ "Block format '%s' does not support the option '%s'", ++ drv->format_name, entry->key); ++ } ++ ++ goto close_and_fail; ++ } ++ ++ bdrv_parent_cb_change_media(bs, true); ++ ++ qobject_unref(options); ++ options = NULL; ++ ++ /* For snapshot=on, create a temporary qcow2 overlay. bs points to the ++ * temporary snapshot afterwards. */ ++ if (snapshot_flags) { ++ BlockDriverState *snapshot_bs; ++ snapshot_bs = bdrv_append_temp_snapshot(bs, snapshot_flags, ++ snapshot_options, &local_err); ++ snapshot_options = NULL; ++ if (local_err) { ++ goto close_and_fail; ++ } ++ /* We are not going to return bs but the overlay on top of it ++ * (snapshot_bs); thus, we have to drop the strong reference to bs ++ * (which we obtained by calling bdrv_new()). bs will not be deleted, ++ * though, because the overlay still has a reference to it. */ ++ bdrv_unref(bs); ++ bs = snapshot_bs; ++ } ++ ++ return bs; ++ ++fail: ++ blk_unref(file); ++ qobject_unref(snapshot_options); ++ qobject_unref(bs->explicit_options); ++ qobject_unref(bs->options); ++ qobject_unref(options); ++ bs->options = NULL; ++ bs->explicit_options = NULL; ++ bdrv_unref(bs); ++ error_propagate(errp, local_err); ++ return NULL; ++ ++close_and_fail: ++ bdrv_unref(bs); ++ qobject_unref(snapshot_options); ++ qobject_unref(options); ++ error_propagate(errp, local_err); ++ return NULL; ++} ++ ++BlockDriverState *bdrv_open(const char *filename, const char *reference, ++ QDict *options, int flags, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ return bdrv_open_inherit(filename, reference, options, flags, NULL, ++ NULL, 0, true, errp); ++} ++ ++/* Return true if the NULL-terminated @list contains @str */ ++static bool is_str_in_list(const char *str, const char *const *list) ++{ ++ if (str && list) { ++ int i; ++ for (i = 0; list[i] != NULL; i++) { ++ if (!strcmp(str, list[i])) { ++ return true; ++ } ++ } ++ } ++ return false; ++} ++ ++/* ++ * Check that every option set in @bs->options is also set in ++ * @new_opts. ++ * ++ * Options listed in the common_options list and in ++ * @bs->drv->mutable_opts are skipped. ++ * ++ * Return 0 on success, otherwise return -EINVAL and set @errp. ++ */ ++static int bdrv_reset_options_allowed(BlockDriverState *bs, ++ const QDict *new_opts, Error **errp) ++{ ++ const QDictEntry *e; ++ /* These options are common to all block drivers and are handled ++ * in bdrv_reopen_prepare() so they can be left out of @new_opts */ ++ const char *const common_options[] = { ++ "node-name", "discard", "cache.direct", "cache.no-flush", ++ "read-only", "auto-read-only", "detect-zeroes", NULL ++ }; ++ ++ for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { ++ if (!qdict_haskey(new_opts, e->key) && ++ !is_str_in_list(e->key, common_options) && ++ !is_str_in_list(e->key, bs->drv->mutable_opts)) { ++ error_setg(errp, "Option '%s' cannot be reset " ++ "to its default value", e->key); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Returns true if @child can be reached recursively from @bs ++ */ ++static bool GRAPH_RDLOCK ++bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child) ++{ ++ BdrvChild *c; ++ ++ if (bs == child) { ++ return true; ++ } ++ ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (bdrv_recurse_has_child(c->bs, child)) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/* ++ * Adds a BlockDriverState to a simple queue for an atomic, transactional ++ * reopen of multiple devices. ++ * ++ * bs_queue can either be an existing BlockReopenQueue that has had QTAILQ_INIT ++ * already performed, or alternatively may be NULL a new BlockReopenQueue will ++ * be created and initialized. This newly created BlockReopenQueue should be ++ * passed back in for subsequent calls that are intended to be of the same ++ * atomic 'set'. ++ * ++ * bs is the BlockDriverState to add to the reopen queue. ++ * ++ * options contains the changed options for the associated bs ++ * (the BlockReopenQueue takes ownership) ++ * ++ * flags contains the open flags for the associated bs ++ * ++ * returns a pointer to bs_queue, which is either the newly allocated ++ * bs_queue, or the existing bs_queue being used. ++ * ++ * bs is drained here and undrained by bdrv_reopen_queue_free(). ++ * ++ * To be called with bs->aio_context locked. ++ */ ++static BlockReopenQueue * GRAPH_RDLOCK ++bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, ++ QDict *options, const BdrvChildClass *klass, ++ BdrvChildRole role, bool parent_is_format, ++ QDict *parent_options, int parent_flags, ++ bool keep_old_opts) ++{ ++ assert(bs != NULL); ++ ++ BlockReopenQueueEntry *bs_entry; ++ BdrvChild *child; ++ QDict *old_options, *explicit_options, *options_copy; ++ int flags; ++ QemuOpts *opts; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * Strictly speaking, draining is illegal under GRAPH_RDLOCK. We know that ++ * we've been called with bdrv_graph_rdlock_main_loop(), though, so it's ok ++ * in practice. ++ */ ++ bdrv_drained_begin(bs); ++ ++ if (bs_queue == NULL) { ++ bs_queue = g_new0(BlockReopenQueue, 1); ++ QTAILQ_INIT(bs_queue); ++ } ++ ++ if (!options) { ++ options = qdict_new(); ++ } ++ ++ /* Check if this BlockDriverState is already in the queue */ ++ QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ++ if (bs == bs_entry->state.bs) { ++ break; ++ } ++ } ++ ++ /* ++ * Precedence of options: ++ * 1. Explicitly passed in options (highest) ++ * 2. Retained from explicitly set options of bs ++ * 3. Inherited from parent node ++ * 4. Retained from effective options of bs ++ */ ++ ++ /* Old explicitly set values (don't overwrite by inherited value) */ ++ if (bs_entry || keep_old_opts) { ++ old_options = qdict_clone_shallow(bs_entry ? ++ bs_entry->state.explicit_options : ++ bs->explicit_options); ++ bdrv_join_options(bs, options, old_options); ++ qobject_unref(old_options); ++ } ++ ++ explicit_options = qdict_clone_shallow(options); ++ ++ /* Inherit from parent node */ ++ if (parent_options) { ++ flags = 0; ++ klass->inherit_options(role, parent_is_format, &flags, options, ++ parent_flags, parent_options); ++ } else { ++ flags = bdrv_get_flags(bs); ++ } ++ ++ if (keep_old_opts) { ++ /* Old values are used for options that aren't set yet */ ++ old_options = qdict_clone_shallow(bs->options); ++ bdrv_join_options(bs, options, old_options); ++ qobject_unref(old_options); ++ } ++ ++ /* We have the final set of options so let's update the flags */ ++ options_copy = qdict_clone_shallow(options); ++ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); ++ qemu_opts_absorb_qdict(opts, options_copy, NULL); ++ update_flags_from_options(&flags, opts); ++ qemu_opts_del(opts); ++ qobject_unref(options_copy); ++ ++ /* bdrv_open_inherit() sets and clears some additional flags internally */ ++ flags &= ~BDRV_O_PROTOCOL; ++ if (flags & BDRV_O_RDWR) { ++ flags |= BDRV_O_ALLOW_RDWR; ++ } ++ ++ if (!bs_entry) { ++ bs_entry = g_new0(BlockReopenQueueEntry, 1); ++ QTAILQ_INSERT_TAIL(bs_queue, bs_entry, entry); ++ } else { ++ qobject_unref(bs_entry->state.options); ++ qobject_unref(bs_entry->state.explicit_options); ++ } ++ ++ bs_entry->state.bs = bs; ++ bs_entry->state.options = options; ++ bs_entry->state.explicit_options = explicit_options; ++ bs_entry->state.flags = flags; ++ ++ /* ++ * If keep_old_opts is false then it means that unspecified ++ * options must be reset to their original value. We don't allow ++ * resetting 'backing' but we need to know if the option is ++ * missing in order to decide if we have to return an error. ++ */ ++ if (!keep_old_opts) { ++ bs_entry->state.backing_missing = ++ !qdict_haskey(options, "backing") && ++ !qdict_haskey(options, "backing.driver"); ++ } ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ QDict *new_child_options = NULL; ++ bool child_keep_old = keep_old_opts; ++ ++ /* reopen can only change the options of block devices that were ++ * implicitly created and inherited options. For other (referenced) ++ * block devices, a syntax like "backing.foo" results in an error. */ ++ if (child->bs->inherits_from != bs) { ++ continue; ++ } ++ ++ /* Check if the options contain a child reference */ ++ if (qdict_haskey(options, child->name)) { ++ const char *childref = qdict_get_try_str(options, child->name); ++ /* ++ * The current child must not be reopened if the child ++ * reference is null or points to a different node. ++ */ ++ if (g_strcmp0(childref, child->bs->node_name)) { ++ continue; ++ } ++ /* ++ * If the child reference points to the current child then ++ * reopen it with its existing set of options (note that ++ * it can still inherit new options from the parent). ++ */ ++ child_keep_old = true; ++ } else { ++ /* Extract child options ("child-name.*") */ ++ char *child_key_dot = g_strdup_printf("%s.", child->name); ++ qdict_extract_subqdict(explicit_options, NULL, child_key_dot); ++ qdict_extract_subqdict(options, &new_child_options, child_key_dot); ++ g_free(child_key_dot); ++ } ++ ++ bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, ++ child->klass, child->role, bs->drv->is_format, ++ options, flags, child_keep_old); ++ } ++ ++ return bs_queue; ++} ++ ++/* To be called with bs->aio_context locked */ ++BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, ++ BlockDriverState *bs, ++ QDict *options, bool keep_old_opts) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false, ++ NULL, 0, keep_old_opts); ++} ++ ++void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) ++{ ++ GLOBAL_STATE_CODE(); ++ if (bs_queue) { ++ BlockReopenQueueEntry *bs_entry, *next; ++ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { ++ bdrv_drained_end(bs_entry->state.bs); ++ qobject_unref(bs_entry->state.explicit_options); ++ qobject_unref(bs_entry->state.options); ++ g_free(bs_entry); ++ } ++ g_free(bs_queue); ++ } ++} ++ ++/* ++ * Reopen multiple BlockDriverStates atomically & transactionally. ++ * ++ * The queue passed in (bs_queue) must have been built up previous ++ * via bdrv_reopen_queue(). ++ * ++ * Reopens all BDS specified in the queue, with the appropriate ++ * flags. All devices are prepared for reopen, and failure of any ++ * device will cause all device changes to be abandoned, and intermediate ++ * data cleaned up. ++ * ++ * If all devices prepare successfully, then the changes are committed ++ * to all devices. ++ * ++ * All affected nodes must be drained between bdrv_reopen_queue() and ++ * bdrv_reopen_multiple(). ++ * ++ * To be called from the main thread, with all other AioContexts unlocked. ++ */ ++int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) ++{ ++ int ret = -1; ++ BlockReopenQueueEntry *bs_entry, *next; ++ Transaction *tran = tran_new(); ++ g_autoptr(GSList) refresh_list = NULL; ++ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ assert(bs_queue != NULL); ++ GLOBAL_STATE_CODE(); ++ ++ QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ++ ret = bdrv_flush(bs_entry->state.bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Error flushing drive"); ++ goto abort; ++ } ++ } ++ ++ QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ++ assert(bs_entry->state.bs->quiesce_counter > 0); ++ ret = bdrv_reopen_prepare(&bs_entry->state, bs_queue, tran, errp); ++ if (ret < 0) { ++ goto abort; ++ } ++ bs_entry->prepared = true; ++ } ++ ++ QTAILQ_FOREACH(bs_entry, bs_queue, entry) { ++ BDRVReopenState *state = &bs_entry->state; ++ ++ refresh_list = g_slist_prepend(refresh_list, state->bs); ++ if (state->old_backing_bs) { ++ refresh_list = g_slist_prepend(refresh_list, state->old_backing_bs); ++ } ++ if (state->old_file_bs) { ++ refresh_list = g_slist_prepend(refresh_list, state->old_file_bs); ++ } ++ } ++ ++ /* ++ * Note that file-posix driver rely on permission update done during reopen ++ * (even if no permission changed), because it wants "new" permissions for ++ * reconfiguring the fd and that's why it does it in raw_check_perm(), not ++ * in raw_reopen_prepare() which is called with "old" permissions. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_list_refresh_perms(refresh_list, bs_queue, tran, errp); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (ret < 0) { ++ goto abort; ++ } ++ ++ /* ++ * If we reach this point, we have success and just need to apply the ++ * changes. ++ * ++ * Reverse order is used to comfort qcow2 driver: on commit it need to write ++ * IN_USE flag to the image, to mark bitmaps in the image as invalid. But ++ * children are usually goes after parents in reopen-queue, so go from last ++ * to first element. ++ */ ++ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { ++ bdrv_reopen_commit(&bs_entry->state); ++ } ++ ++ bdrv_graph_wrlock(); ++ tran_commit(tran); ++ bdrv_graph_wrunlock(); ++ ++ QTAILQ_FOREACH_REVERSE(bs_entry, bs_queue, entry) { ++ BlockDriverState *bs = bs_entry->state.bs; ++ ++ if (bs->drv->bdrv_reopen_commit_post) { ++ bs->drv->bdrv_reopen_commit_post(&bs_entry->state); ++ } ++ } ++ ++ ret = 0; ++ goto cleanup; ++ ++abort: ++ bdrv_graph_wrlock(); ++ tran_abort(tran); ++ bdrv_graph_wrunlock(); ++ ++ QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { ++ if (bs_entry->prepared) { ++ bdrv_reopen_abort(&bs_entry->state); ++ } ++ } ++ ++cleanup: ++ bdrv_reopen_queue_free(bs_queue); ++ ++ return ret; ++} ++ ++int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, ++ Error **errp) ++{ ++ BlockReopenQueue *queue; ++ ++ GLOBAL_STATE_CODE(); ++ ++ queue = bdrv_reopen_queue(NULL, bs, opts, keep_old_opts); ++ ++ return bdrv_reopen_multiple(queue, errp); ++} ++ ++int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, ++ Error **errp) ++{ ++ QDict *opts = qdict_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only); ++ ++ return bdrv_reopen(bs, opts, true, errp); ++} ++ ++/* ++ * Take a BDRVReopenState and check if the value of 'backing' in the ++ * reopen_state->options QDict is valid or not. ++ * ++ * If 'backing' is missing from the QDict then return 0. ++ * ++ * If 'backing' contains the node name of the backing file of ++ * reopen_state->bs then return 0. ++ * ++ * If 'backing' contains a different node name (or is null) then check ++ * whether the current backing file can be replaced with the new one. ++ * If that's the case then reopen_state->replace_backing_bs is set to ++ * true and reopen_state->new_backing_bs contains a pointer to the new ++ * backing BlockDriverState (or NULL). ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ * ++ * Return 0 on success, otherwise return < 0 and set @errp. ++ * ++ * @reopen_state->bs can move to a different AioContext in this function. ++ */ ++static int GRAPH_UNLOCKED ++bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, ++ bool is_backing, Transaction *tran, ++ Error **errp) ++{ ++ BlockDriverState *bs = reopen_state->bs; ++ BlockDriverState *new_child_bs; ++ BlockDriverState *old_child_bs; ++ ++ const char *child_name = is_backing ? "backing" : "file"; ++ QObject *value; ++ const char *str; ++ bool has_child; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ value = qdict_get(reopen_state->options, child_name); ++ if (value == NULL) { ++ return 0; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ ++ switch (qobject_type(value)) { ++ case QTYPE_QNULL: ++ assert(is_backing); /* The 'file' option does not allow a null value */ ++ new_child_bs = NULL; ++ break; ++ case QTYPE_QSTRING: ++ str = qstring_get_str(qobject_to(QString, value)); ++ new_child_bs = bdrv_lookup_bs(NULL, str, errp); ++ if (new_child_bs == NULL) { ++ ret = -EINVAL; ++ goto out_rdlock; ++ } ++ ++ has_child = bdrv_recurse_has_child(new_child_bs, bs); ++ if (has_child) { ++ error_setg(errp, "Making '%s' a %s child of '%s' would create a " ++ "cycle", str, child_name, bs->node_name); ++ ret = -EINVAL; ++ goto out_rdlock; ++ } ++ break; ++ default: ++ /* ++ * The options QDict has been flattened, so 'backing' and 'file' ++ * do not allow any other data type here. ++ */ ++ g_assert_not_reached(); ++ } ++ ++ old_child_bs = is_backing ? child_bs(bs->backing) : child_bs(bs->file); ++ if (old_child_bs == new_child_bs) { ++ ret = 0; ++ goto out_rdlock; ++ } ++ ++ if (old_child_bs) { ++ if (bdrv_skip_implicit_filters(old_child_bs) == new_child_bs) { ++ ret = 0; ++ goto out_rdlock; ++ } ++ ++ if (old_child_bs->implicit) { ++ error_setg(errp, "Cannot replace implicit %s child of %s", ++ child_name, bs->node_name); ++ ret = -EPERM; ++ goto out_rdlock; ++ } ++ } ++ ++ if (bs->drv->is_filter && !old_child_bs) { ++ /* ++ * Filters always have a file or a backing child, so we are trying to ++ * change wrong child ++ */ ++ error_setg(errp, "'%s' is a %s filter node that does not support a " ++ "%s child", bs->node_name, bs->drv->format_name, child_name); ++ ret = -EINVAL; ++ goto out_rdlock; ++ } ++ ++ if (is_backing) { ++ reopen_state->old_backing_bs = old_child_bs; ++ } else { ++ reopen_state->old_file_bs = old_child_bs; ++ } ++ ++ if (old_child_bs) { ++ bdrv_ref(old_child_bs); ++ bdrv_drained_begin(old_child_bs); ++ } ++ ++ bdrv_graph_rdunlock_main_loop(); ++ bdrv_graph_wrlock(); ++ ++ ret = bdrv_set_file_or_backing_noperm(bs, new_child_bs, is_backing, ++ tran, errp); ++ ++ bdrv_graph_wrunlock(); ++ ++ if (old_child_bs) { ++ bdrv_drained_end(old_child_bs); ++ bdrv_unref(old_child_bs); ++ } ++ ++ return ret; ++ ++out_rdlock: ++ bdrv_graph_rdunlock_main_loop(); ++ return ret; ++} ++ ++/* ++ * Prepares a BlockDriverState for reopen. All changes are staged in the ++ * 'opaque' field of the BDRVReopenState, which is used and allocated by ++ * the block driver layer .bdrv_reopen_prepare() ++ * ++ * bs is the BlockDriverState to reopen ++ * flags are the new open flags ++ * queue is the reopen queue ++ * ++ * Returns 0 on success, non-zero on error. On error errp will be set ++ * as well. ++ * ++ * On failure, bdrv_reopen_abort() will be called to clean up any data. ++ * It is the responsibility of the caller to then call the abort() or ++ * commit() for any other BDS that have been left in a prepare() state ++ * ++ * After calling this function, the transaction @change_child_tran may only be ++ * completed while holding a writer lock for the graph. ++ */ ++static int GRAPH_UNLOCKED ++bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, ++ Transaction *change_child_tran, Error **errp) ++{ ++ int ret = -1; ++ int old_flags; ++ Error *local_err = NULL; ++ BlockDriver *drv; ++ QemuOpts *opts; ++ QDict *orig_reopen_opts; ++ char *discard = NULL; ++ bool read_only; ++ bool drv_prepared = false; ++ ++ assert(reopen_state != NULL); ++ assert(reopen_state->bs->drv != NULL); ++ GLOBAL_STATE_CODE(); ++ drv = reopen_state->bs->drv; ++ ++ /* This function and each driver's bdrv_reopen_prepare() remove ++ * entries from reopen_state->options as they are processed, so ++ * we need to make a copy of the original QDict. */ ++ orig_reopen_opts = qdict_clone_shallow(reopen_state->options); ++ ++ /* Process generic block layer options */ ++ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, reopen_state->options, errp)) { ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* This was already called in bdrv_reopen_queue_child() so the flags ++ * are up-to-date. This time we simply want to remove the options from ++ * QemuOpts in order to indicate that they have been processed. */ ++ old_flags = reopen_state->flags; ++ update_flags_from_options(&reopen_state->flags, opts); ++ assert(old_flags == reopen_state->flags); ++ ++ discard = qemu_opt_get_del(opts, BDRV_OPT_DISCARD); ++ if (discard != NULL) { ++ if (bdrv_parse_discard_flags(discard, &reopen_state->flags) != 0) { ++ error_setg(errp, "Invalid discard option"); ++ ret = -EINVAL; ++ goto error; ++ } ++ } ++ ++ reopen_state->detect_zeroes = ++ bdrv_parse_detect_zeroes(opts, reopen_state->flags, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ /* All other options (including node-name and driver) must be unchanged. ++ * Put them back into the QDict, so that they are checked at the end ++ * of this function. */ ++ qemu_opts_to_qdict(opts, reopen_state->options); ++ ++ /* If we are to stay read-only, do not allow permission change ++ * to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is ++ * not set, or if the BDS still has copy_on_read enabled */ ++ read_only = !(reopen_state->flags & BDRV_O_RDWR); ++ ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_can_set_read_only(reopen_state->bs, read_only, true, &local_err); ++ bdrv_graph_rdunlock_main_loop(); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ goto error; ++ } ++ ++ if (drv->bdrv_reopen_prepare) { ++ /* ++ * If a driver-specific option is missing, it means that we ++ * should reset it to its default value. ++ * But not all options allow that, so we need to check it first. ++ */ ++ ret = bdrv_reset_options_allowed(reopen_state->bs, ++ reopen_state->options, errp); ++ if (ret) { ++ goto error; ++ } ++ ++ ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err); ++ if (ret) { ++ if (local_err != NULL) { ++ error_propagate(errp, local_err); ++ } else { ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_refresh_filename(reopen_state->bs); ++ bdrv_graph_rdunlock_main_loop(); ++ error_setg(errp, "failed while preparing to reopen image '%s'", ++ reopen_state->bs->filename); ++ } ++ goto error; ++ } ++ } else { ++ /* It is currently mandatory to have a bdrv_reopen_prepare() ++ * handler for each supported drv. */ ++ bdrv_graph_rdlock_main_loop(); ++ error_setg(errp, "Block format '%s' used by node '%s' " ++ "does not support reopening files", drv->format_name, ++ bdrv_get_device_or_node_name(reopen_state->bs)); ++ bdrv_graph_rdunlock_main_loop(); ++ ret = -1; ++ goto error; ++ } ++ ++ drv_prepared = true; ++ ++ /* ++ * We must provide the 'backing' option if the BDS has a backing ++ * file or if the image file has a backing file name as part of ++ * its metadata. Otherwise the 'backing' option can be omitted. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ if (drv->supports_backing && reopen_state->backing_missing && ++ (reopen_state->bs->backing || reopen_state->bs->backing_file[0])) { ++ error_setg(errp, "backing is missing for '%s'", ++ reopen_state->bs->node_name); ++ bdrv_graph_rdunlock_main_loop(); ++ ret = -EINVAL; ++ goto error; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* ++ * Allow changing the 'backing' option. The new value can be ++ * either a reference to an existing node (using its node name) ++ * or NULL to simply detach the current backing file. ++ */ ++ ret = bdrv_reopen_parse_file_or_backing(reopen_state, true, ++ change_child_tran, errp); ++ if (ret < 0) { ++ goto error; ++ } ++ qdict_del(reopen_state->options, "backing"); ++ ++ /* Allow changing the 'file' option. In this case NULL is not allowed */ ++ ret = bdrv_reopen_parse_file_or_backing(reopen_state, false, ++ change_child_tran, errp); ++ if (ret < 0) { ++ goto error; ++ } ++ qdict_del(reopen_state->options, "file"); ++ ++ /* Options that are not handled are only okay if they are unchanged ++ * compared to the old state. It is expected that some options are only ++ * used for the initial open, but not reopen (e.g. filename) */ ++ if (qdict_size(reopen_state->options)) { ++ const QDictEntry *entry = qdict_first(reopen_state->options); ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ do { ++ QObject *new = entry->value; ++ QObject *old = qdict_get(reopen_state->bs->options, entry->key); ++ ++ /* Allow child references (child_name=node_name) as long as they ++ * point to the current child (i.e. everything stays the same). */ ++ if (qobject_type(new) == QTYPE_QSTRING) { ++ BdrvChild *child; ++ QLIST_FOREACH(child, &reopen_state->bs->children, next) { ++ if (!strcmp(child->name, entry->key)) { ++ break; ++ } ++ } ++ ++ if (child) { ++ if (!strcmp(child->bs->node_name, ++ qstring_get_str(qobject_to(QString, new)))) { ++ continue; /* Found child with this name, skip option */ ++ } ++ } ++ } ++ ++ /* ++ * TODO: When using -drive to specify blockdev options, all values ++ * will be strings; however, when using -blockdev, blockdev-add or ++ * filenames using the json:{} pseudo-protocol, they will be ++ * correctly typed. ++ * In contrast, reopening options are (currently) always strings ++ * (because you can only specify them through qemu-io; all other ++ * callers do not specify any options). ++ * Therefore, when using anything other than -drive to create a BDS, ++ * this cannot detect non-string options as unchanged, because ++ * qobject_is_equal() always returns false for objects of different ++ * type. In the future, this should be remedied by correctly typing ++ * all options. For now, this is not too big of an issue because ++ * the user can simply omit options which cannot be changed anyway, ++ * so they will stay unchanged. ++ */ ++ if (!qobject_is_equal(new, old)) { ++ error_setg(errp, "Cannot change the option '%s'", entry->key); ++ ret = -EINVAL; ++ goto error; ++ } ++ } while ((entry = qdict_next(reopen_state->options, entry))); ++ } ++ ++ ret = 0; ++ ++ /* Restore the original reopen_state->options QDict */ ++ qobject_unref(reopen_state->options); ++ reopen_state->options = qobject_ref(orig_reopen_opts); ++ ++error: ++ if (ret < 0 && drv_prepared) { ++ /* drv->bdrv_reopen_prepare() has succeeded, so we need to ++ * call drv->bdrv_reopen_abort() before signaling an error ++ * (bdrv_reopen_multiple() will not call bdrv_reopen_abort() ++ * when the respective bdrv_reopen_prepare() has failed) */ ++ if (drv->bdrv_reopen_abort) { ++ drv->bdrv_reopen_abort(reopen_state); ++ } ++ } ++ qemu_opts_del(opts); ++ qobject_unref(orig_reopen_opts); ++ g_free(discard); ++ return ret; ++} ++ ++/* ++ * Takes the staged changes for the reopen from bdrv_reopen_prepare(), and ++ * makes them final by swapping the staging BlockDriverState contents into ++ * the active BlockDriverState contents. ++ */ ++static void GRAPH_UNLOCKED bdrv_reopen_commit(BDRVReopenState *reopen_state) ++{ ++ BlockDriver *drv; ++ BlockDriverState *bs; ++ BdrvChild *child; ++ ++ assert(reopen_state != NULL); ++ bs = reopen_state->bs; ++ drv = bs->drv; ++ assert(drv != NULL); ++ GLOBAL_STATE_CODE(); ++ ++ /* If there are any driver level actions to take */ ++ if (drv->bdrv_reopen_commit) { ++ drv->bdrv_reopen_commit(reopen_state); ++ } ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* set BDS specific flags now */ ++ qobject_unref(bs->explicit_options); ++ qobject_unref(bs->options); ++ qobject_ref(reopen_state->explicit_options); ++ qobject_ref(reopen_state->options); ++ ++ bs->explicit_options = reopen_state->explicit_options; ++ bs->options = reopen_state->options; ++ bs->open_flags = reopen_state->flags; ++ bs->detect_zeroes = reopen_state->detect_zeroes; ++ ++ /* Remove child references from bs->options and bs->explicit_options. ++ * Child options were already removed in bdrv_reopen_queue_child() */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ qdict_del(bs->explicit_options, child->name); ++ qdict_del(bs->options, child->name); ++ } ++ /* backing is probably removed, so it's not handled by previous loop */ ++ qdict_del(bs->explicit_options, "backing"); ++ qdict_del(bs->options, "backing"); ++ ++ bdrv_refresh_limits(bs, NULL, NULL); ++ bdrv_refresh_total_sectors(bs, bs->total_sectors); ++} ++ ++/* ++ * Abort the reopen, and delete and free the staged changes in ++ * reopen_state ++ */ ++static void GRAPH_UNLOCKED bdrv_reopen_abort(BDRVReopenState *reopen_state) ++{ ++ BlockDriver *drv; ++ ++ assert(reopen_state != NULL); ++ drv = reopen_state->bs->drv; ++ assert(drv != NULL); ++ GLOBAL_STATE_CODE(); ++ ++ if (drv->bdrv_reopen_abort) { ++ drv->bdrv_reopen_abort(reopen_state); ++ } ++} ++ ++ ++static void bdrv_close(BlockDriverState *bs) ++{ ++ BdrvAioNotifier *ban, *ban_next; ++ BdrvChild *child, *next; ++ ++ GLOBAL_STATE_CODE(); ++ assert(!bs->refcnt); ++ ++ bdrv_drained_begin(bs); /* complete I/O */ ++ bdrv_flush(bs); ++ bdrv_drain(bs); /* in case flush left pending I/O */ ++ ++ if (bs->drv) { ++ if (bs->drv->bdrv_close) { ++ /* Must unfreeze all children, so bdrv_unref_child() works */ ++ bs->drv->bdrv_close(bs); ++ } ++ bs->drv = NULL; ++ } ++ ++ bdrv_graph_wrlock(); ++ QLIST_FOREACH_SAFE(child, &bs->children, next, next) { ++ bdrv_unref_child(bs, child); ++ } ++ ++ assert(!bs->backing); ++ assert(!bs->file); ++ bdrv_graph_wrunlock(); ++ ++ g_free(bs->opaque); ++ bs->opaque = NULL; ++ qatomic_set(&bs->copy_on_read, 0); ++ bs->backing_file[0] = '\0'; ++ bs->backing_format[0] = '\0'; ++ bs->total_sectors = 0; ++ bs->encrypted = false; ++ bs->sg = false; ++ qobject_unref(bs->options); ++ qobject_unref(bs->explicit_options); ++ bs->options = NULL; ++ bs->explicit_options = NULL; ++ qobject_unref(bs->full_open_options); ++ bs->full_open_options = NULL; ++ g_free(bs->block_status_cache); ++ bs->block_status_cache = NULL; ++ ++ bdrv_release_named_dirty_bitmaps(bs); ++ assert(QLIST_EMPTY(&bs->dirty_bitmaps)); ++ ++ QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) { ++ g_free(ban); ++ } ++ QLIST_INIT(&bs->aio_notifiers); ++ bdrv_drained_end(bs); ++ ++ /* ++ * If we're still inside some bdrv_drain_all_begin()/end() sections, end ++ * them now since this BDS won't exist anymore when bdrv_drain_all_end() ++ * gets called. ++ */ ++ if (bs->quiesce_counter) { ++ bdrv_drain_all_end_quiesce(bs); ++ } ++} ++ ++void bdrv_close_all(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(job_next(NULL) == NULL); ++ ++ /* Drop references from requests still in flight, such as canceled block ++ * jobs whose AIO context has not been polled yet */ ++ bdrv_drain_all(); ++ ++ blk_remove_all_bs(); ++ blockdev_close_all_bdrv_states(); ++ ++ assert(QTAILQ_EMPTY(&all_bdrv_states)); ++} ++ ++static bool GRAPH_RDLOCK should_update_child(BdrvChild *c, BlockDriverState *to) ++{ ++ GQueue *queue; ++ GHashTable *found; ++ bool ret; ++ ++ if (c->klass->stay_at_node) { ++ return false; ++ } ++ ++ /* If the child @c belongs to the BDS @to, replacing the current ++ * c->bs by @to would mean to create a loop. ++ * ++ * Such a case occurs when appending a BDS to a backing chain. ++ * For instance, imagine the following chain: ++ * ++ * guest device -> node A -> further backing chain... ++ * ++ * Now we create a new BDS B which we want to put on top of this ++ * chain, so we first attach A as its backing node: ++ * ++ * node B ++ * | ++ * v ++ * guest device -> node A -> further backing chain... ++ * ++ * Finally we want to replace A by B. When doing that, we want to ++ * replace all pointers to A by pointers to B -- except for the ++ * pointer from B because (1) that would create a loop, and (2) ++ * that pointer should simply stay intact: ++ * ++ * guest device -> node B ++ * | ++ * v ++ * node A -> further backing chain... ++ * ++ * In general, when replacing a node A (c->bs) by a node B (@to), ++ * if A is a child of B, that means we cannot replace A by B there ++ * because that would create a loop. Silently detaching A from B ++ * is also not really an option. So overall just leaving A in ++ * place there is the most sensible choice. ++ * ++ * We would also create a loop in any cases where @c is only ++ * indirectly referenced by @to. Prevent this by returning false ++ * if @c is found (by breadth-first search) anywhere in the whole ++ * subtree of @to. ++ */ ++ ++ ret = true; ++ found = g_hash_table_new(NULL, NULL); ++ g_hash_table_add(found, to); ++ queue = g_queue_new(); ++ g_queue_push_tail(queue, to); ++ ++ while (!g_queue_is_empty(queue)) { ++ BlockDriverState *v = g_queue_pop_head(queue); ++ BdrvChild *c2; ++ ++ QLIST_FOREACH(c2, &v->children, next) { ++ if (c2 == c) { ++ ret = false; ++ break; ++ } ++ ++ if (g_hash_table_contains(found, c2->bs)) { ++ continue; ++ } ++ ++ g_queue_push_tail(queue, c2->bs); ++ g_hash_table_add(found, c2->bs); ++ } ++ } ++ ++ g_queue_free(queue); ++ g_hash_table_destroy(found); ++ ++ return ret; ++} ++ ++static void bdrv_remove_child_commit(void *opaque) ++{ ++ GLOBAL_STATE_CODE(); ++ bdrv_child_free(opaque); ++} ++ ++static TransactionActionDrv bdrv_remove_child_drv = { ++ .commit = bdrv_remove_child_commit, ++}; ++ ++/* ++ * Function doesn't update permissions, caller is responsible for this. ++ * ++ * @child->bs (if non-NULL) must be drained. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ */ ++static void GRAPH_WRLOCK bdrv_remove_child(BdrvChild *child, Transaction *tran) ++{ ++ if (!child) { ++ return; ++ } ++ ++ if (child->bs) { ++ assert(child->quiesced_parent); ++ bdrv_replace_child_tran(child, NULL, tran); ++ } ++ ++ tran_add(tran, &bdrv_remove_child_drv, child); ++} ++ ++/* ++ * Both @from and @to (if non-NULL) must be drained. @to must be kept drained ++ * until the transaction is completed. ++ * ++ * After calling this function, the transaction @tran may only be completed ++ * while holding a writer lock for the graph. ++ */ ++static int GRAPH_WRLOCK ++bdrv_replace_node_noperm(BlockDriverState *from, ++ BlockDriverState *to, ++ bool auto_skip, Transaction *tran, ++ Error **errp) ++{ ++ BdrvChild *c, *next; ++ ++ GLOBAL_STATE_CODE(); ++ ++ assert(from->quiesce_counter); ++ assert(to->quiesce_counter); ++ ++ QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { ++ assert(c->bs == from); ++ if (!should_update_child(c, to)) { ++ if (auto_skip) { ++ continue; ++ } ++ error_setg(errp, "Should not change '%s' link to '%s'", ++ c->name, from->node_name); ++ return -EINVAL; ++ } ++ if (c->frozen) { ++ error_setg(errp, "Cannot change '%s' link to '%s'", ++ c->name, from->node_name); ++ return -EPERM; ++ } ++ bdrv_replace_child_tran(c, to, tran); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Switch all parents of @from to point to @to instead. @from and @to must be in ++ * the same AioContext and both must be drained. ++ * ++ * With auto_skip=true bdrv_replace_node_common skips updating from parents ++ * if it creates a parent-child relation loop or if parent is block-job. ++ * ++ * With auto_skip=false the error is returned if from has a parent which should ++ * not be updated. ++ * ++ * 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. ++ */ ++static int GRAPH_WRLOCK ++bdrv_replace_node_common(BlockDriverState *from, BlockDriverState *to, ++ bool auto_skip, bool detach_subchain, Error **errp) ++{ ++ Transaction *tran = tran_new(); ++ g_autoptr(GSList) refresh_list = NULL; ++ BlockDriverState *to_cow_parent = NULL; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ assert(from->quiesce_counter); ++ assert(to->quiesce_counter); ++ assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to)); ++ ++ if (detach_subchain) { ++ assert(bdrv_chain_contains(from, to)); ++ assert(from != to); ++ for (to_cow_parent = from; ++ bdrv_filter_or_cow_bs(to_cow_parent) != to; ++ to_cow_parent = bdrv_filter_or_cow_bs(to_cow_parent)) ++ { ++ ; ++ } ++ } ++ ++ /* ++ * Do the replacement without permission update. ++ * Replacement may influence the permissions, we should calculate new ++ * permissions based on new graph. If we fail, we'll roll-back the ++ * replacement. ++ */ ++ ret = bdrv_replace_node_noperm(from, to, auto_skip, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ if (detach_subchain) { ++ /* to_cow_parent is already drained because from is drained */ ++ bdrv_remove_child(bdrv_filter_or_cow_child(to_cow_parent), tran); ++ } ++ ++ refresh_list = g_slist_prepend(refresh_list, to); ++ refresh_list = g_slist_prepend(refresh_list, from); ++ ++ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = 0; ++ ++out: ++ tran_finalize(tran, ret); ++ return ret; ++} ++ ++int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, ++ Error **errp) ++{ ++ return bdrv_replace_node_common(from, to, true, false, errp); ++} ++ ++int bdrv_drop_filter(BlockDriverState *bs, Error **errp) ++{ ++ BlockDriverState *child_bs; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ child_bs = bdrv_filter_or_cow_bs(bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_drained_begin(child_bs); ++ bdrv_graph_wrlock(); ++ ret = bdrv_replace_node_common(bs, child_bs, true, true, errp); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(child_bs); ++ ++ return ret; ++} ++ ++/* ++ * Add new bs contents at the top of an image chain while the chain is ++ * live, while keeping required fields on the top layer. ++ * ++ * This will modify the BlockDriverState fields, and swap contents ++ * between bs_new and bs_top. Both bs_new and bs_top are modified. ++ * ++ * bs_new must not be attached to a BlockBackend and must not have backing ++ * child. ++ * ++ * This function does not create any image files. ++ */ ++int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, ++ Error **errp) ++{ ++ int ret; ++ BdrvChild *child; ++ Transaction *tran = tran_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ assert(!bs_new->backing); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_drained_begin(bs_top); ++ bdrv_drained_begin(bs_new); ++ ++ bdrv_graph_wrlock(); ++ ++ 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; ++ } ++ ++ ret = bdrv_replace_node_noperm(bs_top, bs_new, true, tran, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = bdrv_refresh_perms(bs_new, tran, errp); ++out: ++ tran_finalize(tran, ret); ++ ++ bdrv_refresh_limits(bs_top, NULL, NULL); ++ bdrv_graph_wrunlock(); ++ ++ bdrv_drained_end(bs_top); ++ bdrv_drained_end(bs_new); ++ ++ return ret; ++} ++ ++/* Not for empty child */ ++int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, ++ Error **errp) ++{ ++ int ret; ++ Transaction *tran = tran_new(); ++ g_autoptr(GSList) refresh_list = NULL; ++ BlockDriverState *old_bs = child->bs; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_ref(old_bs); ++ bdrv_drained_begin(old_bs); ++ bdrv_drained_begin(new_bs); ++ bdrv_graph_wrlock(); ++ ++ bdrv_replace_child_tran(child, new_bs, tran); ++ ++ refresh_list = g_slist_prepend(refresh_list, old_bs); ++ refresh_list = g_slist_prepend(refresh_list, new_bs); ++ ++ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp); ++ ++ tran_finalize(tran, ret); ++ ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(old_bs); ++ bdrv_drained_end(new_bs); ++ bdrv_unref(old_bs); ++ ++ return ret; ++} ++ ++static void bdrv_delete(BlockDriverState *bs) ++{ ++ assert(bdrv_op_blocker_is_empty(bs)); ++ assert(!bs->refcnt); ++ GLOBAL_STATE_CODE(); ++ ++ /* remove from list, if necessary */ ++ if (bs->node_name[0] != '\0') { ++ QTAILQ_REMOVE(&graph_bdrv_states, bs, node_list); ++ } ++ QTAILQ_REMOVE(&all_bdrv_states, bs, bs_list); ++ ++ bdrv_close(bs); ++ ++ qemu_mutex_destroy(&bs->reqs_lock); ++ ++ g_free(bs); ++} ++ ++ ++/* ++ * Replace @bs by newly created block node. ++ * ++ * @options is a QDict of options to pass to the block drivers, or NULL for an ++ * empty set of options. The reference to the QDict belongs to the block layer ++ * after the call (even on failure), so if the caller intends to reuse the ++ * dictionary, it needs to use qobject_ref() before calling bdrv_open. ++ * ++ * The caller must make sure that @bs stays in the same AioContext, i.e. ++ * @options must not refer to nodes in a different AioContext. ++ */ ++BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *options, ++ int flags, Error **errp) ++{ ++ ERRP_GUARD(); ++ int ret; ++ AioContext *ctx = bdrv_get_aio_context(bs); ++ BlockDriverState *new_node_bs = NULL; ++ const char *drvname, *node_name; ++ BlockDriver *drv; ++ ++ drvname = qdict_get_try_str(options, "driver"); ++ if (!drvname) { ++ error_setg(errp, "driver is not specified"); ++ goto fail; ++ } ++ ++ drv = bdrv_find_format(drvname); ++ if (!drv) { ++ error_setg(errp, "Unknown driver: '%s'", drvname); ++ goto fail; ++ } ++ ++ node_name = qdict_get_try_str(options, "node-name"); ++ ++ GLOBAL_STATE_CODE(); ++ ++ new_node_bs = bdrv_new_open_driver_opts(drv, node_name, options, flags, ++ errp); ++ assert(bdrv_get_aio_context(bs) == ctx); ++ ++ options = NULL; /* bdrv_new_open_driver() eats options */ ++ if (!new_node_bs) { ++ error_prepend(errp, "Could not create node: "); ++ goto fail; ++ } ++ ++ /* ++ * Make sure that @bs doesn't go away until we have successfully attached ++ * all of its parents to @new_node_bs and undrained it again. ++ */ ++ bdrv_ref(bs); ++ bdrv_drained_begin(bs); ++ bdrv_drained_begin(new_node_bs); ++ bdrv_graph_wrlock(); ++ ret = bdrv_replace_node(bs, new_node_bs, errp); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(new_node_bs); ++ bdrv_drained_end(bs); ++ bdrv_unref(bs); ++ ++ if (ret < 0) { ++ error_prepend(errp, "Could not replace node: "); ++ goto fail; ++ } ++ ++ return new_node_bs; ++ ++fail: ++ qobject_unref(options); ++ bdrv_unref(new_node_bs); ++ return NULL; ++} ++ ++/* ++ * Run consistency checks on an image ++ * ++ * Returns 0 if the check could be completed (it doesn't mean that the image is ++ * free of errors) or -errno when an internal error occurred. The results of the ++ * check are stored in res. ++ */ ++int coroutine_fn bdrv_co_check(BlockDriverState *bs, ++ BdrvCheckResult *res, BdrvCheckMode fix) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ if (bs->drv == NULL) { ++ return -ENOMEDIUM; ++ } ++ if (bs->drv->bdrv_co_check == NULL) { ++ return -ENOTSUP; ++ } ++ ++ memset(res, 0, sizeof(*res)); ++ return bs->drv->bdrv_co_check(bs, res, fix); ++} ++ ++/* ++ * Return values: ++ * 0 - success ++ * -EINVAL - backing format specified, but no file ++ * -ENOSPC - can't update the backing file because no space is left in the ++ * image file header ++ * -ENOTSUP - format driver doesn't support changing the backing file ++ */ ++int coroutine_fn ++bdrv_co_change_backing_file(BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt, bool require) ++{ ++ BlockDriver *drv = bs->drv; ++ int ret; ++ ++ IO_CODE(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* Backing file format doesn't make sense without a backing file */ ++ if (backing_fmt && !backing_file) { ++ return -EINVAL; ++ } ++ ++ if (require && backing_file && !backing_fmt) { ++ return -EINVAL; ++ } ++ ++ if (drv->bdrv_co_change_backing_file != NULL) { ++ ret = drv->bdrv_co_change_backing_file(bs, backing_file, backing_fmt); ++ } else { ++ ret = -ENOTSUP; ++ } ++ ++ if (ret == 0) { ++ pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); ++ pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); ++ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), ++ backing_file ?: ""); ++ } ++ return ret; ++} ++ ++/* ++ * Finds the first non-filter node above bs in the chain between ++ * active and bs. The returned node is either an immediate parent of ++ * bs, or there are only filter nodes between the two. ++ * ++ * Returns NULL if bs is not found in active's image chain, ++ * or if active == bs. ++ * ++ * Returns the bottommost base image if bs == NULL. ++ */ ++BlockDriverState *bdrv_find_overlay(BlockDriverState *active, ++ BlockDriverState *bs) ++{ ++ ++ GLOBAL_STATE_CODE(); ++ ++ bs = bdrv_skip_filters(bs); ++ active = bdrv_skip_filters(active); ++ ++ while (active) { ++ BlockDriverState *next = bdrv_backing_chain_next(active); ++ if (bs == next) { ++ return active; ++ } ++ active = next; ++ } ++ ++ return NULL; ++} ++ ++/* Given a BDS, searches for the base layer. */ ++BlockDriverState *bdrv_find_base(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ return bdrv_find_overlay(bs, NULL); ++} ++ ++/* ++ * Return true if at least one of the COW (backing) and filter links ++ * between @bs and @base is frozen. @errp is set if that's the case. ++ * @base must be reachable from @bs, or NULL. ++ */ ++static bool GRAPH_RDLOCK ++bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, ++ Error **errp) ++{ ++ BlockDriverState *i; ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (i = bs; i != base; i = child_bs(child)) { ++ child = bdrv_filter_or_cow_child(i); ++ ++ if (child && child->frozen) { ++ error_setg(errp, "Cannot change '%s' link from '%s' to '%s'", ++ child->name, i->node_name, child->bs->node_name); ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/* ++ * Freeze all COW (backing) and filter links between @bs and @base. ++ * If any of the links is already frozen the operation is aborted and ++ * none of the links are modified. ++ * @base must be reachable from @bs, or NULL. ++ * Returns 0 on success. On failure returns < 0 and sets @errp. ++ */ ++int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, ++ Error **errp) ++{ ++ BlockDriverState *i; ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bdrv_is_backing_chain_frozen(bs, base, errp)) { ++ return -EPERM; ++ } ++ ++ for (i = bs; i != base; i = child_bs(child)) { ++ child = bdrv_filter_or_cow_child(i); ++ if (child && child->bs->never_freeze) { ++ error_setg(errp, "Cannot freeze '%s' link to '%s'", ++ child->name, child->bs->node_name); ++ return -EPERM; ++ } ++ } ++ ++ for (i = bs; i != base; i = child_bs(child)) { ++ child = bdrv_filter_or_cow_child(i); ++ if (child) { ++ child->frozen = true; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Unfreeze all COW (backing) and filter links between @bs and @base. ++ * The caller must ensure that all links are frozen before using this ++ * function. ++ * @base must be reachable from @bs, or NULL. ++ */ ++void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base) ++{ ++ BlockDriverState *i; ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (i = bs; i != base; i = child_bs(child)) { ++ child = bdrv_filter_or_cow_child(i); ++ if (child) { ++ assert(child->frozen); ++ child->frozen = false; ++ } ++ } ++} ++ ++/* ++ * Drops images above 'base' up to and including 'top', and sets the image ++ * above 'top' to have base as its backing file. ++ * ++ * Requires that the overlay to 'top' is opened r/w, so that the backing file ++ * information in 'bs' can be properly updated. ++ * ++ * E.g., this will convert the following chain: ++ * bottom <- base <- intermediate <- top <- active ++ * ++ * to ++ * ++ * bottom <- base <- active ++ * ++ * It is allowed for bottom==base, in which case it converts: ++ * ++ * base <- intermediate <- top <- active ++ * ++ * to ++ * ++ * base <- active ++ * ++ * If backing_file_str is non-NULL, it will be used when modifying top's ++ * overlay image metadata. ++ * ++ * Error conditions: ++ * if active == top, that is considered an error ++ * ++ */ ++int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, ++ const char *backing_file_str, ++ bool backing_mask_protocol) ++{ ++ BlockDriverState *explicit_top = top; ++ bool update_inherits_from; ++ BdrvChild *c; ++ Error *local_err = NULL; ++ int ret = -EIO; ++ g_autoptr(GSList) updated_children = NULL; ++ GSList *p; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_ref(top); ++ bdrv_drained_begin(base); ++ bdrv_graph_wrlock(); ++ ++ if (!top->drv || !base->drv) { ++ goto exit_wrlock; ++ } ++ ++ /* Make sure that base is in the backing chain of top */ ++ if (!bdrv_chain_contains(top, base)) { ++ goto exit_wrlock; ++ } ++ ++ /* If 'base' recursively inherits from 'top' then we should set ++ * base->inherits_from to top->inherits_from after 'top' and all ++ * other intermediate nodes have been dropped. ++ * If 'top' is an implicit node (e.g. "commit_top") we should skip ++ * it because no one inherits from it. We use explicit_top for that. */ ++ explicit_top = bdrv_skip_implicit_filters(explicit_top); ++ update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top); ++ ++ /* success - we can delete the intermediate states, and link top->base */ ++ if (!backing_file_str) { ++ bdrv_refresh_filename(base); ++ backing_file_str = base->filename; ++ } ++ ++ QLIST_FOREACH(c, &top->parents, next_parent) { ++ updated_children = g_slist_prepend(updated_children, c); ++ } ++ ++ /* ++ * It seems correct to pass detach_subchain=true here, but it triggers ++ * one more yet not fixed bug, when due to nested aio_poll loop we switch to ++ * another drained section, which modify the graph (for example, removing ++ * the child, which we keep in updated_children list). So, it's a TODO. ++ * ++ * Note, bug triggered if pass detach_subchain=true here and run ++ * test-bdrv-drain. test_drop_intermediate_poll() test-case will crash. ++ * That's a FIXME. ++ */ ++ bdrv_replace_node_common(top, base, false, false, &local_err); ++ bdrv_graph_wrunlock(); ++ ++ if (local_err) { ++ error_report_err(local_err); ++ goto exit; ++ } ++ ++ for (p = updated_children; p; p = p->next) { ++ c = p->data; ++ ++ if (c->klass->update_filename) { ++ ret = c->klass->update_filename(c, base, backing_file_str, ++ backing_mask_protocol, ++ &local_err); ++ if (ret < 0) { ++ /* ++ * TODO: Actually, we want to rollback all previous iterations ++ * of this loop, and (which is almost impossible) previous ++ * bdrv_replace_node()... ++ * ++ * Note, that c->klass->update_filename may lead to permission ++ * update, so it's a bad idea to call it inside permission ++ * update transaction of bdrv_replace_node. ++ */ ++ error_report_err(local_err); ++ goto exit; ++ } ++ } ++ } ++ ++ if (update_inherits_from) { ++ base->inherits_from = explicit_top->inherits_from; ++ } ++ ++ ret = 0; ++ goto exit; ++ ++exit_wrlock: ++ bdrv_graph_wrunlock(); ++exit: ++ bdrv_drained_end(base); ++ bdrv_unref(top); ++ return ret; ++} ++ ++/** ++ * Implementation of BlockDriver.bdrv_co_get_allocated_file_size() that ++ * sums the size of all data-bearing children. (This excludes backing ++ * children.) ++ */ ++static int64_t coroutine_fn GRAPH_RDLOCK ++bdrv_sum_allocated_file_size(BlockDriverState *bs) ++{ ++ BdrvChild *child; ++ int64_t child_size, sum = 0; ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | ++ BDRV_CHILD_FILTERED)) ++ { ++ child_size = bdrv_co_get_allocated_file_size(child->bs); ++ if (child_size < 0) { ++ return child_size; ++ } ++ sum += child_size; ++ } ++ } ++ ++ return sum; ++} ++ ++/** ++ * Length of a allocated file in bytes. Sparse files are counted by actual ++ * allocated space. Return < 0 if error or unknown. ++ */ ++int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ if (drv->bdrv_co_get_allocated_file_size) { ++ return drv->bdrv_co_get_allocated_file_size(bs); ++ } ++ ++ if (drv->protocol_name) { ++ /* ++ * Protocol drivers default to -ENOTSUP (most of their data is ++ * not stored in any of their children (if they even have any), ++ * so there is no generic way to figure it out). ++ */ ++ return -ENOTSUP; ++ } else if (drv->is_filter) { ++ /* Filter drivers default to the size of their filtered child */ ++ return bdrv_co_get_allocated_file_size(bdrv_filter_bs(bs)); ++ } else { ++ /* Other drivers default to summing their children's sizes */ ++ return bdrv_sum_allocated_file_size(bs); ++ } ++} ++ ++/* ++ * bdrv_measure: ++ * @drv: Format driver ++ * @opts: Creation options for new image ++ * @in_bs: Existing image containing data for new image (may be NULL) ++ * @errp: Error object ++ * Returns: A #BlockMeasureInfo (free using qapi_free_BlockMeasureInfo()) ++ * or NULL on error ++ * ++ * Calculate file size required to create a new image. ++ * ++ * If @in_bs is given then space for allocated clusters and zero clusters ++ * from that image are included in the calculation. If @opts contains a ++ * backing file that is shared by @in_bs then backing clusters may be omitted ++ * from the calculation. ++ * ++ * If @in_bs is NULL then the calculation includes no allocated clusters ++ * unless a preallocation option is given in @opts. ++ * ++ * Note that @in_bs may use a different BlockDriver from @drv. ++ * ++ * If an error occurs the @errp pointer is set. ++ */ ++BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, ++ BlockDriverState *in_bs, Error **errp) ++{ ++ IO_CODE(); ++ if (!drv->bdrv_measure) { ++ error_setg(errp, "Block driver '%s' does not support size measurement", ++ drv->format_name); ++ return NULL; ++ } ++ ++ return drv->bdrv_measure(opts, in_bs, errp); ++} ++ ++/** ++ * Return number of sectors on success, -errno on error. ++ */ ++int64_t coroutine_fn bdrv_co_nb_sectors(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) ++ return -ENOMEDIUM; ++ ++ if (bs->bl.has_variable_length) { ++ int ret = bdrv_co_refresh_total_sectors(bs, bs->total_sectors); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ return bs->total_sectors; ++} ++ ++/* ++ * This wrapper is written by hand because this function is in the hot I/O path, ++ * via blk_get_geometry. ++ */ ++int64_t coroutine_mixed_fn bdrv_nb_sectors(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ ++ if (!drv) ++ return -ENOMEDIUM; ++ ++ if (bs->bl.has_variable_length) { ++ int ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return bs->total_sectors; ++} ++ ++/** ++ * Return length in bytes on success, -errno on error. ++ * The length is always a multiple of BDRV_SECTOR_SIZE. ++ */ ++int64_t coroutine_fn bdrv_co_getlength(BlockDriverState *bs) ++{ ++ int64_t ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ ret = bdrv_co_nb_sectors(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ if (ret > INT64_MAX / BDRV_SECTOR_SIZE) { ++ return -EFBIG; ++ } ++ return ret * BDRV_SECTOR_SIZE; ++} ++ ++bool bdrv_is_sg(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs->sg; ++} ++ ++/** ++ * Return whether the given node supports compressed writes. ++ */ ++bool bdrv_supports_compressed_writes(BlockDriverState *bs) ++{ ++ BlockDriverState *filtered; ++ IO_CODE(); ++ ++ if (!bs->drv || !block_driver_can_compress(bs->drv)) { ++ return false; ++ } ++ ++ filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ /* ++ * Filters can only forward compressed writes, so we have to ++ * check the child. ++ */ ++ return bdrv_supports_compressed_writes(filtered); ++ } ++ ++ return true; ++} ++ ++const char *bdrv_get_format_name(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs->drv ? bs->drv->format_name : NULL; ++} ++ ++static int qsort_strcmp(const void *a, const void *b) ++{ ++ return strcmp(*(char *const *)a, *(char *const *)b); ++} ++ ++void bdrv_iterate_format(void (*it)(void *opaque, const char *name), ++ void *opaque, bool read_only) ++{ ++ BlockDriver *drv; ++ int count = 0; ++ int i; ++ const char **formats = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(drv, &bdrv_drivers, list) { ++ if (drv->format_name) { ++ bool found = false; ++ ++ if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, read_only)) { ++ continue; ++ } ++ ++ i = count; ++ while (formats && i && !found) { ++ found = !strcmp(formats[--i], drv->format_name); ++ } ++ ++ if (!found) { ++ formats = g_renew(const char *, formats, count + 1); ++ formats[count++] = drv->format_name; ++ } ++ } ++ } ++ ++ for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); i++) { ++ const char *format_name = block_driver_modules[i].format_name; ++ ++ if (format_name) { ++ bool found = false; ++ int j = count; ++ ++ if (use_bdrv_whitelist && ++ !bdrv_format_is_whitelisted(format_name, read_only)) { ++ continue; ++ } ++ ++ while (formats && j && !found) { ++ found = !strcmp(formats[--j], format_name); ++ } ++ ++ if (!found) { ++ formats = g_renew(const char *, formats, count + 1); ++ formats[count++] = format_name; ++ } ++ } ++ } ++ ++ qsort(formats, count, sizeof(formats[0]), qsort_strcmp); ++ ++ for (i = 0; i < count; i++) { ++ it(opaque, formats[i]); ++ } ++ ++ g_free(formats); ++} ++ ++/* This function is to find a node in the bs graph */ ++BlockDriverState *bdrv_find_node(const char *node_name) ++{ ++ BlockDriverState *bs; ++ ++ assert(node_name); ++ GLOBAL_STATE_CODE(); ++ ++ QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { ++ if (!strcmp(node_name, bs->node_name)) { ++ return bs; ++ } ++ } ++ return NULL; ++} ++ ++/* Put this QMP function here so it can access the static graph_bdrv_states. */ ++BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, ++ Error **errp) ++{ ++ BlockDeviceInfoList *list; ++ BlockDriverState *bs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ list = NULL; ++ QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { ++ BlockDeviceInfo *info = bdrv_block_device_info(NULL, bs, flat, errp); ++ if (!info) { ++ qapi_free_BlockDeviceInfoList(list); ++ return NULL; ++ } ++ QAPI_LIST_PREPEND(list, info); ++ } ++ ++ return list; ++} ++ ++typedef struct XDbgBlockGraphConstructor { ++ XDbgBlockGraph *graph; ++ GHashTable *graph_nodes; ++} XDbgBlockGraphConstructor; ++ ++static XDbgBlockGraphConstructor *xdbg_graph_new(void) ++{ ++ XDbgBlockGraphConstructor *gr = g_new(XDbgBlockGraphConstructor, 1); ++ ++ gr->graph = g_new0(XDbgBlockGraph, 1); ++ gr->graph_nodes = g_hash_table_new(NULL, NULL); ++ ++ return gr; ++} ++ ++static XDbgBlockGraph *xdbg_graph_finalize(XDbgBlockGraphConstructor *gr) ++{ ++ XDbgBlockGraph *graph = gr->graph; ++ ++ g_hash_table_destroy(gr->graph_nodes); ++ g_free(gr); ++ ++ return graph; ++} ++ ++static uintptr_t xdbg_graph_node_num(XDbgBlockGraphConstructor *gr, void *node) ++{ ++ uintptr_t ret = (uintptr_t)g_hash_table_lookup(gr->graph_nodes, node); ++ ++ if (ret != 0) { ++ return ret; ++ } ++ ++ /* ++ * Start counting from 1, not 0, because 0 interferes with not-found (NULL) ++ * answer of g_hash_table_lookup. ++ */ ++ ret = g_hash_table_size(gr->graph_nodes) + 1; ++ g_hash_table_insert(gr->graph_nodes, node, (void *)ret); ++ ++ return ret; ++} ++ ++static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node, ++ XDbgBlockGraphNodeType type, const char *name) ++{ ++ XDbgBlockGraphNode *n; ++ ++ n = g_new0(XDbgBlockGraphNode, 1); ++ ++ n->id = xdbg_graph_node_num(gr, node); ++ n->type = type; ++ n->name = g_strdup(name); ++ ++ QAPI_LIST_PREPEND(gr->graph->nodes, n); ++} ++ ++static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, ++ const BdrvChild *child) ++{ ++ BlockPermission qapi_perm; ++ XDbgBlockGraphEdge *edge; ++ GLOBAL_STATE_CODE(); ++ ++ edge = g_new0(XDbgBlockGraphEdge, 1); ++ ++ edge->parent = xdbg_graph_node_num(gr, parent); ++ edge->child = xdbg_graph_node_num(gr, child->bs); ++ edge->name = g_strdup(child->name); ++ ++ for (qapi_perm = 0; qapi_perm < BLOCK_PERMISSION__MAX; qapi_perm++) { ++ uint64_t flag = bdrv_qapi_perm_to_blk_perm(qapi_perm); ++ ++ if (flag & child->perm) { ++ QAPI_LIST_PREPEND(edge->perm, qapi_perm); ++ } ++ if (flag & child->shared_perm) { ++ QAPI_LIST_PREPEND(edge->shared_perm, qapi_perm); ++ } ++ } ++ ++ QAPI_LIST_PREPEND(gr->graph->edges, edge); ++} ++ ++ ++XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) ++{ ++ BlockBackend *blk; ++ BlockJob *job; ++ BlockDriverState *bs; ++ BdrvChild *child; ++ XDbgBlockGraphConstructor *gr = xdbg_graph_new(); ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { ++ char *allocated_name = NULL; ++ const char *name = blk_name(blk); ++ ++ if (!*name) { ++ name = allocated_name = blk_get_attached_dev_id(blk); ++ } ++ xdbg_graph_add_node(gr, blk, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, ++ name); ++ g_free(allocated_name); ++ if (blk_root(blk)) { ++ xdbg_graph_add_edge(gr, blk, blk_root(blk)); ++ } ++ } ++ ++ WITH_JOB_LOCK_GUARD() { ++ for (job = block_job_next_locked(NULL); job; ++ job = block_job_next_locked(job)) { ++ GSList *el; ++ ++ xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, ++ job->job.id); ++ for (el = job->nodes; el; el = el->next) { ++ xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data); ++ } ++ } ++ } ++ ++ QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { ++ xdbg_graph_add_node(gr, bs, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, ++ bs->node_name); ++ QLIST_FOREACH(child, &bs->children, next) { ++ xdbg_graph_add_edge(gr, bs, child); ++ } ++ } ++ ++ return xdbg_graph_finalize(gr); ++} ++ ++BlockDriverState *bdrv_lookup_bs(const char *device, ++ const char *node_name, ++ Error **errp) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (device) { ++ blk = blk_by_name(device); ++ ++ if (blk) { ++ bs = blk_bs(blk); ++ if (!bs) { ++ error_setg(errp, "Device '%s' has no medium", device); ++ } ++ ++ return bs; ++ } ++ } ++ ++ if (node_name) { ++ bs = bdrv_find_node(node_name); ++ ++ if (bs) { ++ return bs; ++ } ++ } ++ ++ error_setg(errp, "Cannot find device=\'%s\' nor node-name=\'%s\'", ++ device ? device : "", ++ node_name ? node_name : ""); ++ return NULL; ++} ++ ++/* If 'base' is in the same chain as 'top', return true. Otherwise, ++ * return false. If either argument is NULL, return false. */ ++bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base) ++{ ++ ++ GLOBAL_STATE_CODE(); ++ ++ while (top && top != base) { ++ top = bdrv_filter_or_cow_bs(top); ++ } ++ ++ return top != NULL; ++} ++ ++BlockDriverState *bdrv_next_node(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs) { ++ return QTAILQ_FIRST(&graph_bdrv_states); ++ } ++ return QTAILQ_NEXT(bs, node_list); ++} ++ ++BlockDriverState *bdrv_next_all_states(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs) { ++ return QTAILQ_FIRST(&all_bdrv_states); ++ } ++ return QTAILQ_NEXT(bs, bs_list); ++} ++ ++const char *bdrv_get_node_name(const BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs->node_name; ++} ++ ++const char *bdrv_get_parent_name(const BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ const char *name; ++ IO_CODE(); ++ ++ /* If multiple parents have a name, just pick the first one. */ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->klass->get_name) { ++ name = c->klass->get_name(c); ++ if (name && *name) { ++ return name; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++/* TODO check what callers really want: bs->node_name or blk_name() */ ++const char *bdrv_get_device_name(const BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_get_parent_name(bs) ?: ""; ++} ++ ++/* This can be used to identify nodes that might not have a device ++ * name associated. Since node and device names live in the same ++ * namespace, the result is unambiguous. The exception is if both are ++ * absent, then this returns an empty (non-null) string. */ ++const char *bdrv_get_device_or_node_name(const BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_get_parent_name(bs) ?: bs->node_name; ++} ++ ++int bdrv_get_flags(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs->open_flags; ++} ++ ++int bdrv_has_zero_init_1(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return 1; ++} ++ ++int coroutine_mixed_fn bdrv_has_zero_init(BlockDriverState *bs) ++{ ++ BlockDriverState *filtered; ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs->drv) { ++ return 0; ++ } ++ ++ /* If BS is a copy on write image, it is initialized to ++ the contents of the base image, which may not be zeroes. */ ++ if (bdrv_cow_child(bs)) { ++ return 0; ++ } ++ if (bs->drv->bdrv_has_zero_init) { ++ return bs->drv->bdrv_has_zero_init(bs); ++ } ++ ++ filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ return bdrv_has_zero_init(filtered); ++ } ++ ++ /* safe default */ ++ return 0; ++} ++ ++bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ if (!(bs->open_flags & BDRV_O_UNMAP)) { ++ return false; ++ } ++ ++ return bs->supported_zero_flags & BDRV_REQ_MAY_UNMAP; ++} ++ ++void bdrv_get_backing_filename(BlockDriverState *bs, ++ char *filename, int filename_size) ++{ ++ IO_CODE(); ++ pstrcpy(filename, filename_size, bs->backing_file); ++} ++ ++int coroutine_fn bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ int ret; ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ if (!drv->bdrv_co_get_info) { ++ BlockDriverState *filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ return bdrv_co_get_info(filtered, bdi); ++ } ++ return -ENOTSUP; ++ } ++ memset(bdi, 0, sizeof(*bdi)); ++ ret = drv->bdrv_co_get_info(bs, bdi); ++ if (bdi->subcluster_size == 0) { ++ /* ++ * If the driver left this unset, subclusters are not supported. ++ * Then it is safe to treat each cluster as having only one subcluster. ++ */ ++ bdi->subcluster_size = bdi->cluster_size; ++ } ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (bdi->cluster_size > BDRV_MAX_ALIGNMENT) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, ++ Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ if (drv && drv->bdrv_get_specific_info) { ++ return drv->bdrv_get_specific_info(bs, errp); ++ } ++ return NULL; ++} ++ ++BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ if (!drv || !drv->bdrv_get_specific_stats) { ++ return NULL; ++ } ++ return drv->bdrv_get_specific_stats(bs); ++} ++ ++void coroutine_fn bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent event) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!bs || !bs->drv || !bs->drv->bdrv_co_debug_event) { ++ return; ++ } ++ ++ bs->drv->bdrv_co_debug_event(bs, event); ++} ++ ++static BlockDriverState * GRAPH_RDLOCK ++bdrv_find_debug_node(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) { ++ bs = bdrv_primary_bs(bs); ++ } ++ ++ if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) { ++ assert(bs->drv->bdrv_debug_remove_breakpoint); ++ return bs; ++ } ++ ++ return NULL; ++} ++ ++int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event, ++ const char *tag) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_find_debug_node(bs); ++ if (bs) { ++ return bs->drv->bdrv_debug_breakpoint(bs, event, tag); ++ } ++ ++ return -ENOTSUP; ++} ++ ++int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_find_debug_node(bs); ++ if (bs) { ++ return bs->drv->bdrv_debug_remove_breakpoint(bs, tag); ++ } ++ ++ return -ENOTSUP; ++} ++ ++int bdrv_debug_resume(BlockDriverState *bs, const char *tag) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) { ++ bs = bdrv_primary_bs(bs); ++ } ++ ++ if (bs && bs->drv && bs->drv->bdrv_debug_resume) { ++ return bs->drv->bdrv_debug_resume(bs, tag); ++ } ++ ++ return -ENOTSUP; ++} ++ ++bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) { ++ bs = bdrv_primary_bs(bs); ++ } ++ ++ if (bs && bs->drv && bs->drv->bdrv_debug_is_suspended) { ++ return bs->drv->bdrv_debug_is_suspended(bs, tag); ++ } ++ ++ return false; ++} ++ ++/* backing_file can either be relative, or absolute, or a protocol. If it is ++ * relative, it must be relative to the chain. So, passing in bs->filename ++ * from a BDS as backing_file should not be done, as that may be relative to ++ * the CWD rather than the chain. */ ++BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, ++ const char *backing_file) ++{ ++ char *filename_full = NULL; ++ char *backing_file_full = NULL; ++ char *filename_tmp = NULL; ++ int is_protocol = 0; ++ bool filenames_refreshed = false; ++ BlockDriverState *curr_bs = NULL; ++ BlockDriverState *retval = NULL; ++ BlockDriverState *bs_below; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!bs || !bs->drv || !backing_file) { ++ return NULL; ++ } ++ ++ filename_full = g_malloc(PATH_MAX); ++ backing_file_full = g_malloc(PATH_MAX); ++ ++ is_protocol = path_has_protocol(backing_file); ++ ++ /* ++ * Being largely a legacy function, skip any filters here ++ * (because filters do not have normal filenames, so they cannot ++ * match anyway; and allowing json:{} filenames is a bit out of ++ * scope). ++ */ ++ for (curr_bs = bdrv_skip_filters(bs); ++ bdrv_cow_child(curr_bs) != NULL; ++ curr_bs = bs_below) ++ { ++ bs_below = bdrv_backing_chain_next(curr_bs); ++ ++ if (bdrv_backing_overridden(curr_bs)) { ++ /* ++ * If the backing file was overridden, we can only compare ++ * directly against the backing node's filename. ++ */ ++ ++ if (!filenames_refreshed) { ++ /* ++ * This will automatically refresh all of the ++ * filenames in the rest of the backing chain, so we ++ * only need to do this once. ++ */ ++ bdrv_refresh_filename(bs_below); ++ filenames_refreshed = true; ++ } ++ ++ if (strcmp(backing_file, bs_below->filename) == 0) { ++ retval = bs_below; ++ break; ++ } ++ } else if (is_protocol || path_has_protocol(curr_bs->backing_file)) { ++ /* ++ * If either of the filename paths is actually a protocol, then ++ * compare unmodified paths; otherwise make paths relative. ++ */ ++ char *backing_file_full_ret; ++ ++ if (strcmp(backing_file, curr_bs->backing_file) == 0) { ++ retval = bs_below; ++ break; ++ } ++ /* Also check against the full backing filename for the image */ ++ backing_file_full_ret = bdrv_get_full_backing_filename(curr_bs, ++ NULL); ++ if (backing_file_full_ret) { ++ bool equal = strcmp(backing_file, backing_file_full_ret) == 0; ++ g_free(backing_file_full_ret); ++ if (equal) { ++ retval = bs_below; ++ break; ++ } ++ } ++ } else { ++ /* If not an absolute filename path, make it relative to the current ++ * image's filename path */ ++ filename_tmp = bdrv_make_absolute_filename(curr_bs, backing_file, ++ NULL); ++ /* We are going to compare canonicalized absolute pathnames */ ++ if (!filename_tmp || !realpath(filename_tmp, filename_full)) { ++ g_free(filename_tmp); ++ continue; ++ } ++ g_free(filename_tmp); ++ ++ /* We need to make sure the backing filename we are comparing against ++ * is relative to the current image filename (or absolute) */ ++ filename_tmp = bdrv_get_full_backing_filename(curr_bs, NULL); ++ if (!filename_tmp || !realpath(filename_tmp, backing_file_full)) { ++ g_free(filename_tmp); ++ continue; ++ } ++ g_free(filename_tmp); ++ ++ if (strcmp(backing_file_full, filename_full) == 0) { ++ retval = bs_below; ++ break; ++ } ++ } ++ } ++ ++ g_free(filename_full); ++ g_free(backing_file_full); ++ return retval; ++} ++ ++void bdrv_init(void) ++{ ++#ifdef CONFIG_BDRV_WHITELIST_TOOLS ++ use_bdrv_whitelist = 1; ++#endif ++ module_call_init(MODULE_INIT_BLOCK); ++} ++ ++void bdrv_init_with_whitelist(void) ++{ ++ use_bdrv_whitelist = 1; ++ bdrv_init(); ++} ++ ++int bdrv_activate(BlockDriverState *bs, Error **errp) ++{ ++ BdrvChild *child, *parent; ++ Error *local_err = NULL; ++ int ret; ++ BdrvDirtyBitmap *bm; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!bs->drv) { ++ return -ENOMEDIUM; ++ } ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ bdrv_activate(child->bs, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ } ++ ++ /* ++ * Update permissions, they may differ for inactive nodes. ++ * ++ * Note that the required permissions of inactive images are always a ++ * subset of the permissions required after activating the image. This ++ * allows us to just get the permissions upfront without restricting ++ * bdrv_co_invalidate_cache(). ++ * ++ * It also means that in error cases, we don't have to try and revert to ++ * the old permissions (which is an operation that could fail, too). We can ++ * just keep the extended permissions for the next time that an activation ++ * of the image is tried. ++ */ ++ if (bs->open_flags & BDRV_O_INACTIVE) { ++ bs->open_flags &= ~BDRV_O_INACTIVE; ++ ret = bdrv_refresh_perms(bs, NULL, errp); ++ if (ret < 0) { ++ bs->open_flags |= BDRV_O_INACTIVE; ++ return ret; ++ } ++ ++ ret = bdrv_invalidate_cache(bs, errp); ++ if (ret < 0) { ++ bs->open_flags |= BDRV_O_INACTIVE; ++ return ret; ++ } ++ ++ FOR_EACH_DIRTY_BITMAP(bs, bm) { ++ bdrv_dirty_bitmap_skip_store(bm, false); ++ } ++ ++ ret = bdrv_refresh_total_sectors(bs, bs->total_sectors); ++ if (ret < 0) { ++ bs->open_flags |= BDRV_O_INACTIVE; ++ error_setg_errno(errp, -ret, "Could not refresh total sector count"); ++ return ret; ++ } ++ } ++ ++ QLIST_FOREACH(parent, &bs->parents, next_parent) { ++ if (parent->klass->activate) { ++ parent->klass->activate(parent, &local_err); ++ if (local_err) { ++ bs->open_flags |= BDRV_O_INACTIVE; ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) ++{ ++ Error *local_err = NULL; ++ IO_CODE(); ++ ++ assert(!(bs->open_flags & BDRV_O_INACTIVE)); ++ assert_bdrv_graph_readable(); ++ ++ if (bs->drv->bdrv_co_invalidate_cache) { ++ bs->drv->bdrv_co_invalidate_cache(bs, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++void bdrv_activate_all(Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvNextIterator it; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ int ret; ++ ++ ret = bdrv_activate(bs, errp); ++ if (ret < 0) { ++ bdrv_next_cleanup(&it); ++ return; ++ } ++ } ++} ++ ++static bool GRAPH_RDLOCK ++bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) ++{ ++ BdrvChild *parent; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH(parent, &bs->parents, next_parent) { ++ if (parent->klass->parent_is_bds) { ++ BlockDriverState *parent_bs = parent->opaque; ++ if (!only_active || !(parent_bs->open_flags & BDRV_O_INACTIVE)) { ++ return true; ++ } ++ } ++ } ++ ++ return false; ++} ++ ++static int GRAPH_RDLOCK bdrv_inactivate_recurse(BlockDriverState *bs) ++{ ++ BdrvChild *child, *parent; ++ int ret; ++ uint64_t cumulative_perms, cumulative_shared_perms; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs->drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* Make sure that we don't inactivate a child before its parent. ++ * It will be covered by recursion from the yet active parent. */ ++ if (bdrv_has_bds_parent(bs, true)) { ++ return 0; ++ } ++ ++ assert(!(bs->open_flags & BDRV_O_INACTIVE)); ++ ++ /* Inactivate this node */ ++ if (bs->drv->bdrv_inactivate) { ++ ret = bs->drv->bdrv_inactivate(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ QLIST_FOREACH(parent, &bs->parents, next_parent) { ++ if (parent->klass->inactivate) { ++ ret = parent->klass->inactivate(parent); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ } ++ ++ bdrv_get_cumulative_perm(bs, &cumulative_perms, ++ &cumulative_shared_perms); ++ if (cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) { ++ /* Our inactive parents still need write access. Inactivation failed. */ ++ return -EPERM; ++ } ++ ++ bs->open_flags |= BDRV_O_INACTIVE; ++ ++ /* ++ * Update permissions, they may differ for inactive nodes. ++ * We only tried to loosen restrictions, so errors are not fatal, ignore ++ * them. ++ */ ++ bdrv_refresh_perms(bs, NULL, NULL); ++ ++ /* Recursively inactivate children */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ ret = bdrv_inactivate_recurse(child->bs); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++int bdrv_inactivate_all(void) ++{ ++ BlockDriverState *bs = NULL; ++ BdrvNextIterator it; ++ int ret = 0; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ /* Nodes with BDS parents are covered by recursion from the last ++ * parent that gets inactivated. Don't inactivate them a second ++ * time if that has already happened. */ ++ if (bdrv_has_bds_parent(bs, false)) { ++ continue; ++ } ++ ret = bdrv_inactivate_recurse(bs); ++ if (ret < 0) { ++ bdrv_next_cleanup(&it); ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++/**************************************************************/ ++/* removable device support */ ++ ++/** ++ * Return TRUE if the media is present ++ */ ++bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ BdrvChild *child; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return false; ++ } ++ if (drv->bdrv_co_is_inserted) { ++ return drv->bdrv_co_is_inserted(bs); ++ } ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (!bdrv_co_is_inserted(child->bs)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++/** ++ * If eject_flag is TRUE, eject the media. Otherwise, close the tray ++ */ ++void coroutine_fn bdrv_co_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (drv && drv->bdrv_co_eject) { ++ drv->bdrv_co_eject(bs, eject_flag); ++ } ++} ++ ++/** ++ * Lock or unlock the media (if it is locked, the user won't be able ++ * to eject it manually). ++ */ ++void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ BlockDriver *drv = bs->drv; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ trace_bdrv_lock_medium(bs, locked); ++ ++ if (drv && drv->bdrv_co_lock_medium) { ++ drv->bdrv_co_lock_medium(bs, locked); ++ } ++} ++ ++/* Get a reference to bs */ ++void bdrv_ref(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ bs->refcnt++; ++} ++ ++/* Release a previously grabbed reference to bs. ++ * If after releasing, reference count is zero, the BlockDriverState is ++ * deleted. */ ++void bdrv_unref(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs) { ++ return; ++ } ++ assert(bs->refcnt > 0); ++ if (--bs->refcnt == 0) { ++ bdrv_delete(bs); ++ } ++} ++ ++static void bdrv_schedule_unref_bh(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ ++ bdrv_unref(bs); ++} ++ ++/* ++ * Release a BlockDriverState reference while holding the graph write lock. ++ * ++ * Calling bdrv_unref() directly is forbidden while holding the graph lock ++ * because bdrv_close() both involves polling and taking the graph lock ++ * internally. bdrv_schedule_unref() instead delays decreasing the refcount and ++ * possibly closing @bs until the graph lock is released. ++ */ ++void bdrv_schedule_unref(BlockDriverState *bs) ++{ ++ if (!bs) { ++ return; ++ } ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_schedule_unref_bh, bs); ++} ++ ++struct BdrvOpBlocker { ++ Error *reason; ++ QLIST_ENTRY(BdrvOpBlocker) list; ++}; ++ ++bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) ++{ ++ BdrvOpBlocker *blocker; ++ GLOBAL_STATE_CODE(); ++ ++ assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); ++ if (!QLIST_EMPTY(&bs->op_blockers[op])) { ++ blocker = QLIST_FIRST(&bs->op_blockers[op]); ++ error_propagate_prepend(errp, error_copy(blocker->reason), ++ "Node '%s' is busy: ", ++ bdrv_get_device_or_node_name(bs)); ++ return true; ++ } ++ return false; ++} ++ ++void bdrv_op_block(BlockDriverState *bs, BlockOpType op, Error *reason) ++{ ++ BdrvOpBlocker *blocker; ++ GLOBAL_STATE_CODE(); ++ assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); ++ ++ blocker = g_new0(BdrvOpBlocker, 1); ++ blocker->reason = reason; ++ QLIST_INSERT_HEAD(&bs->op_blockers[op], blocker, list); ++} ++ ++void bdrv_op_unblock(BlockDriverState *bs, BlockOpType op, Error *reason) ++{ ++ BdrvOpBlocker *blocker, *next; ++ GLOBAL_STATE_CODE(); ++ assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); ++ QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, next) { ++ if (blocker->reason == reason) { ++ QLIST_REMOVE(blocker, list); ++ g_free(blocker); ++ } ++ } ++} ++ ++void bdrv_op_block_all(BlockDriverState *bs, Error *reason) ++{ ++ int i; ++ GLOBAL_STATE_CODE(); ++ for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { ++ bdrv_op_block(bs, i, reason); ++ } ++} ++ ++void bdrv_op_unblock_all(BlockDriverState *bs, Error *reason) ++{ ++ int i; ++ GLOBAL_STATE_CODE(); ++ for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { ++ bdrv_op_unblock(bs, i, reason); ++ } ++} ++ ++bool bdrv_op_blocker_is_empty(BlockDriverState *bs) ++{ ++ int i; ++ GLOBAL_STATE_CODE(); ++ for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) { ++ if (!QLIST_EMPTY(&bs->op_blockers[i])) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++/* ++ * Must not be called while holding the lock of an AioContext other than the ++ * current one. ++ */ ++void bdrv_img_create(const char *filename, const char *fmt, ++ const char *base_filename, const char *base_fmt, ++ char *options, uint64_t img_size, int flags, bool quiet, ++ Error **errp) ++{ ++ QemuOptsList *create_opts = NULL; ++ QemuOpts *opts = NULL; ++ const char *backing_fmt, *backing_file; ++ int64_t size; ++ BlockDriver *drv, *proto_drv; ++ Error *local_err = NULL; ++ int ret = 0; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Find driver and parse its options */ ++ drv = bdrv_find_format(fmt); ++ if (!drv) { ++ error_setg(errp, "Unknown file format '%s'", fmt); ++ return; ++ } ++ ++ proto_drv = bdrv_find_protocol(filename, true, errp); ++ if (!proto_drv) { ++ return; ++ } ++ ++ if (!drv->create_opts) { ++ error_setg(errp, "Format driver '%s' does not support image creation", ++ drv->format_name); ++ return; ++ } ++ ++ if (!proto_drv->create_opts) { ++ error_setg(errp, "Protocol driver '%s' does not support image creation", ++ proto_drv->format_name); ++ return; ++ } ++ ++ /* Create parameter list */ ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); ++ ++ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); ++ ++ /* Parse -o options */ ++ if (options) { ++ if (!qemu_opts_do_parse(opts, options, NULL, errp)) { ++ goto out; ++ } ++ } ++ ++ if (!qemu_opt_get(opts, BLOCK_OPT_SIZE)) { ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); ++ } else if (img_size != UINT64_C(-1)) { ++ error_setg(errp, "The image size must be specified only once"); ++ goto out; ++ } ++ ++ if (base_filename) { ++ if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, ++ NULL)) { ++ error_setg(errp, "Backing file not supported for file format '%s'", ++ fmt); ++ goto out; ++ } ++ } ++ ++ if (base_fmt) { ++ if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, NULL)) { ++ error_setg(errp, "Backing file format not supported for file " ++ "format '%s'", fmt); ++ goto out; ++ } ++ } ++ ++ backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); ++ if (backing_file) { ++ if (!strcmp(filename, backing_file)) { ++ error_setg(errp, "Error: Trying to create an image with the " ++ "same filename as the backing file"); ++ goto out; ++ } ++ if (backing_file[0] == '\0') { ++ error_setg(errp, "Expected backing file name, got empty string"); ++ goto out; ++ } ++ } ++ ++ backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); ++ ++ /* The size for the image must always be specified, unless we have a backing ++ * file and we have not been forbidden from opening it. */ ++ size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, img_size); ++ if (backing_file && !(flags & BDRV_O_NO_BACKING)) { ++ BlockDriverState *bs; ++ char *full_backing; ++ int back_flags; ++ QDict *backing_options = NULL; ++ ++ full_backing = ++ bdrv_get_full_backing_filename_from_filename(filename, backing_file, ++ &local_err); ++ if (local_err) { ++ goto out; ++ } ++ assert(full_backing); ++ ++ /* ++ * No need to do I/O here, which allows us to open encrypted ++ * backing images without needing the secret ++ */ ++ back_flags = flags; ++ back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); ++ back_flags |= BDRV_O_NO_IO; ++ ++ backing_options = qdict_new(); ++ if (backing_fmt) { ++ qdict_put_str(backing_options, "driver", backing_fmt); ++ } ++ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true); ++ ++ bs = bdrv_open(full_backing, NULL, backing_options, back_flags, ++ &local_err); ++ g_free(full_backing); ++ if (!bs) { ++ error_append_hint(&local_err, "Could not open backing image.\n"); ++ goto out; ++ } else { ++ if (!backing_fmt) { ++ error_setg(&local_err, ++ "Backing file specified without backing format"); ++ error_append_hint(&local_err, "Detected format of %s.\n", ++ bs->drv->format_name); ++ goto out; ++ } ++ if (size == -1) { ++ /* Opened BS, have no size */ ++ size = bdrv_getlength(bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "Could not get size of '%s'", ++ backing_file); ++ bdrv_unref(bs); ++ goto out; ++ } ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort); ++ } ++ bdrv_unref(bs); ++ } ++ /* (backing_file && !(flags & BDRV_O_NO_BACKING)) */ ++ } else if (backing_file && !backing_fmt) { ++ error_setg(&local_err, ++ "Backing file specified without backing format"); ++ goto out; ++ } ++ ++ /* Parameter 'size' is not needed for detached LUKS header */ ++ if (size == -1 && ++ !(!strcmp(fmt, "luks") && ++ qemu_opt_get_bool(opts, "detached-header", false))) { ++ error_setg(errp, "Image creation needs a size parameter"); ++ goto out; ++ } ++ ++ if (!quiet) { ++ printf("Formatting '%s', fmt=%s ", filename, fmt); ++ qemu_opts_print(opts, " "); ++ puts(""); ++ fflush(stdout); ++ } ++ ++ ret = bdrv_create(drv, filename, opts, &local_err); ++ ++ if (ret == -EFBIG) { ++ /* This is generally a better message than whatever the driver would ++ * deliver (especially because of the cluster_size_hint), since that ++ * is most probably not much different from "image too large". */ ++ const char *cluster_size_hint = ""; ++ if (qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, 0)) { ++ cluster_size_hint = " (try using a larger cluster size)"; ++ } ++ error_setg(errp, "The image size is too large for file format '%s'" ++ "%s", fmt, cluster_size_hint); ++ error_free(local_err); ++ local_err = NULL; ++ } ++ ++out: ++ qemu_opts_del(opts); ++ qemu_opts_free(create_opts); ++ error_propagate(errp, local_err); ++} ++ ++AioContext *bdrv_get_aio_context(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bs ? bs->aio_context : qemu_get_aio_context(); ++} ++ ++AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ AioContext *old_ctx = qemu_coroutine_get_aio_context(self); ++ AioContext *new_ctx; ++ IO_CODE(); ++ ++ /* ++ * Increase bs->in_flight to ensure that this operation is completed before ++ * moving the node to a different AioContext. Read new_ctx only afterwards. ++ */ ++ bdrv_inc_in_flight(bs); ++ ++ new_ctx = bdrv_get_aio_context(bs); ++ aio_co_reschedule_self(new_ctx); ++ return old_ctx; ++} ++ ++void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) ++{ ++ IO_CODE(); ++ aio_co_reschedule_self(old_ctx); ++ bdrv_dec_in_flight(bs); ++} ++ ++static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) ++{ ++ GLOBAL_STATE_CODE(); ++ QLIST_REMOVE(ban, list); ++ g_free(ban); ++} ++ ++static void bdrv_detach_aio_context(BlockDriverState *bs) ++{ ++ BdrvAioNotifier *baf, *baf_tmp; ++ ++ assert(!bs->walking_aio_notifiers); ++ GLOBAL_STATE_CODE(); ++ bs->walking_aio_notifiers = true; ++ QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) { ++ if (baf->deleted) { ++ bdrv_do_remove_aio_context_notifier(baf); ++ } else { ++ baf->detach_aio_context(baf->opaque); ++ } ++ } ++ /* Never mind iterating again to check for ->deleted. bdrv_close() will ++ * remove remaining aio notifiers if we aren't called again. ++ */ ++ bs->walking_aio_notifiers = false; ++ ++ if (bs->drv && bs->drv->bdrv_detach_aio_context) { ++ bs->drv->bdrv_detach_aio_context(bs); ++ } ++ ++ bs->aio_context = NULL; ++} ++ ++static void bdrv_attach_aio_context(BlockDriverState *bs, ++ AioContext *new_context) ++{ ++ BdrvAioNotifier *ban, *ban_tmp; ++ GLOBAL_STATE_CODE(); ++ ++ bs->aio_context = new_context; ++ ++ if (bs->drv && bs->drv->bdrv_attach_aio_context) { ++ bs->drv->bdrv_attach_aio_context(bs, new_context); ++ } ++ ++ assert(!bs->walking_aio_notifiers); ++ bs->walking_aio_notifiers = true; ++ QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_tmp) { ++ if (ban->deleted) { ++ bdrv_do_remove_aio_context_notifier(ban); ++ } else { ++ ban->attached_aio_context(new_context, ban->opaque); ++ } ++ } ++ bs->walking_aio_notifiers = false; ++} ++ ++typedef struct BdrvStateSetAioContext { ++ AioContext *new_ctx; ++ BlockDriverState *bs; ++} BdrvStateSetAioContext; ++ ++static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, ++ GHashTable *visited, ++ Transaction *tran, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (g_hash_table_contains(visited, c)) { ++ return true; ++ } ++ g_hash_table_add(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_change_aio_context(BdrvChild *c, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (g_hash_table_contains(visited, c)) { ++ return true; ++ } ++ g_hash_table_add(visited, c); ++ return bdrv_change_aio_context(c->bs, ctx, visited, tran, errp); ++} ++ ++static void bdrv_set_aio_context_clean(void *opaque) ++{ ++ BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; ++ BlockDriverState *bs = (BlockDriverState *) state->bs; ++ ++ /* Paired with bdrv_drained_begin in bdrv_change_aio_context() */ ++ bdrv_drained_end(bs); ++ ++ g_free(state); ++} ++ ++static void bdrv_set_aio_context_commit(void *opaque) ++{ ++ BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; ++ BlockDriverState *bs = (BlockDriverState *) state->bs; ++ AioContext *new_context = state->new_ctx; ++ ++ bdrv_detach_aio_context(bs); ++ 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. ++ * ++ * @visited will accumulate all visited BdrvChild objects. The caller is ++ * responsible for freeing the list afterwards. ++ */ ++static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, ++ GHashTable *visited, Transaction *tran, ++ Error **errp) ++{ ++ BdrvChild *c; ++ BdrvStateSetAioContext *state; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bdrv_get_aio_context(bs) == ctx) { ++ return true; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) { ++ bdrv_graph_rdunlock_main_loop(); ++ return false; ++ } ++ } ++ ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (!bdrv_child_change_aio_context(c, ctx, visited, tran, errp)) { ++ bdrv_graph_rdunlock_main_loop(); ++ return false; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ 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; ++} ++ ++/* ++ * 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. ++ */ ++int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, ++ BdrvChild *ignore_child, Error **errp) ++{ ++ Transaction *tran; ++ GHashTable *visited; ++ int ret; ++ 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, building a linear list of callbacks to run if everything ++ * is successful (the transaction itself). ++ */ ++ tran = tran_new(); ++ visited = g_hash_table_new(NULL, NULL); ++ if (ignore_child) { ++ g_hash_table_add(visited, ignore_child); ++ } ++ ret = bdrv_change_aio_context(bs, ctx, visited, tran, errp); ++ g_hash_table_destroy(visited); ++ ++ /* ++ * Linear phase: go through all callbacks collected in the transaction. ++ * Run all callbacks collected in the recursion to switch every node's ++ * AioContext (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; ++ } ++ ++ tran_commit(tran); ++ return 0; ++} ++ ++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) ++{ ++ BdrvAioNotifier *ban = g_new(BdrvAioNotifier, 1); ++ *ban = (BdrvAioNotifier){ ++ .attached_aio_context = attached_aio_context, ++ .detach_aio_context = detach_aio_context, ++ .opaque = opaque ++ }; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_INSERT_HEAD(&bs->aio_notifiers, ban, list); ++} ++ ++void bdrv_remove_aio_context_notifier(BlockDriverState *bs, ++ void (*attached_aio_context)(AioContext *, ++ void *), ++ void (*detach_aio_context)(void *), ++ void *opaque) ++{ ++ BdrvAioNotifier *ban, *ban_next; ++ GLOBAL_STATE_CODE(); ++ ++ QLIST_FOREACH_SAFE(ban, &bs->aio_notifiers, list, ban_next) { ++ if (ban->attached_aio_context == attached_aio_context && ++ ban->detach_aio_context == detach_aio_context && ++ ban->opaque == opaque && ++ ban->deleted == false) ++ { ++ if (bs->walking_aio_notifiers) { ++ ban->deleted = true; ++ } else { ++ bdrv_do_remove_aio_context_notifier(ban); ++ } ++ return; ++ } ++ } ++ ++ abort(); ++} ++ ++int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ bool force, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!bs->drv) { ++ error_setg(errp, "Node is ejected"); ++ return -ENOMEDIUM; ++ } ++ if (!bs->drv->bdrv_amend_options) { ++ error_setg(errp, "Block driver '%s' does not support option amendment", ++ bs->drv->format_name); ++ return -ENOTSUP; ++ } ++ return bs->drv->bdrv_amend_options(bs, opts, status_cb, ++ cb_opaque, force, errp); ++} ++ ++/* ++ * This function checks whether the given @to_replace is allowed to be ++ * replaced by a node that always shows the same data as @bs. This is ++ * used for example to verify whether the mirror job can replace ++ * @to_replace by the target mirrored from @bs. ++ * To be replaceable, @bs and @to_replace may either be guaranteed to ++ * always show the same data (because they are only connected through ++ * filters), or some driver may allow replacing one of its children ++ * because it can guarantee that this child's data is not visible at ++ * all (for example, for dissenting quorum children that have no other ++ * parents). ++ */ ++bool bdrv_recurse_can_replace(BlockDriverState *bs, ++ BlockDriverState *to_replace) ++{ ++ BlockDriverState *filtered; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs || !bs->drv) { ++ return false; ++ } ++ ++ if (bs == to_replace) { ++ return true; ++ } ++ ++ /* See what the driver can do */ ++ if (bs->drv->bdrv_recurse_can_replace) { ++ return bs->drv->bdrv_recurse_can_replace(bs, to_replace); ++ } ++ ++ /* For filters without an own implementation, we can recurse on our own */ ++ filtered = bdrv_filter_bs(bs); ++ if (filtered) { ++ return bdrv_recurse_can_replace(filtered, to_replace); ++ } ++ ++ /* Safe default */ ++ return false; ++} ++ ++/* ++ * Check whether the given @node_name can be replaced by a node that ++ * has the same data as @parent_bs. If so, return @node_name's BDS; ++ * NULL otherwise. ++ * ++ * @node_name must be a (recursive) *child of @parent_bs (or this ++ * function will return NULL). ++ * ++ * The result (whether the node can be replaced or not) is only valid ++ * for as long as no graph or permission changes occur. ++ */ ++BlockDriverState *check_to_replace_node(BlockDriverState *parent_bs, ++ const char *node_name, Error **errp) ++{ ++ BlockDriverState *to_replace_bs = bdrv_find_node(node_name); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!to_replace_bs) { ++ error_setg(errp, "Failed to find node with node-name='%s'", node_name); ++ return NULL; ++ } ++ ++ if (bdrv_op_is_blocked(to_replace_bs, BLOCK_OP_TYPE_REPLACE, errp)) { ++ return NULL; ++ } ++ ++ /* We don't want arbitrary node of the BDS chain to be replaced only the top ++ * most non filter in order to prevent data corruption. ++ * Another benefit is that this tests exclude backing files which are ++ * blocked by the backing blockers. ++ */ ++ if (!bdrv_recurse_can_replace(parent_bs, to_replace_bs)) { ++ error_setg(errp, "Cannot replace '%s' by a node mirrored from '%s', " ++ "because it cannot be guaranteed that doing so would not " ++ "lead to an abrupt change of visible data", ++ node_name, parent_bs->node_name); ++ return NULL; ++ } ++ ++ return to_replace_bs; ++} ++ ++/** ++ * Iterates through the list of runtime option keys that are said to ++ * be "strong" for a BDS. An option is called "strong" if it changes ++ * a BDS's data. For example, the null block driver's "size" and ++ * "read-zeroes" options are strong, but its "latency-ns" option is ++ * not. ++ * ++ * If a key returned by this function ends with a dot, all options ++ * starting with that prefix are strong. ++ */ ++static const char *const *strong_options(BlockDriverState *bs, ++ const char *const *curopt) ++{ ++ static const char *const global_options[] = { ++ "driver", "filename", NULL ++ }; ++ ++ if (!curopt) { ++ return &global_options[0]; ++ } ++ ++ curopt++; ++ if (curopt == &global_options[ARRAY_SIZE(global_options) - 1] && bs->drv) { ++ curopt = bs->drv->strong_runtime_opts; ++ } ++ ++ return (curopt && *curopt) ? curopt : NULL; ++} ++ ++/** ++ * Copies all strong runtime options from bs->options to the given ++ * QDict. The set of strong option keys is determined by invoking ++ * strong_options(). ++ * ++ * Returns true iff any strong option was present in bs->options (and ++ * thus copied to the target QDict) with the exception of "filename" ++ * and "driver". The caller is expected to use this value to decide ++ * whether the existence of strong options prevents the generation of ++ * a plain filename. ++ */ ++static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs) ++{ ++ bool found_any = false; ++ const char *const *option_name = NULL; ++ ++ if (!bs->drv) { ++ return false; ++ } ++ ++ while ((option_name = strong_options(bs, option_name))) { ++ bool option_given = false; ++ ++ assert(strlen(*option_name) > 0); ++ if ((*option_name)[strlen(*option_name) - 1] != '.') { ++ QObject *entry = qdict_get(bs->options, *option_name); ++ if (!entry) { ++ continue; ++ } ++ ++ qdict_put_obj(d, *option_name, qobject_ref(entry)); ++ option_given = true; ++ } else { ++ const QDictEntry *entry; ++ for (entry = qdict_first(bs->options); entry; ++ entry = qdict_next(bs->options, entry)) ++ { ++ if (strstart(qdict_entry_key(entry), *option_name, NULL)) { ++ qdict_put_obj(d, qdict_entry_key(entry), ++ qobject_ref(qdict_entry_value(entry))); ++ option_given = true; ++ } ++ } ++ } ++ ++ /* While "driver" and "filename" need to be included in a JSON filename, ++ * their existence does not prohibit generation of a plain filename. */ ++ if (!found_any && option_given && ++ strcmp(*option_name, "driver") && strcmp(*option_name, "filename")) ++ { ++ found_any = true; ++ } ++ } ++ ++ if (!qdict_haskey(d, "driver")) { ++ /* Drivers created with bdrv_new_open_driver() may not have a ++ * @driver option. Add it here. */ ++ qdict_put_str(d, "driver", bs->drv->format_name); ++ } ++ ++ return found_any; ++} ++ ++/* Note: This function may return false positives; it may return true ++ * even if opening the backing file specified by bs's image header ++ * would result in exactly bs->backing. */ ++static bool GRAPH_RDLOCK bdrv_backing_overridden(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ if (bs->backing) { ++ return strcmp(bs->auto_backing_file, ++ bs->backing->bs->filename); ++ } else { ++ /* No backing BDS, so if the image header reports any backing ++ * file, it must have been suppressed */ ++ return bs->auto_backing_file[0] != '\0'; ++ } ++} ++ ++/* Updates the following BDS fields: ++ * - exact_filename: A filename which may be used for opening a block device ++ * which (mostly) equals the given BDS (even without any ++ * other options; so reading and writing must return the same ++ * results, but caching etc. may be different) ++ * - full_open_options: Options which, when given when opening a block device ++ * (without a filename), result in a BDS (mostly) ++ * equalling the given one ++ * - filename: If exact_filename is set, it is copied here. Otherwise, ++ * full_open_options is converted to a JSON object, prefixed with ++ * "json:" (for use through the JSON pseudo protocol) and put here. ++ */ ++void bdrv_refresh_filename(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ BdrvChild *child; ++ BlockDriverState *primary_child_bs; ++ QDict *opts; ++ bool backing_overridden; ++ bool generate_json_filename; /* Whether our default implementation should ++ fill exact_filename (false) or not (true) */ ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ return; ++ } ++ ++ /* This BDS's file name may depend on any of its children's file names, so ++ * refresh those first */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ bdrv_refresh_filename(child->bs); ++ } ++ ++ if (bs->implicit) { ++ /* For implicit nodes, just copy everything from the single child */ ++ child = QLIST_FIRST(&bs->children); ++ assert(QLIST_NEXT(child, next) == NULL); ++ ++ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), ++ child->bs->exact_filename); ++ pstrcpy(bs->filename, sizeof(bs->filename), child->bs->filename); ++ ++ qobject_unref(bs->full_open_options); ++ bs->full_open_options = qobject_ref(child->bs->full_open_options); ++ ++ return; ++ } ++ ++ backing_overridden = bdrv_backing_overridden(bs); ++ ++ if (bs->open_flags & BDRV_O_NO_IO) { ++ /* Without I/O, the backing file does not change anything. ++ * Therefore, in such a case (primarily qemu-img), we can ++ * pretend the backing file has not been overridden even if ++ * it technically has been. */ ++ backing_overridden = false; ++ } ++ ++ /* Gather the options QDict */ ++ opts = qdict_new(); ++ generate_json_filename = append_strong_runtime_options(opts, bs); ++ generate_json_filename |= backing_overridden; ++ ++ if (drv->bdrv_gather_child_options) { ++ /* Some block drivers may not want to present all of their children's ++ * options, or name them differently from BdrvChild.name */ ++ drv->bdrv_gather_child_options(bs, opts, backing_overridden); ++ } else { ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child == bs->backing && !backing_overridden) { ++ /* We can skip the backing BDS if it has not been overridden */ ++ continue; ++ } ++ ++ qdict_put(opts, child->name, ++ qobject_ref(child->bs->full_open_options)); ++ } ++ ++ if (backing_overridden && !bs->backing) { ++ /* Force no backing file */ ++ qdict_put_null(opts, "backing"); ++ } ++ } ++ ++ qobject_unref(bs->full_open_options); ++ bs->full_open_options = opts; ++ ++ primary_child_bs = bdrv_primary_bs(bs); ++ ++ if (drv->bdrv_refresh_filename) { ++ /* Obsolete information is of no use here, so drop the old file name ++ * information before refreshing it */ ++ bs->exact_filename[0] = '\0'; ++ ++ drv->bdrv_refresh_filename(bs); ++ } else if (primary_child_bs) { ++ /* ++ * Try to reconstruct valid information from the underlying ++ * file -- this only works for format nodes (filter nodes ++ * cannot be probed and as such must be selected by the user ++ * either through an options dict, or through a special ++ * filename which the filter driver must construct in its ++ * .bdrv_refresh_filename() implementation). ++ */ ++ ++ bs->exact_filename[0] = '\0'; ++ ++ /* ++ * We can use the underlying file's filename if: ++ * - it has a filename, ++ * - the current BDS is not a filter, ++ * - the file is a protocol BDS, and ++ * - opening that file (as this BDS's format) will automatically create ++ * the BDS tree we have right now, that is: ++ * - the user did not significantly change this BDS's behavior with ++ * some explicit (strong) options ++ * - no non-file child of this BDS has been overridden by the user ++ * Both of these conditions are represented by generate_json_filename. ++ */ ++ if (primary_child_bs->exact_filename[0] && ++ primary_child_bs->drv->protocol_name && ++ !drv->is_filter && !generate_json_filename) ++ { ++ strcpy(bs->exact_filename, primary_child_bs->exact_filename); ++ } ++ } ++ ++ if (bs->exact_filename[0]) { ++ pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename); ++ } else { ++ GString *json = qobject_to_json(QOBJECT(bs->full_open_options)); ++ if (snprintf(bs->filename, sizeof(bs->filename), "json:%s", ++ json->str) >= sizeof(bs->filename)) { ++ /* Give user a hint if we truncated things. */ ++ strcpy(bs->filename + sizeof(bs->filename) - 4, "..."); ++ } ++ g_string_free(json, true); ++ } ++} ++ ++char *bdrv_dirname(BlockDriverState *bs, Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *child_bs; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ error_setg(errp, "Node '%s' is ejected", bs->node_name); ++ return NULL; ++ } ++ ++ if (drv->bdrv_dirname) { ++ return drv->bdrv_dirname(bs, errp); ++ } ++ ++ child_bs = bdrv_primary_bs(bs); ++ if (child_bs) { ++ return bdrv_dirname(child_bs, errp); ++ } ++ ++ bdrv_refresh_filename(bs); ++ if (bs->exact_filename[0] != '\0') { ++ return path_combine(bs->exact_filename, ""); ++ } ++ ++ error_setg(errp, "Cannot generate a base directory for %s nodes", ++ drv->format_name); ++ return NULL; ++} ++ ++/* ++ * Hot add/remove a BDS's child. So the user can take a child offline when ++ * it is broken and take a new child online ++ */ ++void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!parent_bs->drv || !parent_bs->drv->bdrv_add_child) { ++ error_setg(errp, "The node %s does not support adding a child", ++ bdrv_get_device_or_node_name(parent_bs)); ++ return; ++ } ++ ++ /* ++ * Non-zoned block drivers do not follow zoned storage constraints ++ * (i.e. sequential writes to zones). Refuse mixing zoned and non-zoned ++ * drivers in a graph. ++ */ ++ if (!parent_bs->drv->supports_zoned_children && ++ child_bs->bl.zoned == BLK_Z_HM) { ++ /* ++ * The host-aware model allows zoned storage constraints and random ++ * write. Allow mixing host-aware and non-zoned drivers. Using ++ * host-aware device as a regular device. ++ */ ++ error_setg(errp, "Cannot add a %s child to a %s parent", ++ child_bs->bl.zoned == BLK_Z_HM ? "zoned" : "non-zoned", ++ parent_bs->drv->supports_zoned_children ? ++ "support zoned children" : "not support zoned children"); ++ return; ++ } ++ ++ if (!QLIST_EMPTY(&child_bs->parents)) { ++ error_setg(errp, "The node %s already has a parent", ++ child_bs->node_name); ++ return; ++ } ++ ++ parent_bs->drv->bdrv_add_child(parent_bs, child_bs, errp); ++} ++ ++void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp) ++{ ++ BdrvChild *tmp; ++ ++ GLOBAL_STATE_CODE(); ++ if (!parent_bs->drv || !parent_bs->drv->bdrv_del_child) { ++ error_setg(errp, "The node %s does not support removing a child", ++ bdrv_get_device_or_node_name(parent_bs)); ++ return; ++ } ++ ++ QLIST_FOREACH(tmp, &parent_bs->children, next) { ++ if (tmp == child) { ++ break; ++ } ++ } ++ ++ if (!tmp) { ++ error_setg(errp, "The node %s does not have a child named %s", ++ bdrv_get_device_or_node_name(parent_bs), ++ bdrv_get_device_or_node_name(child->bs)); ++ return; ++ } ++ ++ parent_bs->drv->bdrv_del_child(parent_bs, child, errp); ++} ++ ++int bdrv_make_empty(BdrvChild *c, Error **errp) ++{ ++ BlockDriver *drv = c->bs->drv; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ assert(c->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)); ++ ++ if (!drv->bdrv_make_empty) { ++ error_setg(errp, "%s does not support emptying nodes", ++ drv->format_name); ++ return -ENOTSUP; ++ } ++ ++ ret = drv->bdrv_make_empty(c->bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to empty %s", ++ c->bs->filename); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Return the child that @bs acts as an overlay for, and from which data may be ++ * copied in COW or COR operations. Usually this is the backing file. ++ */ ++BdrvChild *bdrv_cow_child(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ ++ if (!bs || !bs->drv) { ++ return NULL; ++ } ++ ++ if (bs->drv->is_filter) { ++ return NULL; ++ } ++ ++ if (!bs->backing) { ++ return NULL; ++ } ++ ++ assert(bs->backing->role & BDRV_CHILD_COW); ++ return bs->backing; ++} ++ ++/* ++ * If @bs acts as a filter for exactly one of its children, return ++ * that child. ++ */ ++BdrvChild *bdrv_filter_child(BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ IO_CODE(); ++ ++ if (!bs || !bs->drv) { ++ return NULL; ++ } ++ ++ if (!bs->drv->is_filter) { ++ return NULL; ++ } ++ ++ /* Only one of @backing or @file may be used */ ++ assert(!(bs->backing && bs->file)); ++ ++ c = bs->backing ?: bs->file; ++ if (!c) { ++ return NULL; ++ } ++ ++ assert(c->role & BDRV_CHILD_FILTERED); ++ return c; ++} ++ ++/* ++ * Return either the result of bdrv_cow_child() or bdrv_filter_child(), ++ * whichever is non-NULL. ++ * ++ * Return NULL if both are NULL. ++ */ ++BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs) ++{ ++ BdrvChild *cow_child = bdrv_cow_child(bs); ++ BdrvChild *filter_child = bdrv_filter_child(bs); ++ IO_CODE(); ++ ++ /* Filter nodes cannot have COW backing files */ ++ assert(!(cow_child && filter_child)); ++ ++ return cow_child ?: filter_child; ++} ++ ++/* ++ * Return the primary child of this node: For filters, that is the ++ * filtered child. For other nodes, that is usually the child storing ++ * metadata. ++ * (A generally more helpful description is that this is (usually) the ++ * child that has the same filename as @bs.) ++ * ++ * Drivers do not necessarily have a primary child; for example quorum ++ * does not. ++ */ ++BdrvChild *bdrv_primary_child(BlockDriverState *bs) ++{ ++ BdrvChild *c, *found = NULL; ++ IO_CODE(); ++ ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (c->role & BDRV_CHILD_PRIMARY) { ++ assert(!found); ++ found = c; ++ } ++ } ++ ++ return found; ++} ++ ++static BlockDriverState * GRAPH_RDLOCK ++bdrv_do_skip_filters(BlockDriverState *bs, bool stop_on_explicit_filter) ++{ ++ BdrvChild *c; ++ ++ if (!bs) { ++ return NULL; ++ } ++ ++ while (!(stop_on_explicit_filter && !bs->implicit)) { ++ c = bdrv_filter_child(bs); ++ if (!c) { ++ /* ++ * A filter that is embedded in a working block graph must ++ * have a child. Assert this here so this function does ++ * not return a filter node that is not expected by the ++ * caller. ++ */ ++ assert(!bs->drv || !bs->drv->is_filter); ++ break; ++ } ++ bs = c->bs; ++ } ++ /* ++ * Note that this treats nodes with bs->drv == NULL as not being ++ * filters (bs->drv == NULL should be replaced by something else ++ * anyway). ++ * The advantage of this behavior is that this function will thus ++ * always return a non-NULL value (given a non-NULL @bs). ++ */ ++ ++ return bs; ++} ++ ++/* ++ * Return the first BDS that has not been added implicitly or that ++ * does not have a filtered child down the chain starting from @bs ++ * (including @bs itself). ++ */ ++BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_do_skip_filters(bs, true); ++} ++ ++/* ++ * Return the first BDS that does not have a filtered child down the ++ * chain starting from @bs (including @bs itself). ++ */ ++BlockDriverState *bdrv_skip_filters(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_do_skip_filters(bs, false); ++} ++ ++/* ++ * For a backing chain, return the first non-filter backing image of ++ * the first non-filter image. ++ */ ++BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ return bdrv_skip_filters(bdrv_cow_bs(bdrv_skip_filters(bs))); ++} ++ ++/** ++ * Check whether [offset, offset + bytes) overlaps with the cached ++ * block-status data region. ++ * ++ * If so, and @pnum is not NULL, set *pnum to `bsc.data_end - offset`, ++ * which is what bdrv_bsc_is_data()'s interface needs. ++ * Otherwise, *pnum is not touched. ++ */ ++static bool bdrv_bsc_range_overlaps_locked(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum) ++{ ++ BdrvBlockStatusCache *bsc = qatomic_rcu_read(&bs->block_status_cache); ++ bool overlaps; ++ ++ overlaps = ++ qatomic_read(&bsc->valid) && ++ ranges_overlap(offset, bytes, bsc->data_start, ++ bsc->data_end - bsc->data_start); ++ ++ if (overlaps && pnum) { ++ *pnum = bsc->data_end - offset; ++ } ++ ++ return overlaps; ++} ++ ++/** ++ * See block_int.h for this function's documentation. ++ */ ++bool bdrv_bsc_is_data(BlockDriverState *bs, int64_t offset, int64_t *pnum) ++{ ++ IO_CODE(); ++ RCU_READ_LOCK_GUARD(); ++ return bdrv_bsc_range_overlaps_locked(bs, offset, 1, pnum); ++} ++ ++/** ++ * See block_int.h for this function's documentation. ++ */ ++void bdrv_bsc_invalidate_range(BlockDriverState *bs, ++ int64_t offset, int64_t bytes) ++{ ++ IO_CODE(); ++ RCU_READ_LOCK_GUARD(); ++ ++ if (bdrv_bsc_range_overlaps_locked(bs, offset, bytes, NULL)) { ++ qatomic_set(&bs->block_status_cache->valid, false); ++ } ++} ++ ++/** ++ * See block_int.h for this function's documentation. ++ */ ++void bdrv_bsc_fill(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BdrvBlockStatusCache *new_bsc = g_new(BdrvBlockStatusCache, 1); ++ BdrvBlockStatusCache *old_bsc; ++ IO_CODE(); ++ ++ *new_bsc = (BdrvBlockStatusCache) { ++ .valid = true, ++ .data_start = offset, ++ .data_end = offset + bytes, ++ }; ++ ++ QEMU_LOCK_GUARD(&bs->bsc_modify_lock); ++ ++ old_bsc = qatomic_rcu_read(&bs->block_status_cache); ++ qatomic_rcu_set(&bs->block_status_cache, new_bsc); ++ if (old_bsc) { ++ g_free_rcu(old_bsc, rcu); ++ } ++} +diff --git a/qcow2/lib/block/accounting.c b/qcow2/lib/block/accounting.c +new file mode 100644 +index 00000000..28297453 +--- /dev/null ++++ b/qcow2/lib/block/accounting.c +@@ -0,0 +1,298 @@ ++/* ++ * QEMU System Emulator block accounting ++ * ++ * Copyright (c) 2011 Christoph Hellwig ++ * Copyright (c) 2015 Igalia, S.L. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/accounting.h" ++#include "block/block_int.h" ++#include "qemu/timer.h" ++#include "sysemu/qtest.h" ++ ++static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; ++static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; ++ ++void block_acct_init(BlockAcctStats *stats) ++{ ++ qemu_mutex_init(&stats->lock); ++ if (qtest_enabled()) { ++ clock_type = QEMU_CLOCK_VIRTUAL; ++ } ++ stats->account_invalid = true; ++ stats->account_failed = true; ++} ++ ++static bool bool_from_onoffauto(OnOffAuto val, bool def) ++{ ++ switch (val) { ++ case ON_OFF_AUTO_AUTO: ++ return def; ++ case ON_OFF_AUTO_ON: ++ return true; ++ case ON_OFF_AUTO_OFF: ++ return false; ++ default: ++ abort(); ++ } ++} ++ ++void block_acct_setup(BlockAcctStats *stats, enum OnOffAuto account_invalid, ++ enum OnOffAuto account_failed) ++{ ++ stats->account_invalid = bool_from_onoffauto(account_invalid, ++ stats->account_invalid); ++ stats->account_failed = bool_from_onoffauto(account_failed, ++ stats->account_failed); ++} ++ ++void block_acct_cleanup(BlockAcctStats *stats) ++{ ++ BlockAcctTimedStats *s, *next; ++ QSLIST_FOREACH_SAFE(s, &stats->intervals, entries, next) { ++ g_free(s); ++ } ++ qemu_mutex_destroy(&stats->lock); ++} ++ ++void block_acct_add_interval(BlockAcctStats *stats, unsigned interval_length) ++{ ++ BlockAcctTimedStats *s; ++ unsigned i; ++ ++ s = g_new0(BlockAcctTimedStats, 1); ++ s->interval_length = interval_length; ++ s->stats = stats; ++ qemu_mutex_lock(&stats->lock); ++ QSLIST_INSERT_HEAD(&stats->intervals, s, entries); ++ ++ for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { ++ timed_average_init(&s->latency[i], clock_type, ++ (uint64_t) interval_length * NANOSECONDS_PER_SECOND); ++ } ++ qemu_mutex_unlock(&stats->lock); ++} ++ ++BlockAcctTimedStats *block_acct_interval_next(BlockAcctStats *stats, ++ BlockAcctTimedStats *s) ++{ ++ if (s == NULL) { ++ return QSLIST_FIRST(&stats->intervals); ++ } else { ++ return QSLIST_NEXT(s, entries); ++ } ++} ++ ++void block_acct_start(BlockAcctStats *stats, BlockAcctCookie *cookie, ++ int64_t bytes, enum BlockAcctType type) ++{ ++ assert(type < BLOCK_MAX_IOTYPE); ++ ++ cookie->bytes = bytes; ++ cookie->start_time_ns = qemu_clock_get_ns(clock_type); ++ cookie->type = type; ++} ++ ++/* block_latency_histogram_compare_func: ++ * Compare @key with interval [@it[0], @it[1]). ++ * Return: -1 if @key < @it[0] ++ * 0 if @key in [@it[0], @it[1]) ++ * +1 if @key >= @it[1] ++ */ ++static int block_latency_histogram_compare_func(const void *key, const void *it) ++{ ++ uint64_t k = *(uint64_t *)key; ++ uint64_t a = ((uint64_t *)it)[0]; ++ uint64_t b = ((uint64_t *)it)[1]; ++ ++ return k < a ? -1 : (k < b ? 0 : 1); ++} ++ ++static void block_latency_histogram_account(BlockLatencyHistogram *hist, ++ int64_t latency_ns) ++{ ++ uint64_t *pos; ++ ++ if (hist->bins == NULL) { ++ /* histogram disabled */ ++ return; ++ } ++ ++ ++ if (latency_ns < hist->boundaries[0]) { ++ hist->bins[0]++; ++ return; ++ } ++ ++ if (latency_ns >= hist->boundaries[hist->nbins - 2]) { ++ hist->bins[hist->nbins - 1]++; ++ return; ++ } ++ ++ pos = bsearch(&latency_ns, hist->boundaries, hist->nbins - 2, ++ sizeof(hist->boundaries[0]), ++ block_latency_histogram_compare_func); ++ assert(pos != NULL); ++ ++ hist->bins[pos - hist->boundaries + 1]++; ++} ++ ++int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, ++ uint64List *boundaries) ++{ ++ BlockLatencyHistogram *hist = &stats->latency_histogram[type]; ++ uint64List *entry; ++ uint64_t *ptr; ++ uint64_t prev = 0; ++ int new_nbins = 1; ++ ++ for (entry = boundaries; entry; entry = entry->next) { ++ if (entry->value <= prev) { ++ return -EINVAL; ++ } ++ new_nbins++; ++ prev = entry->value; ++ } ++ ++ hist->nbins = new_nbins; ++ g_free(hist->boundaries); ++ hist->boundaries = g_new(uint64_t, hist->nbins - 1); ++ for (entry = boundaries, ptr = hist->boundaries; entry; ++ entry = entry->next, ptr++) ++ { ++ *ptr = entry->value; ++ } ++ ++ g_free(hist->bins); ++ hist->bins = g_new0(uint64_t, hist->nbins); ++ ++ return 0; ++} ++ ++void block_latency_histograms_clear(BlockAcctStats *stats) ++{ ++ int i; ++ ++ for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { ++ BlockLatencyHistogram *hist = &stats->latency_histogram[i]; ++ g_free(hist->bins); ++ g_free(hist->boundaries); ++ memset(hist, 0, sizeof(*hist)); ++ } ++} ++ ++static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, ++ bool failed) ++{ ++ BlockAcctTimedStats *s; ++ int64_t time_ns = qemu_clock_get_ns(clock_type); ++ int64_t latency_ns = time_ns - cookie->start_time_ns; ++ ++ if (qtest_enabled()) { ++ latency_ns = qtest_latency_ns; ++ } ++ ++ assert(cookie->type < BLOCK_MAX_IOTYPE); ++ ++ if (cookie->type == BLOCK_ACCT_NONE) { ++ return; ++ } ++ ++ WITH_QEMU_LOCK_GUARD(&stats->lock) { ++ if (failed) { ++ stats->failed_ops[cookie->type]++; ++ } else { ++ stats->nr_bytes[cookie->type] += cookie->bytes; ++ stats->nr_ops[cookie->type]++; ++ } ++ ++ block_latency_histogram_account(&stats->latency_histogram[cookie->type], ++ latency_ns); ++ ++ if (!failed || stats->account_failed) { ++ stats->total_time_ns[cookie->type] += latency_ns; ++ stats->last_access_time_ns = time_ns; ++ ++ QSLIST_FOREACH(s, &stats->intervals, entries) { ++ timed_average_account(&s->latency[cookie->type], latency_ns); ++ } ++ } ++ } ++ ++ cookie->type = BLOCK_ACCT_NONE; ++} ++ ++void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie) ++{ ++ block_account_one_io(stats, cookie, false); ++} ++ ++void block_acct_failed(BlockAcctStats *stats, BlockAcctCookie *cookie) ++{ ++ block_account_one_io(stats, cookie, true); ++} ++ ++void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type) ++{ ++ assert(type < BLOCK_MAX_IOTYPE); ++ ++ /* block_account_one_io() updates total_time_ns[], but this one does ++ * not. The reason is that invalid requests are accounted during their ++ * submission, therefore there's no actual I/O involved. ++ */ ++ qemu_mutex_lock(&stats->lock); ++ stats->invalid_ops[type]++; ++ ++ if (stats->account_invalid) { ++ stats->last_access_time_ns = qemu_clock_get_ns(clock_type); ++ } ++ qemu_mutex_unlock(&stats->lock); ++} ++ ++void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, ++ int num_requests) ++{ ++ assert(type < BLOCK_MAX_IOTYPE); ++ ++ qemu_mutex_lock(&stats->lock); ++ stats->merged[type] += num_requests; ++ qemu_mutex_unlock(&stats->lock); ++} ++ ++int64_t block_acct_idle_time_ns(BlockAcctStats *stats) ++{ ++ return qemu_clock_get_ns(clock_type) - stats->last_access_time_ns; ++} ++ ++double block_acct_queue_depth(BlockAcctTimedStats *stats, ++ enum BlockAcctType type) ++{ ++ uint64_t sum, elapsed; ++ ++ assert(type < BLOCK_MAX_IOTYPE); ++ ++ qemu_mutex_lock(&stats->stats->lock); ++ sum = timed_average_sum(&stats->latency[type], &elapsed); ++ qemu_mutex_unlock(&stats->stats->lock); ++ ++ return (double) sum / elapsed; ++} +diff --git a/qcow2/lib/block/aio_task.c b/qcow2/lib/block/aio_task.c +new file mode 100644 +index 00000000..9bd17ea2 +--- /dev/null ++++ b/qcow2/lib/block/aio_task.c +@@ -0,0 +1,126 @@ ++/* ++ * Aio tasks loops ++ * ++ * Copyright (c) 2019 Virtuozzo International GmbH. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/aio.h" ++#include "block/aio_task.h" ++ ++struct AioTaskPool { ++ Coroutine *main_co; ++ int status; ++ int max_busy_tasks; ++ int busy_tasks; ++ bool waiting; ++}; ++ ++static void coroutine_fn aio_task_co(void *opaque) ++{ ++ AioTask *task = opaque; ++ AioTaskPool *pool = task->pool; ++ ++ assert(pool->busy_tasks < pool->max_busy_tasks); ++ pool->busy_tasks++; ++ ++ task->ret = task->func(task); ++ ++ pool->busy_tasks--; ++ ++ if (task->ret < 0 && pool->status == 0) { ++ pool->status = task->ret; ++ } ++ ++ g_free(task); ++ ++ if (pool->waiting) { ++ pool->waiting = false; ++ aio_co_wake(pool->main_co); ++ } ++} ++ ++void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool) ++{ ++ assert(pool->busy_tasks > 0); ++ assert(qemu_coroutine_self() == pool->main_co); ++ ++ pool->waiting = true; ++ qemu_coroutine_yield(); ++ ++ assert(!pool->waiting); ++ assert(pool->busy_tasks < pool->max_busy_tasks); ++} ++ ++void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool) ++{ ++ if (pool->busy_tasks < pool->max_busy_tasks) { ++ return; ++ } ++ ++ aio_task_pool_wait_one(pool); ++} ++ ++void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool) ++{ ++ while (pool->busy_tasks > 0) { ++ aio_task_pool_wait_one(pool); ++ } ++} ++ ++void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task) ++{ ++ aio_task_pool_wait_slot(pool); ++ ++ task->pool = pool; ++ qemu_coroutine_enter(qemu_coroutine_create(aio_task_co, task)); ++} ++ ++AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks) ++{ ++ AioTaskPool *pool = g_new0(AioTaskPool, 1); ++ ++ assert(max_busy_tasks > 0); ++ ++ pool->main_co = qemu_coroutine_self(); ++ pool->max_busy_tasks = max_busy_tasks; ++ ++ return pool; ++} ++ ++void aio_task_pool_free(AioTaskPool *pool) ++{ ++ g_free(pool); ++} ++ ++int aio_task_pool_status(AioTaskPool *pool) ++{ ++ if (!pool) { ++ return 0; /* Sugar for lazy allocation of aio pool */ ++ } ++ ++ return pool->status; ++} ++ ++bool aio_task_pool_empty(AioTaskPool *pool) ++{ ++ return pool->busy_tasks == 0; ++} +diff --git a/qcow2/lib/block/block-backend.c b/qcow2/lib/block/block-backend.c +new file mode 100644 +index 00000000..db6f9b92 +--- /dev/null ++++ b/qcow2/lib/block/block-backend.c +@@ -0,0 +1,2899 @@ ++/* ++ * QEMU Block backends ++ * ++ * Copyright (C) 2014-2016 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "sysemu/block-backend.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "block/coroutines.h" ++#include "block/throttle-groups.h" ++#include "hw/qdev-core.h" ++#include "sysemu/blockdev.h" ++#include "sysemu/runstate.h" ++#include "sysemu/replay.h" ++#include "qapi/error.h" ++#include "qapi/qapi-events-block.h" ++#include "qemu/id.h" ++#include "qemu/main-loop.h" ++#include "qemu/option.h" ++#include "trace.h" ++#include "migration/misc.h" ++ ++/* Number of coroutines to reserve per attached device model */ ++#define COROUTINE_POOL_RESERVATION 64 ++ ++#define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ ++ ++typedef struct BlockBackendAioNotifier { ++ void (*attached_aio_context)(AioContext *new_context, void *opaque); ++ void (*detach_aio_context)(void *opaque); ++ void *opaque; ++ QLIST_ENTRY(BlockBackendAioNotifier) list; ++} BlockBackendAioNotifier; ++ ++struct BlockBackend { ++ char *name; ++ int refcnt; ++ BdrvChild *root; ++ AioContext *ctx; /* access with atomic operations only */ ++ DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ ++ QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ ++ QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ ++ BlockBackendPublic public; ++ ++ DeviceState *dev; /* attached device model, if any */ ++ const BlockDevOps *dev_ops; ++ void *dev_opaque; ++ ++ /* If the BDS tree is removed, some of its options are stored here (which ++ * can be used to restore those options in the new BDS on insert) */ ++ BlockBackendRootState root_state; ++ ++ bool enable_write_cache; ++ ++ /* I/O stats (display with "info blockstats"). */ ++ BlockAcctStats stats; ++ ++ BlockdevOnError on_read_error, on_write_error; ++ bool iostatus_enabled; ++ BlockDeviceIoStatus iostatus; ++ ++ uint64_t perm; ++ uint64_t shared_perm; ++ bool disable_perm; ++ ++ bool allow_aio_context_change; ++ bool allow_write_beyond_eof; ++ ++ /* Protected by BQL */ ++ NotifierList remove_bs_notifiers, insert_bs_notifiers; ++ QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers; ++ ++ int quiesce_counter; /* atomic: written under BQL, read by other threads */ ++ QemuMutex queued_requests_lock; /* protects queued_requests */ ++ CoQueue queued_requests; ++ bool disable_request_queuing; /* atomic */ ++ ++ VMChangeStateEntry *vmsh; ++ bool force_allow_inactivate; ++ ++ /* Number of in-flight aio requests. BlockDriverState also counts ++ * in-flight requests but aio requests can exist even when blk->root is ++ * NULL, so we cannot rely on its counter for that case. ++ * Accessed with atomic ops. ++ */ ++ unsigned int in_flight; ++}; ++ ++typedef struct BlockBackendAIOCB { ++ BlockAIOCB common; ++ BlockBackend *blk; ++ int ret; ++} BlockBackendAIOCB; ++ ++static const AIOCBInfo block_backend_aiocb_info = { ++ .aiocb_size = sizeof(BlockBackendAIOCB), ++}; ++ ++static void drive_info_del(DriveInfo *dinfo); ++static BlockBackend *bdrv_first_blk(BlockDriverState *bs); ++ ++/* All BlockBackends. Protected by BQL. */ ++static QTAILQ_HEAD(, BlockBackend) block_backends = ++ QTAILQ_HEAD_INITIALIZER(block_backends); ++ ++/* ++ * All BlockBackends referenced by the monitor and which are iterated through by ++ * blk_next(). Protected by BQL. ++ */ ++static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = ++ QTAILQ_HEAD_INITIALIZER(monitor_block_backends); ++ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++blk_set_perm_locked(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, ++ Error **errp); ++ ++static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format, ++ int *child_flags, QDict *child_options, ++ int parent_flags, QDict *parent_options) ++{ ++ /* We're not supposed to call this function for root nodes */ ++ abort(); ++} ++static void blk_root_drained_begin(BdrvChild *child); ++static bool blk_root_drained_poll(BdrvChild *child); ++static void blk_root_drained_end(BdrvChild *child); ++ ++static void blk_root_change_media(BdrvChild *child, bool load); ++static void blk_root_resize(BdrvChild *child); ++ ++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) ++{ ++ BlockBackend *blk = child->opaque; ++ g_autofree char *dev_id = NULL; ++ ++ if (blk->name) { ++ return g_strdup_printf("block device '%s'", blk->name); ++ } ++ ++ dev_id = blk_get_attached_dev_id(blk); ++ if (*dev_id) { ++ return g_strdup_printf("block device '%s'", dev_id); ++ } else { ++ /* TODO Callback into the BB owner for something more detailed */ ++ return g_strdup("an unnamed block device"); ++ } ++} ++ ++static const char *blk_root_get_name(BdrvChild *child) ++{ ++ return blk_name(child->opaque); ++} ++ ++static void blk_vm_state_changed(void *opaque, bool running, RunState state) ++{ ++ Error *local_err = NULL; ++ BlockBackend *blk = opaque; ++ ++ if (state == RUN_STATE_INMIGRATE) { ++ return; ++ } ++ ++ qemu_del_vm_change_state_handler(blk->vmsh); ++ blk->vmsh = NULL; ++ blk_set_perm(blk, blk->perm, blk->shared_perm, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ } ++} ++ ++/* ++ * Notifies the user of the BlockBackend that migration has completed. qdev ++ * devices can tighten their permissions in response (specifically revoke ++ * shared write permissions that we needed for storage migration). ++ * ++ * If an error is returned, the VM cannot be allowed to be resumed. ++ */ ++static void GRAPH_RDLOCK blk_root_activate(BdrvChild *child, Error **errp) ++{ ++ BlockBackend *blk = child->opaque; ++ Error *local_err = NULL; ++ uint64_t saved_shared_perm; ++ ++ if (!blk->disable_perm) { ++ return; ++ } ++ ++ blk->disable_perm = false; ++ ++ /* ++ * blk->shared_perm contains the permissions we want to share once ++ * migration is really completely done. For now, we need to share ++ * all; but we also need to retain blk->shared_perm, which is ++ * overwritten by a successful blk_set_perm() call. Save it and ++ * restore it below. ++ */ ++ saved_shared_perm = blk->shared_perm; ++ ++ blk_set_perm_locked(blk, blk->perm, BLK_PERM_ALL, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ blk->disable_perm = true; ++ return; ++ } ++ blk->shared_perm = saved_shared_perm; ++ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ /* Activation can happen when migration process is still active, for ++ * example when nbd_server_add is called during non-shared storage ++ * migration. Defer the shared_perm update to migration completion. */ ++ if (!blk->vmsh) { ++ blk->vmsh = qemu_add_vm_change_state_handler(blk_vm_state_changed, ++ blk); ++ } ++ return; ++ } ++ ++ blk_set_perm_locked(blk, blk->perm, blk->shared_perm, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ blk->disable_perm = true; ++ return; ++ } ++} ++ ++void blk_set_force_allow_inactivate(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->force_allow_inactivate = true; ++} ++ ++static bool blk_can_inactivate(BlockBackend *blk) ++{ ++ /* If it is a guest device, inactivate is ok. */ ++ if (blk->dev || blk_name(blk)[0]) { ++ return true; ++ } ++ ++ /* Inactivating means no more writes to the image can be done, ++ * even if those writes would be changes invisible to the ++ * guest. For block job BBs that satisfy this, we can just allow ++ * it. This is the case for mirror job source, which is required ++ * by libvirt non-shared block migration. */ ++ if (!(blk->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED))) { ++ return true; ++ } ++ ++ return blk->force_allow_inactivate; ++} ++ ++static int GRAPH_RDLOCK blk_root_inactivate(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ ++ if (blk->disable_perm) { ++ return 0; ++ } ++ ++ if (!blk_can_inactivate(blk)) { ++ return -EPERM; ++ } ++ ++ blk->disable_perm = true; ++ if (blk->root) { ++ bdrv_child_try_set_perm(blk->root, 0, BLK_PERM_ALL, &error_abort); ++ } ++ ++ return 0; ++} ++ ++static void blk_root_attach(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ BlockBackendAioNotifier *notifier; ++ ++ trace_blk_root_attach(child, blk, child->bs); ++ ++ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { ++ bdrv_add_aio_context_notifier(child->bs, ++ notifier->attached_aio_context, ++ notifier->detach_aio_context, ++ notifier->opaque); ++ } ++} ++ ++static void blk_root_detach(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ BlockBackendAioNotifier *notifier; ++ ++ trace_blk_root_detach(child, blk, child->bs); ++ ++ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { ++ bdrv_remove_aio_context_notifier(child->bs, ++ notifier->attached_aio_context, ++ notifier->detach_aio_context, ++ notifier->opaque); ++ } ++} ++ ++static AioContext *blk_root_get_parent_aio_context(BdrvChild *c) ++{ ++ BlockBackend *blk = c->opaque; ++ IO_CODE(); ++ ++ return blk_get_aio_context(blk); ++} ++ ++static const BdrvChildClass child_root = { ++ .inherit_options = blk_root_inherit_options, ++ ++ .change_media = blk_root_change_media, ++ .resize = blk_root_resize, ++ .get_name = blk_root_get_name, ++ .get_parent_desc = blk_root_get_parent_desc, ++ ++ .drained_begin = blk_root_drained_begin, ++ .drained_poll = blk_root_drained_poll, ++ .drained_end = blk_root_drained_end, ++ ++ .activate = blk_root_activate, ++ .inactivate = blk_root_inactivate, ++ ++ .attach = blk_root_attach, ++ .detach = blk_root_detach, ++ ++ .change_aio_ctx = blk_root_change_aio_ctx, ++ ++ .get_parent_aio_context = blk_root_get_parent_aio_context, ++}; ++ ++/* ++ * Create a new BlockBackend with a reference count of one. ++ * ++ * @perm is a bitmasks of BLK_PERM_* constants which describes the permissions ++ * to request for a block driver node that is attached to this BlockBackend. ++ * @shared_perm is a bitmask which describes which permissions may be granted ++ * to other users of the attached node. ++ * Both sets of permissions can be changed later using blk_set_perm(). ++ * ++ * Return the new BlockBackend on success, null on failure. ++ */ ++BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm) ++{ ++ BlockBackend *blk; ++ ++ GLOBAL_STATE_CODE(); ++ ++ blk = g_new0(BlockBackend, 1); ++ blk->refcnt = 1; ++ blk->ctx = ctx; ++ blk->perm = perm; ++ blk->shared_perm = shared_perm; ++ blk_set_enable_write_cache(blk, true); ++ ++ blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT; ++ blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; ++ ++ block_acct_init(&blk->stats); ++ ++ qemu_mutex_init(&blk->queued_requests_lock); ++ qemu_co_queue_init(&blk->queued_requests); ++ notifier_list_init(&blk->remove_bs_notifiers); ++ notifier_list_init(&blk->insert_bs_notifiers); ++ QLIST_INIT(&blk->aio_notifiers); ++ ++ QTAILQ_INSERT_TAIL(&block_backends, blk, link); ++ return blk; ++} ++ ++/* ++ * Create a new BlockBackend connected to an existing BlockDriverState. ++ * ++ * @perm is a bitmasks of BLK_PERM_* constants which describes the ++ * permissions to request for @bs that is attached to this ++ * BlockBackend. @shared_perm is a bitmask which describes which ++ * permissions may be granted to other users of the attached node. ++ * Both sets of permissions can be changed later using blk_set_perm(). ++ * ++ * Return the new BlockBackend on success, null on failure. ++ */ ++BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm, ++ uint64_t shared_perm, Error **errp) ++{ ++ BlockBackend *blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (blk_insert_bs(blk, bs, errp) < 0) { ++ blk_unref(blk); ++ return NULL; ++ } ++ return blk; ++} ++ ++/* ++ * Creates a new BlockBackend, opens a new BlockDriverState, and connects both. ++ * By default, the new BlockBackend is in the main AioContext, but if the ++ * parameters connect it with any existing node in a different AioContext, it ++ * may end up there instead. ++ * ++ * Just as with bdrv_open(), after having called this function the reference to ++ * @options belongs to the block layer (even on failure). ++ * ++ * TODO: Remove @filename and @flags; it should be possible to specify a whole ++ * BDS tree just by specifying the @options QDict (or @reference, ++ * alternatively). At the time of adding this function, this is not possible, ++ * though, so callers of this function have to be able to specify @filename and ++ * @flags. ++ */ ++BlockBackend *blk_new_open(const char *filename, const char *reference, ++ QDict *options, int flags, Error **errp) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ uint64_t perm = 0; ++ uint64_t shared = BLK_PERM_ALL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * blk_new_open() is mainly used in .bdrv_create implementations and the ++ * tools where sharing isn't a major concern because the BDS stays private ++ * and the file is generally not supposed to be used by a second process, ++ * so we just request permission according to the flags. ++ * ++ * The exceptions are xen_disk and blockdev_init(); in these cases, the ++ * caller of blk_new_open() doesn't make use of the permissions, but they ++ * shouldn't hurt either. We can still share everything here because the ++ * guest devices will add their own blockers if they can't share. ++ */ ++ if ((flags & BDRV_O_NO_IO) == 0) { ++ perm |= BLK_PERM_CONSISTENT_READ; ++ if (flags & BDRV_O_RDWR) { ++ perm |= BLK_PERM_WRITE; ++ } ++ } ++ if (flags & BDRV_O_RESIZE) { ++ perm |= BLK_PERM_RESIZE; ++ } ++ if (flags & BDRV_O_NO_SHARE) { ++ shared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; ++ } ++ ++ bs = bdrv_open(filename, reference, options, flags, errp); ++ if (!bs) { ++ return NULL; ++ } ++ ++ /* bdrv_open() could have moved bs to a different AioContext */ ++ blk = blk_new(bdrv_get_aio_context(bs), perm, shared); ++ blk->perm = perm; ++ blk->shared_perm = shared; ++ ++ blk_insert_bs(blk, bs, errp); ++ bdrv_unref(bs); ++ ++ if (!blk->root) { ++ blk_unref(blk); ++ return NULL; ++ } ++ ++ return blk; ++} ++ ++static void blk_delete(BlockBackend *blk) ++{ ++ assert(!blk->refcnt); ++ assert(!blk->name); ++ assert(!blk->dev); ++ if (blk->public.throttle_group_member.throttle_state) { ++ blk_io_limits_disable(blk); ++ } ++ if (blk->root) { ++ blk_remove_bs(blk); ++ } ++ if (blk->vmsh) { ++ qemu_del_vm_change_state_handler(blk->vmsh); ++ blk->vmsh = NULL; ++ } ++ assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); ++ assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); ++ assert(QLIST_EMPTY(&blk->aio_notifiers)); ++ assert(qemu_co_queue_empty(&blk->queued_requests)); ++ qemu_mutex_destroy(&blk->queued_requests_lock); ++ QTAILQ_REMOVE(&block_backends, blk, link); ++ drive_info_del(blk->legacy_dinfo); ++ block_acct_cleanup(&blk->stats); ++ g_free(blk); ++} ++ ++static void drive_info_del(DriveInfo *dinfo) ++{ ++ if (!dinfo) { ++ return; ++ } ++ qemu_opts_del(dinfo->opts); ++ g_free(dinfo); ++} ++ ++int blk_get_refcnt(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk ? blk->refcnt : 0; ++} ++ ++/* ++ * Increment @blk's reference count. ++ * @blk must not be null. ++ */ ++void blk_ref(BlockBackend *blk) ++{ ++ assert(blk->refcnt > 0); ++ GLOBAL_STATE_CODE(); ++ blk->refcnt++; ++} ++ ++/* ++ * Decrement @blk's reference count. ++ * If this drops it to zero, destroy @blk. ++ * For convenience, do nothing if @blk is null. ++ */ ++void blk_unref(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk) { ++ assert(blk->refcnt > 0); ++ if (blk->refcnt > 1) { ++ blk->refcnt--; ++ } else { ++ blk_drain(blk); ++ /* blk_drain() cannot resurrect blk, nobody held a reference */ ++ assert(blk->refcnt == 1); ++ blk->refcnt = 0; ++ blk_delete(blk); ++ } ++ } ++} ++ ++/* ++ * Behaves similarly to blk_next() but iterates over all BlockBackends, even the ++ * ones which are hidden (i.e. are not referenced by the monitor). ++ */ ++BlockBackend *blk_all_next(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk ? QTAILQ_NEXT(blk, link) ++ : QTAILQ_FIRST(&block_backends); ++} ++ ++void blk_remove_all_bs(void) ++{ ++ BlockBackend *blk = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ while ((blk = blk_all_next(blk)) != NULL) { ++ if (blk->root) { ++ blk_remove_bs(blk); ++ } ++ } ++} ++ ++/* ++ * Return the monitor-owned BlockBackend after @blk. ++ * If @blk is null, return the first one. ++ * Else, return @blk's next sibling, which may be null. ++ * ++ * To iterate over all BlockBackends, do ++ * for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ * ... ++ * } ++ */ ++BlockBackend *blk_next(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk ? QTAILQ_NEXT(blk, monitor_link) ++ : QTAILQ_FIRST(&monitor_block_backends); ++} ++ ++/* Iterates over all top-level BlockDriverStates, i.e. BDSs that are owned by ++ * the monitor or attached to a BlockBackend */ ++BlockDriverState *bdrv_next(BdrvNextIterator *it) ++{ ++ BlockDriverState *bs, *old_bs; ++ ++ /* Must be called from the main loop */ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ ++ old_bs = it->bs; ++ ++ /* First, return all root nodes of BlockBackends. In order to avoid ++ * returning a BDS twice when multiple BBs refer to it, we only return it ++ * if the BB is the first one in the parent list of the BDS. */ ++ if (it->phase == BDRV_NEXT_BACKEND_ROOTS) { ++ BlockBackend *old_blk = it->blk; ++ ++ do { ++ it->blk = blk_all_next(it->blk); ++ bs = it->blk ? blk_bs(it->blk) : NULL; ++ } while (it->blk && (bs == NULL || bdrv_first_blk(bs) != it->blk)); ++ ++ if (it->blk) { ++ blk_ref(it->blk); ++ } ++ blk_unref(old_blk); ++ ++ if (bs) { ++ bdrv_ref(bs); ++ bdrv_unref(old_bs); ++ it->bs = bs; ++ return bs; ++ } ++ it->phase = BDRV_NEXT_MONITOR_OWNED; ++ } ++ ++ /* Then return the monitor-owned BDSes without a BB attached. Ignore all ++ * BDSes that are attached to a BlockBackend here; they have been handled ++ * by the above block already */ ++ do { ++ it->bs = bdrv_next_monitor_owned(it->bs); ++ bs = it->bs; ++ } while (bs && bdrv_has_blk(bs)); ++ ++ if (bs) { ++ bdrv_ref(bs); ++ } ++ bdrv_unref(old_bs); ++ ++ return bs; ++} ++ ++static void bdrv_next_reset(BdrvNextIterator *it) ++{ ++ *it = (BdrvNextIterator) { ++ .phase = BDRV_NEXT_BACKEND_ROOTS, ++ }; ++} ++ ++BlockDriverState *bdrv_first(BdrvNextIterator *it) ++{ ++ GLOBAL_STATE_CODE(); ++ bdrv_next_reset(it); ++ return bdrv_next(it); ++} ++ ++/* Must be called when aborting a bdrv_next() iteration before ++ * bdrv_next() returns NULL */ ++void bdrv_next_cleanup(BdrvNextIterator *it) ++{ ++ /* Must be called from the main loop */ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ ++ bdrv_unref(it->bs); ++ ++ if (it->phase == BDRV_NEXT_BACKEND_ROOTS && it->blk) { ++ blk_unref(it->blk); ++ } ++ ++ bdrv_next_reset(it); ++} ++ ++/* ++ * Add a BlockBackend into the list of backends referenced by the monitor, with ++ * the given @name acting as the handle for the monitor. ++ * Strictly for use by blockdev.c. ++ * ++ * @name must not be null or empty. ++ * ++ * Returns true on success and false on failure. In the latter case, an Error ++ * object is returned through @errp. ++ */ ++bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp) ++{ ++ assert(!blk->name); ++ assert(name && name[0]); ++ GLOBAL_STATE_CODE(); ++ ++ if (!id_wellformed(name)) { ++ error_setg(errp, "Invalid device name"); ++ return false; ++ } ++ if (blk_by_name(name)) { ++ error_setg(errp, "Device with id '%s' already exists", name); ++ return false; ++ } ++ if (bdrv_find_node(name)) { ++ error_setg(errp, ++ "Device name '%s' conflicts with an existing node name", ++ name); ++ return false; ++ } ++ ++ blk->name = g_strdup(name); ++ QTAILQ_INSERT_TAIL(&monitor_block_backends, blk, monitor_link); ++ return true; ++} ++ ++/* ++ * Remove a BlockBackend from the list of backends referenced by the monitor. ++ * Strictly for use by blockdev.c. ++ */ ++void monitor_remove_blk(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ if (!blk->name) { ++ return; ++ } ++ ++ QTAILQ_REMOVE(&monitor_block_backends, blk, monitor_link); ++ g_free(blk->name); ++ blk->name = NULL; ++} ++ ++/* ++ * Return @blk's name, a non-null string. ++ * Returns an empty string iff @blk is not referenced by the monitor. ++ */ ++const char *blk_name(const BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->name ?: ""; ++} ++ ++/* ++ * Return the BlockBackend with name @name if it exists, else null. ++ * @name must not be null. ++ */ ++BlockBackend *blk_by_name(const char *name) ++{ ++ BlockBackend *blk = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ assert(name); ++ while ((blk = blk_next(blk)) != NULL) { ++ if (!strcmp(name, blk->name)) { ++ return blk; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Return the BlockDriverState attached to @blk if any, else null. ++ */ ++BlockDriverState *blk_bs(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->root ? blk->root->bs : NULL; ++} ++ ++static BlockBackend * GRAPH_RDLOCK bdrv_first_blk(BlockDriverState *bs) ++{ ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(child, &bs->parents, next_parent) { ++ if (child->klass == &child_root) { ++ return child->opaque; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Returns true if @bs has an associated BlockBackend. ++ */ ++bool bdrv_has_blk(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_first_blk(bs) != NULL; ++} ++ ++/* ++ * Returns true if @bs has only BlockBackends as parents. ++ */ ++bool bdrv_is_root_node(BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->klass != &child_root) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * Return @blk's DriveInfo if any, else null. ++ */ ++DriveInfo *blk_legacy_dinfo(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->legacy_dinfo; ++} ++ ++/* ++ * Set @blk's DriveInfo to @dinfo, and return it. ++ * @blk must not have a DriveInfo set already. ++ * No other BlockBackend may have the same DriveInfo set. ++ */ ++DriveInfo *blk_set_legacy_dinfo(BlockBackend *blk, DriveInfo *dinfo) ++{ ++ assert(!blk->legacy_dinfo); ++ GLOBAL_STATE_CODE(); ++ return blk->legacy_dinfo = dinfo; ++} ++ ++/* ++ * Return the BlockBackend with DriveInfo @dinfo. ++ * It must exist. ++ */ ++BlockBackend *blk_by_legacy_dinfo(DriveInfo *dinfo) ++{ ++ BlockBackend *blk = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ while ((blk = blk_next(blk)) != NULL) { ++ if (blk->legacy_dinfo == dinfo) { ++ return blk; ++ } ++ } ++ abort(); ++} ++ ++/* ++ * Returns a pointer to the publicly accessible fields of @blk. ++ */ ++BlockBackendPublic *blk_get_public(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return &blk->public; ++} ++ ++/* ++ * Returns a BlockBackend given the associated @public fields. ++ */ ++BlockBackend *blk_by_public(BlockBackendPublic *public) ++{ ++ GLOBAL_STATE_CODE(); ++ return container_of(public, BlockBackend, public); ++} ++ ++/* ++ * Disassociates the currently associated BlockDriverState from @blk. ++ */ ++void blk_remove_bs(BlockBackend *blk) ++{ ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ BdrvChild *root; ++ ++ GLOBAL_STATE_CODE(); ++ ++ notifier_list_notify(&blk->remove_bs_notifiers, blk); ++ if (tgm->throttle_state) { ++ BlockDriverState *bs = blk_bs(blk); ++ ++ /* ++ * Take a ref in case blk_bs() changes across bdrv_drained_begin(), for ++ * example, if a temporary filter node is removed by a blockjob. ++ */ ++ bdrv_ref(bs); ++ bdrv_drained_begin(bs); ++ throttle_group_detach_aio_context(tgm); ++ throttle_group_attach_aio_context(tgm, qemu_get_aio_context()); ++ bdrv_drained_end(bs); ++ bdrv_unref(bs); ++ } ++ ++ blk_update_root_state(blk); ++ ++ /* bdrv_root_unref_child() will cause blk->root to become stale and may ++ * switch to a completion coroutine later on. Let's drain all I/O here ++ * to avoid that and a potential QEMU crash. ++ */ ++ blk_drain(blk); ++ root = blk->root; ++ blk->root = NULL; ++ ++ bdrv_graph_wrlock(); ++ bdrv_root_unref_child(root); ++ bdrv_graph_wrunlock(); ++} ++ ++/* ++ * Associates a new BlockDriverState with @blk. ++ */ ++int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) ++{ ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ ++ GLOBAL_STATE_CODE(); ++ bdrv_ref(bs); ++ bdrv_graph_wrlock(); ++ blk->root = bdrv_root_attach_child(bs, "root", &child_root, ++ BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, ++ blk->perm, blk->shared_perm, ++ blk, errp); ++ bdrv_graph_wrunlock(); ++ if (blk->root == NULL) { ++ return -EPERM; ++ } ++ ++ notifier_list_notify(&blk->insert_bs_notifiers, blk); ++ if (tgm->throttle_state) { ++ throttle_group_detach_aio_context(tgm); ++ throttle_group_attach_aio_context(tgm, bdrv_get_aio_context(bs)); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Change BlockDriverState associated with @blk. ++ */ ++int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_replace_child_bs(blk->root, new_bs, errp); ++} ++ ++/* ++ * Sets the permission bitmasks that the user of the BlockBackend needs. ++ */ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++blk_set_perm_locked(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, ++ Error **errp) ++{ ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ if (blk->root && !blk->disable_perm) { ++ ret = bdrv_child_try_set_perm(blk->root, perm, shared_perm, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ blk->perm = perm; ++ blk->shared_perm = shared_perm; ++ ++ return 0; ++} ++ ++int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, ++ Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ return blk_set_perm_locked(blk, perm, shared_perm, errp); ++} ++ ++void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) ++{ ++ GLOBAL_STATE_CODE(); ++ *perm = blk->perm; ++ *shared_perm = blk->shared_perm; ++} ++ ++/* ++ * Attach device model @dev to @blk. ++ * Return 0 on success, -EBUSY when a device model is attached already. ++ */ ++int blk_attach_dev(BlockBackend *blk, DeviceState *dev) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk->dev) { ++ return -EBUSY; ++ } ++ ++ /* While migration is still incoming, we don't need to apply the ++ * permissions of guest device BlockBackends. We might still have a block ++ * job or NBD server writing to the image for storage migration. */ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ blk->disable_perm = true; ++ } ++ ++ blk_ref(blk); ++ blk->dev = dev; ++ blk_iostatus_reset(blk); ++ ++ return 0; ++} ++ ++/* ++ * Detach device model @dev from @blk. ++ * @dev must be currently attached to @blk. ++ */ ++void blk_detach_dev(BlockBackend *blk, DeviceState *dev) ++{ ++ assert(blk->dev == dev); ++ GLOBAL_STATE_CODE(); ++ blk->dev = NULL; ++ blk->dev_ops = NULL; ++ blk->dev_opaque = NULL; ++ blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort); ++ blk_unref(blk); ++} ++ ++/* ++ * Return the device model attached to @blk if any, else null. ++ */ ++DeviceState *blk_get_attached_dev(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->dev; ++} ++ ++/* Return the qdev ID, or if no ID is assigned the QOM path, of the block ++ * device attached to the BlockBackend. */ ++char *blk_get_attached_dev_id(BlockBackend *blk) ++{ ++ DeviceState *dev = blk->dev; ++ IO_CODE(); ++ ++ if (!dev) { ++ return g_strdup(""); ++ } else if (dev->id) { ++ return g_strdup(dev->id); ++ } ++ ++ return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); ++} ++ ++/* ++ * Return the BlockBackend which has the device model @dev attached if it ++ * exists, else null. ++ * ++ * @dev must not be null. ++ */ ++BlockBackend *blk_by_dev(void *dev) ++{ ++ BlockBackend *blk = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ assert(dev != NULL); ++ while ((blk = blk_all_next(blk)) != NULL) { ++ if (blk->dev == dev) { ++ return blk; ++ } ++ } ++ return NULL; ++} ++ ++/* ++ * Set @blk's device model callbacks to @ops. ++ * @opaque is the opaque argument to pass to the callbacks. ++ * This is for use by device models. ++ */ ++void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, ++ void *opaque) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->dev_ops = ops; ++ blk->dev_opaque = opaque; ++ ++ /* Are we currently quiesced? Should we enforce this right now? */ ++ if (qatomic_read(&blk->quiesce_counter) && ops && ops->drained_begin) { ++ ops->drained_begin(opaque); ++ } ++} ++ ++/* ++ * Notify @blk's attached device model of media change. ++ * ++ * If @load is true, notify of media load. This action can fail, meaning that ++ * the medium cannot be loaded. @errp is set then. ++ * ++ * If @load is false, notify of media eject. This can never fail. ++ * ++ * Also send DEVICE_TRAY_MOVED events as appropriate. ++ */ ++void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk->dev_ops && blk->dev_ops->change_media_cb) { ++ bool tray_was_open, tray_is_open; ++ Error *local_err = NULL; ++ ++ tray_was_open = blk_dev_is_tray_open(blk); ++ blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); ++ if (local_err) { ++ assert(load == true); ++ error_propagate(errp, local_err); ++ return; ++ } ++ tray_is_open = blk_dev_is_tray_open(blk); ++ ++ if (tray_was_open != tray_is_open) { ++ char *id = blk_get_attached_dev_id(blk); ++ qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); ++ g_free(id); ++ } ++ } ++} ++ ++static void blk_root_change_media(BdrvChild *child, bool load) ++{ ++ blk_dev_change_media_cb(child->opaque, load, NULL); ++} ++ ++/* ++ * Does @blk's attached device model have removable media? ++ * %true if no device model is attached. ++ */ ++bool blk_dev_has_removable_media(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return !blk->dev || (blk->dev_ops && blk->dev_ops->change_media_cb); ++} ++ ++/* ++ * Does @blk's attached device model have a tray? ++ */ ++bool blk_dev_has_tray(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->dev_ops && blk->dev_ops->is_tray_open; ++} ++ ++/* ++ * Notify @blk's attached device model of a media eject request. ++ * If @force is true, the medium is about to be yanked out forcefully. ++ */ ++void blk_dev_eject_request(BlockBackend *blk, bool force) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk->dev_ops && blk->dev_ops->eject_request_cb) { ++ blk->dev_ops->eject_request_cb(blk->dev_opaque, force); ++ } ++} ++ ++/* ++ * Does @blk's attached device model have a tray, and is it open? ++ */ ++bool blk_dev_is_tray_open(BlockBackend *blk) ++{ ++ IO_CODE(); ++ if (blk_dev_has_tray(blk)) { ++ return blk->dev_ops->is_tray_open(blk->dev_opaque); ++ } ++ return false; ++} ++ ++/* ++ * Does @blk's attached device model have the medium locked? ++ * %false if the device model has no such lock. ++ */ ++bool blk_dev_is_medium_locked(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk->dev_ops && blk->dev_ops->is_medium_locked) { ++ return blk->dev_ops->is_medium_locked(blk->dev_opaque); ++ } ++ return false; ++} ++ ++/* ++ * Notify @blk's attached device model of a backend size change. ++ */ ++static void blk_root_resize(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ ++ if (blk->dev_ops && blk->dev_ops->resize_cb) { ++ blk->dev_ops->resize_cb(blk->dev_opaque); ++ } ++} ++ ++void blk_iostatus_enable(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->iostatus_enabled = true; ++ blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; ++} ++ ++/* The I/O status is only enabled if the drive explicitly ++ * enables it _and_ the VM is configured to stop on errors */ ++bool blk_iostatus_is_enabled(const BlockBackend *blk) ++{ ++ IO_CODE(); ++ return (blk->iostatus_enabled && ++ (blk->on_write_error == BLOCKDEV_ON_ERROR_ENOSPC || ++ blk->on_write_error == BLOCKDEV_ON_ERROR_STOP || ++ blk->on_read_error == BLOCKDEV_ON_ERROR_STOP)); ++} ++ ++BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->iostatus; ++} ++ ++void blk_iostatus_disable(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->iostatus_enabled = false; ++} ++ ++void blk_iostatus_reset(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ if (blk_iostatus_is_enabled(blk)) { ++ blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; ++ } ++} ++ ++void blk_iostatus_set_err(BlockBackend *blk, int error) ++{ ++ IO_CODE(); ++ assert(blk_iostatus_is_enabled(blk)); ++ if (blk->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { ++ blk->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : ++ BLOCK_DEVICE_IO_STATUS_FAILED; ++ } ++} ++ ++void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow) ++{ ++ IO_CODE(); ++ blk->allow_write_beyond_eof = allow; ++} ++ ++void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow) ++{ ++ IO_CODE(); ++ blk->allow_aio_context_change = allow; ++} ++ ++void blk_set_disable_request_queuing(BlockBackend *blk, bool disable) ++{ ++ IO_CODE(); ++ qatomic_set(&blk->disable_request_queuing, disable); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++blk_check_byte_request(BlockBackend *blk, int64_t offset, int64_t bytes) ++{ ++ int64_t len; ++ ++ if (bytes < 0) { ++ return -EIO; ++ } ++ ++ if (!blk_co_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ if (offset < 0) { ++ return -EIO; ++ } ++ ++ if (!blk->allow_write_beyond_eof) { ++ len = bdrv_co_getlength(blk_bs(blk)); ++ if (len < 0) { ++ return len; ++ } ++ ++ if (offset > len || len - offset < bytes) { ++ return -EIO; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Are we currently in a drained section? */ ++bool blk_in_drain(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); /* change to IO_OR_GS_CODE(), if necessary */ ++ return qatomic_read(&blk->quiesce_counter); ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static void coroutine_fn blk_wait_while_drained(BlockBackend *blk) ++{ ++ assert(blk->in_flight > 0); ++ ++ if (qatomic_read(&blk->quiesce_counter) && ++ !qatomic_read(&blk->disable_request_queuing)) { ++ /* ++ * Take lock before decrementing in flight counter so main loop thread ++ * waits for us to enqueue ourselves before it can leave the drained ++ * section. ++ */ ++ qemu_mutex_lock(&blk->queued_requests_lock); ++ blk_dec_in_flight(blk); ++ qemu_co_queue_wait(&blk->queued_requests, &blk->queued_requests_lock); ++ blk_inc_in_flight(blk); ++ qemu_mutex_unlock(&blk->queued_requests_lock); ++ } ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn ++blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ BlockDriverState *bs; ++ IO_CODE(); ++ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ /* Call blk_bs() only after waiting, the graph may have changed */ ++ bs = blk_bs(blk); ++ trace_blk_co_preadv(blk, bs, offset, bytes, flags); ++ ++ ret = blk_check_byte_request(blk, offset, bytes); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ /* throttling disk I/O */ ++ if (blk->public.throttle_group_member.throttle_state) { ++ throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, ++ bytes, THROTTLE_READ); ++ } ++ ++ ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset, ++ flags); ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++int coroutine_fn blk_co_pread(BlockBackend *blk, int64_t offset, int64_t bytes, ++ void *buf, BdrvRequestFlags flags) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_OR_GS_CODE(); ++ ++ assert(bytes <= SIZE_MAX); ++ ++ return blk_co_preadv(blk, offset, bytes, &qiov, flags); ++} ++ ++int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_preadv_part(blk, offset, bytes, qiov, 0, flags); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++int coroutine_fn blk_co_preadv_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset, BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_preadv_part(blk, offset, bytes, qiov, qiov_offset, flags); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn ++blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ BlockDriverState *bs; ++ IO_CODE(); ++ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ /* Call blk_bs() only after waiting, the graph may have changed */ ++ bs = blk_bs(blk); ++ trace_blk_co_pwritev(blk, bs, offset, bytes, flags); ++ ++ ret = blk_check_byte_request(blk, offset, bytes); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ /* throttling disk I/O */ ++ if (blk->public.throttle_group_member.throttle_state) { ++ throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, ++ bytes, THROTTLE_WRITE); ++ } ++ ++ if (!blk->enable_write_cache) { ++ flags |= BDRV_REQ_FUA; ++ } ++ ++ ret = bdrv_co_pwritev_part(blk->root, offset, bytes, qiov, qiov_offset, ++ flags); ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset, ++ int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++int coroutine_fn blk_co_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, ++ const void *buf, BdrvRequestFlags flags) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_OR_GS_CODE(); ++ ++ assert(bytes <= SIZE_MAX); ++ ++ return blk_co_pwritev(blk, offset, bytes, &qiov, flags); ++} ++ ++int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ IO_OR_GS_CODE(); ++ return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags); ++} ++ ++int coroutine_fn blk_co_block_status_above(BlockBackend *blk, ++ BlockDriverState *base, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ return bdrv_co_block_status_above(blk_bs(blk), base, offset, bytes, pnum, ++ map, file); ++} ++ ++int coroutine_fn blk_co_is_allocated_above(BlockBackend *blk, ++ BlockDriverState *base, ++ bool include_base, int64_t offset, ++ int64_t bytes, int64_t *pnum) ++{ ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ return bdrv_co_is_allocated_above(blk_bs(blk), base, include_base, offset, ++ bytes, pnum); ++} ++ ++typedef struct BlkRwCo { ++ BlockBackend *blk; ++ int64_t offset; ++ void *iobuf; ++ int ret; ++ BdrvRequestFlags flags; ++} BlkRwCo; ++ ++int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) ++{ ++ GLOBAL_STATE_CODE(); ++ return bdrv_make_zero(blk->root, flags); ++} ++ ++void blk_inc_in_flight(BlockBackend *blk) ++{ ++ IO_CODE(); ++ qatomic_inc(&blk->in_flight); ++} ++ ++void blk_dec_in_flight(BlockBackend *blk) ++{ ++ IO_CODE(); ++ qatomic_dec(&blk->in_flight); ++ aio_wait_kick(); ++} ++ ++static void error_callback_bh(void *opaque) ++{ ++ struct BlockBackendAIOCB *acb = opaque; ++ ++ blk_dec_in_flight(acb->blk); ++ acb->common.cb(acb->common.opaque, acb->ret); ++ qemu_aio_unref(acb); ++} ++ ++BlockAIOCB *blk_abort_aio_request(BlockBackend *blk, ++ BlockCompletionFunc *cb, ++ void *opaque, int ret) ++{ ++ struct BlockBackendAIOCB *acb; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque); ++ acb->blk = blk; ++ acb->ret = ret; ++ ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ error_callback_bh, acb); ++ return &acb->common; ++} ++ ++typedef struct BlkAioEmAIOCB { ++ BlockAIOCB common; ++ BlkRwCo rwco; ++ int64_t bytes; ++ bool has_returned; ++} BlkAioEmAIOCB; ++ ++static const AIOCBInfo blk_aio_em_aiocb_info = { ++ .aiocb_size = sizeof(BlkAioEmAIOCB), ++}; ++ ++static void blk_aio_complete(BlkAioEmAIOCB *acb) ++{ ++ if (acb->has_returned) { ++ acb->common.cb(acb->common.opaque, acb->rwco.ret); ++ blk_dec_in_flight(acb->rwco.blk); ++ qemu_aio_unref(acb); ++ } ++} ++ ++static void blk_aio_complete_bh(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ assert(acb->has_returned); ++ blk_aio_complete(acb); ++} ++ ++static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, ++ int64_t bytes, ++ void *iobuf, CoroutineEntry co_entry, ++ BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ BlkAioEmAIOCB *acb; ++ Coroutine *co; ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); ++ acb->rwco = (BlkRwCo) { ++ .blk = blk, ++ .offset = offset, ++ .iobuf = iobuf, ++ .flags = flags, ++ .ret = NOT_DONE, ++ }; ++ acb->bytes = bytes; ++ acb->has_returned = false; ++ ++ co = qemu_coroutine_create(co_entry, acb); ++ aio_co_enter(qemu_get_current_aio_context(), co); ++ ++ acb->has_returned = true; ++ if (acb->rwco.ret != NOT_DONE) { ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ blk_aio_complete_bh, acb); ++ } ++ ++ return &acb->common; ++} ++ ++static void coroutine_fn blk_aio_read_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ QEMUIOVector *qiov = rwco->iobuf; ++ ++ assert(qiov->size == acb->bytes); ++ rwco->ret = blk_co_do_preadv_part(rwco->blk, rwco->offset, acb->bytes, qiov, ++ 0, rwco->flags); ++ blk_aio_complete(acb); ++} ++ ++static void coroutine_fn blk_aio_write_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ QEMUIOVector *qiov = rwco->iobuf; ++ ++ assert(!qiov || qiov->size == acb->bytes); ++ rwco->ret = blk_co_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes, ++ qiov, 0, rwco->flags); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_write_entry, ++ flags | BDRV_REQ_ZERO_WRITE, cb, opaque); ++} ++ ++int64_t coroutine_fn blk_co_getlength(BlockBackend *blk) ++{ ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (!blk_co_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_co_getlength(blk_bs(blk)); ++} ++ ++int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (!bs) { ++ return -ENOMEDIUM; ++ } else { ++ return bdrv_co_nb_sectors(bs); ++ } ++} ++ ++/* ++ * This wrapper is written by hand because this function is in the hot I/O path, ++ * via blk_get_geometry. ++ */ ++int64_t coroutine_mixed_fn blk_nb_sectors(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ++ IO_CODE(); ++ ++ if (!bs) { ++ return -ENOMEDIUM; ++ } else { ++ return bdrv_nb_sectors(bs); ++ } ++} ++ ++/* return 0 as number of sectors if no device present or error */ ++void coroutine_fn blk_co_get_geometry(BlockBackend *blk, ++ uint64_t *nb_sectors_ptr) ++{ ++ int64_t ret = blk_co_nb_sectors(blk); ++ *nb_sectors_ptr = ret < 0 ? 0 : ret; ++} ++ ++/* ++ * This wrapper is written by hand because this function is in the hot I/O path. ++ */ ++void coroutine_mixed_fn blk_get_geometry(BlockBackend *blk, ++ uint64_t *nb_sectors_ptr) ++{ ++ int64_t ret = blk_nb_sectors(blk); ++ *nb_sectors_ptr = ret < 0 ? 0 : ret; ++} ++ ++BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ assert((uint64_t)qiov->size <= INT64_MAX); ++ return blk_aio_prwv(blk, offset, qiov->size, qiov, ++ blk_aio_read_entry, flags, cb, opaque); ++} ++ ++BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ assert((uint64_t)qiov->size <= INT64_MAX); ++ return blk_aio_prwv(blk, offset, qiov->size, qiov, ++ blk_aio_write_entry, flags, cb, opaque); ++} ++ ++void blk_aio_cancel(BlockAIOCB *acb) ++{ ++ GLOBAL_STATE_CODE(); ++ bdrv_aio_cancel(acb); ++} ++ ++void blk_aio_cancel_async(BlockAIOCB *acb) ++{ ++ IO_CODE(); ++ bdrv_aio_cancel_async(acb); ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn ++blk_co_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf) ++{ ++ IO_CODE(); ++ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (!blk_co_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_co_ioctl(blk_bs(blk), req, buf); ++} ++ ++int coroutine_fn blk_co_ioctl(BlockBackend *blk, unsigned long int req, ++ void *buf) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_ioctl(blk, req, buf); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++static void coroutine_fn blk_aio_ioctl_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_do_ioctl(rwco->blk, rwco->offset, rwco->iobuf); ++ ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque); ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn ++blk_co_do_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) ++{ ++ int ret; ++ IO_CODE(); ++ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ ret = blk_check_byte_request(blk, offset, bytes); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return bdrv_co_pdiscard(blk->root, offset, bytes); ++} ++ ++static void coroutine_fn blk_aio_pdiscard_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_do_pdiscard(rwco->blk, rwco->offset, acb->bytes); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, ++ int64_t offset, int64_t bytes, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0, ++ cb, opaque); ++} ++ ++int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, ++ int64_t bytes) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_pdiscard(blk, offset, bytes); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++/* To be called between exactly one pair of blk_inc/dec_in_flight() */ ++static int coroutine_fn blk_co_do_flush(BlockBackend *blk) ++{ ++ IO_CODE(); ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (!blk_co_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_co_flush(blk_bs(blk)); ++} ++ ++static void coroutine_fn blk_aio_flush_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_do_flush(rwco->blk); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_flush(BlockBackend *blk, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque); ++} ++ ++int coroutine_fn blk_co_flush(BlockBackend *blk) ++{ ++ int ret; ++ IO_OR_GS_CODE(); ++ ++ blk_inc_in_flight(blk); ++ ret = blk_co_do_flush(blk); ++ blk_dec_in_flight(blk); ++ ++ return ret; ++} ++ ++static void coroutine_fn blk_aio_zone_report_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_zone_report(rwco->blk, rwco->offset, ++ (unsigned int*)(uintptr_t)acb->bytes, ++ rwco->iobuf); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ BlkAioEmAIOCB *acb; ++ Coroutine *co; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); ++ acb->rwco = (BlkRwCo) { ++ .blk = blk, ++ .offset = offset, ++ .iobuf = zones, ++ .ret = NOT_DONE, ++ }; ++ acb->bytes = (int64_t)(uintptr_t)nr_zones, ++ acb->has_returned = false; ++ ++ co = qemu_coroutine_create(blk_aio_zone_report_entry, acb); ++ aio_co_enter(qemu_get_current_aio_context(), co); ++ ++ acb->has_returned = true; ++ if (acb->rwco.ret != NOT_DONE) { ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ blk_aio_complete_bh, acb); ++ } ++ ++ return &acb->common; ++} ++ ++static void coroutine_fn blk_aio_zone_mgmt_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_zone_mgmt(rwco->blk, ++ (BlockZoneOp)(uintptr_t)rwco->iobuf, ++ rwco->offset, acb->bytes); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len, ++ BlockCompletionFunc *cb, void *opaque) { ++ BlkAioEmAIOCB *acb; ++ Coroutine *co; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); ++ acb->rwco = (BlkRwCo) { ++ .blk = blk, ++ .offset = offset, ++ .iobuf = (void *)(uintptr_t)op, ++ .ret = NOT_DONE, ++ }; ++ acb->bytes = len; ++ acb->has_returned = false; ++ ++ co = qemu_coroutine_create(blk_aio_zone_mgmt_entry, acb); ++ aio_co_enter(qemu_get_current_aio_context(), co); ++ ++ acb->has_returned = true; ++ if (acb->rwco.ret != NOT_DONE) { ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ blk_aio_complete_bh, acb); ++ } ++ ++ return &acb->common; ++} ++ ++static void coroutine_fn blk_aio_zone_append_entry(void *opaque) ++{ ++ BlkAioEmAIOCB *acb = opaque; ++ BlkRwCo *rwco = &acb->rwco; ++ ++ rwco->ret = blk_co_zone_append(rwco->blk, (int64_t *)(uintptr_t)acb->bytes, ++ rwco->iobuf, rwco->flags); ++ blk_aio_complete(acb); ++} ++ ++BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags, ++ BlockCompletionFunc *cb, void *opaque) { ++ BlkAioEmAIOCB *acb; ++ Coroutine *co; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque); ++ acb->rwco = (BlkRwCo) { ++ .blk = blk, ++ .ret = NOT_DONE, ++ .flags = flags, ++ .iobuf = qiov, ++ }; ++ acb->bytes = (int64_t)(uintptr_t)offset; ++ acb->has_returned = false; ++ ++ co = qemu_coroutine_create(blk_aio_zone_append_entry, acb); ++ aio_co_enter(qemu_get_current_aio_context(), co); ++ acb->has_returned = true; ++ if (acb->rwco.ret != NOT_DONE) { ++ replay_bh_schedule_oneshot_event(qemu_get_current_aio_context(), ++ blk_aio_complete_bh, acb); ++ } ++ ++ return &acb->common; ++} ++ ++/* ++ * Send a zone_report command. ++ * offset is a byte offset from the start of the device. No alignment ++ * required for offset. ++ * nr_zones represents IN maximum and OUT actual. ++ */ ++int coroutine_fn blk_co_zone_report(BlockBackend *blk, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones) ++{ ++ int ret; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); /* increase before waiting */ ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ if (!blk_is_available(blk)) { ++ blk_dec_in_flight(blk); ++ return -ENOMEDIUM; ++ } ++ ret = bdrv_co_zone_report(blk_bs(blk), offset, nr_zones, zones); ++ blk_dec_in_flight(blk); ++ return ret; ++} ++ ++/* ++ * Send a zone_management command. ++ * op is the zone operation; ++ * offset is the byte offset from the start of the zoned device; ++ * len is the maximum number of bytes the command should operate on. It ++ * should be aligned with the device zone size. ++ */ ++int coroutine_fn blk_co_zone_mgmt(BlockBackend *blk, BlockZoneOp op, ++ int64_t offset, int64_t len) ++{ ++ int ret; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ ++ ret = blk_check_byte_request(blk, offset, len); ++ if (ret < 0) { ++ blk_dec_in_flight(blk); ++ return ret; ++ } ++ ++ ret = bdrv_co_zone_mgmt(blk_bs(blk), op, offset, len); ++ blk_dec_in_flight(blk); ++ return ret; ++} ++ ++/* ++ * Send a zone_append command. ++ */ ++int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_CODE(); ++ ++ blk_inc_in_flight(blk); ++ blk_wait_while_drained(blk); ++ GRAPH_RDLOCK_GUARD(); ++ if (!blk_is_available(blk)) { ++ blk_dec_in_flight(blk); ++ return -ENOMEDIUM; ++ } ++ ++ ret = bdrv_co_zone_append(blk_bs(blk), offset, qiov, flags); ++ blk_dec_in_flight(blk); ++ return ret; ++} ++ ++void blk_drain(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_ref(bs); ++ bdrv_drained_begin(bs); ++ } ++ ++ /* We may have -ENOMEDIUM completions in flight */ ++ AIO_WAIT_WHILE(blk_get_aio_context(blk), ++ qatomic_read(&blk->in_flight) > 0); ++ ++ if (bs) { ++ bdrv_drained_end(bs); ++ bdrv_unref(bs); ++ } ++} ++ ++void blk_drain_all(void) ++{ ++ BlockBackend *blk = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_drain_all_begin(); ++ ++ while ((blk = blk_all_next(blk)) != NULL) { ++ /* We may have -ENOMEDIUM completions in flight */ ++ AIO_WAIT_WHILE_UNLOCKED(NULL, qatomic_read(&blk->in_flight) > 0); ++ } ++ ++ bdrv_drain_all_end(); ++} ++ ++void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, ++ BlockdevOnError on_write_error) ++{ ++ GLOBAL_STATE_CODE(); ++ blk->on_read_error = on_read_error; ++ blk->on_write_error = on_write_error; ++} ++ ++BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) ++{ ++ IO_CODE(); ++ return is_read ? blk->on_read_error : blk->on_write_error; ++} ++ ++BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, ++ int error) ++{ ++ BlockdevOnError on_err = blk_get_on_error(blk, is_read); ++ IO_CODE(); ++ ++ switch (on_err) { ++ case BLOCKDEV_ON_ERROR_ENOSPC: ++ return (error == ENOSPC) ? ++ BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; ++ case BLOCKDEV_ON_ERROR_STOP: ++ return BLOCK_ERROR_ACTION_STOP; ++ case BLOCKDEV_ON_ERROR_REPORT: ++ return BLOCK_ERROR_ACTION_REPORT; ++ case BLOCKDEV_ON_ERROR_IGNORE: ++ return BLOCK_ERROR_ACTION_IGNORE; ++ case BLOCKDEV_ON_ERROR_AUTO: ++ default: ++ abort(); ++ } ++} ++ ++static void send_qmp_error_event(BlockBackend *blk, ++ BlockErrorAction action, ++ bool is_read, int error) ++{ ++ IoOperationType optype; ++ BlockDriverState *bs = blk_bs(blk); ++ ++ optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE; ++ qapi_event_send_block_io_error(blk_name(blk), ++ bs ? bdrv_get_node_name(bs) : NULL, optype, ++ action, blk_iostatus_is_enabled(blk), ++ error == ENOSPC, strerror(error)); ++} ++ ++/* This is done by device models because, while the block layer knows ++ * about the error, it does not know whether an operation comes from ++ * the device or the block layer (from a job, for example). ++ */ ++void blk_error_action(BlockBackend *blk, BlockErrorAction action, ++ bool is_read, int error) ++{ ++ assert(error >= 0); ++ IO_CODE(); ++ ++ if (action == BLOCK_ERROR_ACTION_STOP) { ++ /* First set the iostatus, so that "info block" returns an iostatus ++ * that matches the events raised so far (an additional error iostatus ++ * is fine, but not a lost one). ++ */ ++ blk_iostatus_set_err(blk, error); ++ ++ /* Then raise the request to stop the VM and the event. ++ * qemu_system_vmstop_request_prepare has two effects. First, ++ * it ensures that the STOP event always comes after the ++ * BLOCK_IO_ERROR event. Second, it ensures that even if management ++ * can observe the STOP event and do a "cont" before the STOP ++ * event is issued, the VM will not stop. In this case, vm_start() ++ * also ensures that the STOP/RESUME pair of events is emitted. ++ */ ++ qemu_system_vmstop_request_prepare(); ++ send_qmp_error_event(blk, action, is_read, error); ++ qemu_system_vmstop_request(RUN_STATE_IO_ERROR); ++ } else { ++ send_qmp_error_event(blk, action, is_read, error); ++ } ++} ++ ++/* ++ * Returns true if the BlockBackend can support taking write permissions ++ * (because its root node is not read-only). ++ */ ++bool blk_supports_write_perm(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ return !bdrv_is_read_only(bs); ++ } else { ++ return blk->root_state.open_flags & BDRV_O_RDWR; ++ } ++} ++ ++/* ++ * Returns true if the BlockBackend can be written to in its current ++ * configuration (i.e. if write permission have been requested) ++ */ ++bool blk_is_writable(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->perm & BLK_PERM_WRITE; ++} ++ ++bool blk_is_sg(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs) { ++ return false; ++ } ++ ++ return bdrv_is_sg(bs); ++} ++ ++bool blk_enable_write_cache(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->enable_write_cache; ++} ++ ++void blk_set_enable_write_cache(BlockBackend *blk, bool wce) ++{ ++ IO_CODE(); ++ blk->enable_write_cache = wce; ++} ++ ++void blk_activate(BlockBackend *blk, Error **errp) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs) { ++ error_setg(errp, "Device '%s' has no medium", blk->name); ++ return; ++ } ++ ++ /* ++ * Migration code can call this function in coroutine context, so leave ++ * coroutine context if necessary. ++ */ ++ if (qemu_in_coroutine()) { ++ bdrv_co_activate(bs, errp); ++ } else { ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ bdrv_activate(bs, errp); ++ } ++} ++ ++bool coroutine_fn blk_co_is_inserted(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ return bs && bdrv_co_is_inserted(bs); ++} ++ ++bool coroutine_fn blk_co_is_available(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk_co_is_inserted(blk) && !blk_dev_is_tray_open(blk); ++} ++ ++void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (bs) { ++ bdrv_co_lock_medium(bs, locked); ++ } ++} ++ ++void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ char *id; ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ if (bs) { ++ bdrv_co_eject(bs, eject_flag); ++ } ++ ++ /* Whether or not we ejected on the backend, ++ * the frontend experienced a tray event. */ ++ id = blk_get_attached_dev_id(blk); ++ qapi_event_send_device_tray_moved(blk_name(blk), id, ++ eject_flag); ++ g_free(id); ++} ++ ++int blk_get_flags(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ return bdrv_get_flags(bs); ++ } else { ++ return blk->root_state.open_flags; ++ } ++} ++ ++/* Returns the minimum request alignment, in bytes; guaranteed nonzero */ ++uint32_t blk_get_request_alignment(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ IO_CODE(); ++ return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE; ++} ++ ++/* Returns the maximum hardware transfer length, in bytes; guaranteed nonzero */ ++uint64_t blk_get_max_hw_transfer(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ uint64_t max = INT_MAX; ++ IO_CODE(); ++ ++ if (bs) { ++ max = MIN_NON_ZERO(max, bs->bl.max_hw_transfer); ++ max = MIN_NON_ZERO(max, bs->bl.max_transfer); ++ } ++ return ROUND_DOWN(max, blk_get_request_alignment(blk)); ++} ++ ++/* Returns the maximum transfer length, in bytes; guaranteed nonzero */ ++uint32_t blk_get_max_transfer(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ uint32_t max = INT_MAX; ++ IO_CODE(); ++ ++ if (bs) { ++ max = MIN_NON_ZERO(max, bs->bl.max_transfer); ++ } ++ return ROUND_DOWN(max, blk_get_request_alignment(blk)); ++} ++ ++int blk_get_max_hw_iov(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return MIN_NON_ZERO(blk->root->bs->bl.max_hw_iov, ++ blk->root->bs->bl.max_iov); ++} ++ ++int blk_get_max_iov(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return blk->root->bs->bl.max_iov; ++} ++ ++void *blk_try_blockalign(BlockBackend *blk, size_t size) ++{ ++ IO_CODE(); ++ return qemu_try_blockalign(blk ? blk_bs(blk) : NULL, size); ++} ++ ++void *blk_blockalign(BlockBackend *blk, size_t size) ++{ ++ IO_CODE(); ++ return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); ++} ++ ++bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!bs) { ++ return false; ++ } ++ ++ return bdrv_op_is_blocked(bs, op, errp); ++} ++ ++void blk_op_unblock(BlockBackend *blk, BlockOpType op, Error *reason) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_op_unblock(bs, op, reason); ++ } ++} ++ ++void blk_op_block_all(BlockBackend *blk, Error *reason) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_op_block_all(bs, reason); ++ } ++} ++ ++void blk_op_unblock_all(BlockBackend *blk, Error *reason) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_op_unblock_all(bs, reason); ++ } ++} ++ ++/** ++ * Return BB's current AioContext. Note that this context may change ++ * concurrently at any time, with one exception: If the BB has a root node ++ * attached, its context will only change through bdrv_try_change_aio_context(), ++ * which creates a drained section. Therefore, incrementing such a BB's ++ * in-flight counter will prevent its context from changing. ++ */ ++AioContext *blk_get_aio_context(BlockBackend *blk) ++{ ++ IO_CODE(); ++ ++ if (!blk) { ++ return qemu_get_aio_context(); ++ } ++ ++ return qatomic_read(&blk->ctx); ++} ++ ++int blk_set_aio_context(BlockBackend *blk, AioContext *new_context, ++ Error **errp) ++{ ++ bool old_allow_change; ++ BlockDriverState *bs = blk_bs(blk); ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!bs) { ++ qatomic_set(&blk->ctx, new_context); ++ return 0; ++ } ++ ++ bdrv_ref(bs); ++ ++ old_allow_change = blk->allow_aio_context_change; ++ blk->allow_aio_context_change = true; ++ ++ ret = bdrv_try_change_aio_context(bs, new_context, NULL, errp); ++ ++ blk->allow_aio_context_change = old_allow_change; ++ ++ bdrv_unref(bs); ++ return ret; ++} ++ ++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; ++ AioContext *new_context = s->new_ctx; ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ ++ qatomic_set(&blk->ctx, new_context); ++ if (tgm->throttle_state) { ++ throttle_group_detach_aio_context(tgm); ++ throttle_group_attach_aio_context(tgm, new_context); ++ } ++} ++ ++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; ++} ++ ++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) ++{ ++ BlockBackendAioNotifier *notifier; ++ BlockDriverState *bs = blk_bs(blk); ++ GLOBAL_STATE_CODE(); ++ ++ notifier = g_new(BlockBackendAioNotifier, 1); ++ notifier->attached_aio_context = attached_aio_context; ++ notifier->detach_aio_context = detach_aio_context; ++ notifier->opaque = opaque; ++ QLIST_INSERT_HEAD(&blk->aio_notifiers, notifier, list); ++ ++ if (bs) { ++ bdrv_add_aio_context_notifier(bs, attached_aio_context, ++ detach_aio_context, opaque); ++ } ++} ++ ++void blk_remove_aio_context_notifier(BlockBackend *blk, ++ void (*attached_aio_context)(AioContext *, ++ void *), ++ void (*detach_aio_context)(void *), ++ void *opaque) ++{ ++ BlockBackendAioNotifier *notifier; ++ BlockDriverState *bs = blk_bs(blk); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_remove_aio_context_notifier(bs, attached_aio_context, ++ detach_aio_context, opaque); ++ } ++ ++ QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { ++ if (notifier->attached_aio_context == attached_aio_context && ++ notifier->detach_aio_context == detach_aio_context && ++ notifier->opaque == opaque) { ++ QLIST_REMOVE(notifier, list); ++ g_free(notifier); ++ return; ++ } ++ } ++ ++ abort(); ++} ++ ++void blk_add_remove_bs_notifier(BlockBackend *blk, Notifier *notify) ++{ ++ GLOBAL_STATE_CODE(); ++ notifier_list_add(&blk->remove_bs_notifiers, notify); ++} ++ ++void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify) ++{ ++ GLOBAL_STATE_CODE(); ++ notifier_list_add(&blk->insert_bs_notifiers, notify); ++} ++ ++BlockAcctStats *blk_get_stats(BlockBackend *blk) ++{ ++ IO_CODE(); ++ return &blk->stats; ++} ++ ++void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ IO_CODE(); ++ return qemu_aio_get(aiocb_info, blk_bs(blk), cb, opaque); ++} ++ ++int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags) ++{ ++ IO_OR_GS_CODE(); ++ return blk_co_pwritev(blk, offset, bytes, NULL, ++ flags | BDRV_REQ_ZERO_WRITE); ++} ++ ++int coroutine_fn blk_co_pwrite_compressed(BlockBackend *blk, int64_t offset, ++ int64_t bytes, const void *buf) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); ++ IO_OR_GS_CODE(); ++ return blk_co_pwritev_part(blk, offset, bytes, &qiov, 0, ++ BDRV_REQ_WRITE_COMPRESSED); ++} ++ ++int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, ++ Error **errp) ++{ ++ IO_OR_GS_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ if (!blk_co_is_available(blk)) { ++ error_setg(errp, "No medium inserted"); ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_co_truncate(blk->root, offset, exact, prealloc, flags, errp); ++} ++ ++int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, ++ int64_t pos, int size) ++{ ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ if (!blk_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ ret = bdrv_save_vmstate(blk_bs(blk), buf, pos, size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (ret == size && !blk->enable_write_cache) { ++ ret = bdrv_flush(blk_bs(blk)); ++ } ++ ++ return ret < 0 ? ret : size; ++} ++ ++int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!blk_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_load_vmstate(blk_bs(blk), buf, pos, size); ++} ++ ++int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!blk_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_probe_blocksizes(blk_bs(blk), bsz); ++} ++ ++int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo) ++{ ++ GLOBAL_STATE_CODE(); ++ if (!blk_is_available(blk)) { ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_probe_geometry(blk_bs(blk), geo); ++} ++ ++/* ++ * Updates the BlockBackendRootState object with data from the currently ++ * attached BlockDriverState. ++ */ ++void blk_update_root_state(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(blk->root); ++ ++ blk->root_state.open_flags = blk->root->bs->open_flags; ++ blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes; ++} ++ ++/* ++ * Returns the detect-zeroes setting to be used for bdrv_open() of a ++ * BlockDriverState which is supposed to inherit the root state. ++ */ ++bool blk_get_detect_zeroes_from_root_state(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->root_state.detect_zeroes; ++} ++ ++/* ++ * Returns the flags to be used for bdrv_open() of a BlockDriverState which is ++ * supposed to inherit the root state. ++ */ ++int blk_get_open_flags_from_root_state(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->root_state.open_flags; ++} ++ ++BlockBackendRootState *blk_get_root_state(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return &blk->root_state; ++} ++ ++int blk_commit_all(void) ++{ ++ BlockBackend *blk = NULL; ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ while ((blk = blk_all_next(blk)) != NULL) { ++ BlockDriverState *unfiltered_bs = bdrv_skip_filters(blk_bs(blk)); ++ ++ if (blk_is_inserted(blk) && bdrv_cow_child(unfiltered_bs)) { ++ int ret; ++ ++ ret = bdrv_commit(unfiltered_bs); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ } ++ return 0; ++} ++ ++ ++/* throttling disk I/O limits */ ++void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg) ++{ ++ GLOBAL_STATE_CODE(); ++ throttle_group_config(&blk->public.throttle_group_member, cfg); ++} ++ ++void blk_io_limits_disable(BlockBackend *blk) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ assert(tgm->throttle_state); ++ GLOBAL_STATE_CODE(); ++ if (bs) { ++ bdrv_ref(bs); ++ bdrv_drained_begin(bs); ++ } ++ throttle_group_unregister_tgm(tgm); ++ if (bs) { ++ bdrv_drained_end(bs); ++ bdrv_unref(bs); ++ } ++} ++ ++/* should be called before blk_set_io_limits if a limit is set */ ++void blk_io_limits_enable(BlockBackend *blk, const char *group) ++{ ++ assert(!blk->public.throttle_group_member.throttle_state); ++ GLOBAL_STATE_CODE(); ++ throttle_group_register_tgm(&blk->public.throttle_group_member, ++ group, blk_get_aio_context(blk)); ++} ++ ++void blk_io_limits_update_group(BlockBackend *blk, const char *group) ++{ ++ GLOBAL_STATE_CODE(); ++ /* this BB is not part of any group */ ++ if (!blk->public.throttle_group_member.throttle_state) { ++ return; ++ } ++ ++ /* this BB is a part of the same group than the one we want */ ++ if (!g_strcmp0(throttle_group_get_name(&blk->public.throttle_group_member), ++ group)) { ++ return; ++ } ++ ++ /* need to change the group this bs belong to */ ++ blk_io_limits_disable(blk); ++ blk_io_limits_enable(blk, group); ++} ++ ++static void blk_root_drained_begin(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ ++ if (qatomic_fetch_inc(&blk->quiesce_counter) == 0) { ++ if (blk->dev_ops && blk->dev_ops->drained_begin) { ++ blk->dev_ops->drained_begin(blk->dev_opaque); ++ } ++ } ++ ++ /* Note that blk->root may not be accessible here yet if we are just ++ * attaching to a BlockDriverState that is drained. Use child instead. */ ++ ++ if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) { ++ throttle_group_restart_tgm(tgm); ++ } ++} ++ ++static bool blk_root_drained_poll(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ bool busy = false; ++ assert(qatomic_read(&blk->quiesce_counter)); ++ ++ if (blk->dev_ops && blk->dev_ops->drained_poll) { ++ busy = blk->dev_ops->drained_poll(blk->dev_opaque); ++ } ++ return busy || !!blk->in_flight; ++} ++ ++static void blk_root_drained_end(BdrvChild *child) ++{ ++ BlockBackend *blk = child->opaque; ++ assert(qatomic_read(&blk->quiesce_counter)); ++ ++ assert(blk->public.throttle_group_member.io_limits_disabled); ++ qatomic_dec(&blk->public.throttle_group_member.io_limits_disabled); ++ ++ if (qatomic_fetch_dec(&blk->quiesce_counter) == 1) { ++ if (blk->dev_ops && blk->dev_ops->drained_end) { ++ blk->dev_ops->drained_end(blk->dev_opaque); ++ } ++ qemu_mutex_lock(&blk->queued_requests_lock); ++ while (qemu_co_enter_next(&blk->queued_requests, ++ &blk->queued_requests_lock)) { ++ /* Resume all queued requests */ ++ } ++ qemu_mutex_unlock(&blk->queued_requests_lock); ++ } ++} ++ ++bool blk_register_buf(BlockBackend *blk, void *host, size_t size, Error **errp) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ return bdrv_register_buf(bs, host, size, errp); ++ } ++ return true; ++} ++ ++void blk_unregister_buf(BlockBackend *blk, void *host, size_t size) ++{ ++ BlockDriverState *bs = blk_bs(blk); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (bs) { ++ bdrv_unregister_buf(bs, host, size); ++ } ++} ++ ++int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, ++ BlockBackend *blk_out, int64_t off_out, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ int r; ++ IO_CODE(); ++ GRAPH_RDLOCK_GUARD(); ++ ++ r = blk_check_byte_request(blk_in, off_in, bytes); ++ if (r) { ++ return r; ++ } ++ r = blk_check_byte_request(blk_out, off_out, bytes); ++ if (r) { ++ return r; ++ } ++ ++ return bdrv_co_copy_range(blk_in->root, off_in, ++ blk_out->root, off_out, ++ bytes, read_flags, write_flags); ++} ++ ++const BdrvChild *blk_root(BlockBackend *blk) ++{ ++ GLOBAL_STATE_CODE(); ++ return blk->root; ++} ++ ++int blk_make_empty(BlockBackend *blk, Error **errp) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!blk_is_available(blk)) { ++ error_setg(errp, "No medium inserted"); ++ return -ENOMEDIUM; ++ } ++ ++ return bdrv_make_empty(blk->root, errp); ++} +diff --git a/qcow2/lib/block/block-gen.c b/qcow2/lib/block/block-gen.c +new file mode 100644 +index 00000000..ff2832fa +--- /dev/null ++++ b/qcow2/lib/block/block-gen.c +@@ -0,0 +1,2636 @@ ++/* ++ * File is generated by scripts/block-coroutine-wrapper.py ++ * ++ * Copyright (c) 2020 Virtuozzo International GmbH. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/coroutines.h" ++#include "block/block-gen.h" ++#include "block/block_int.h" ++#include "block/dirty-bitmap.h" ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_pwrite_zeroes ++ */ ++ ++typedef struct BdrvPwriteZeroes { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++ BdrvRequestFlags flags; ++} BdrvPwriteZeroes; ++ ++static void coroutine_fn bdrv_co_pwrite_zeroes_entry(void *opaque) ++{ ++ BdrvPwriteZeroes *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pwrite_zeroes(s->child, s->offset, s->bytes, s->flags); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int64_t bytes, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pwrite_zeroes(child, offset, bytes, flags); ++ } else { ++ BdrvPwriteZeroes s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pwrite_zeroes_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_pread ++ */ ++ ++typedef struct BdrvPread { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++ void *buf; ++ BdrvRequestFlags flags; ++} BdrvPread; ++ ++static void coroutine_fn bdrv_co_pread_entry(void *opaque) ++{ ++ BdrvPread *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pread(s->child, s->offset, s->bytes, s->buf, s->flags); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pread(BdrvChild *child, int64_t offset, int64_t bytes, void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pread(child, offset, bytes, buf, flags); ++ } else { ++ BdrvPread s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pread_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_pwrite ++ */ ++ ++typedef struct BdrvPwrite { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++ const void *buf; ++ BdrvRequestFlags flags; ++} BdrvPwrite; ++ ++static void coroutine_fn bdrv_co_pwrite_entry(void *opaque) ++{ ++ BdrvPwrite *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pwrite(s->child, s->offset, s->bytes, s->buf, s->flags); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pwrite(BdrvChild *child, int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pwrite(child, offset, bytes, buf, flags); ++ } else { ++ BdrvPwrite s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pwrite_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_pwrite_sync ++ */ ++ ++typedef struct BdrvPwriteSync { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++ const void *buf; ++ BdrvRequestFlags flags; ++} BdrvPwriteSync; ++ ++static void coroutine_fn bdrv_co_pwrite_sync_entry(void *opaque) ++{ ++ BdrvPwriteSync *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pwrite_sync(s->child, s->offset, s->bytes, s->buf, s->flags); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pwrite_sync(child, offset, bytes, buf, flags); ++ } else { ++ BdrvPwriteSync s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pwrite_sync_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_getlength ++ */ ++ ++typedef struct BdrvGetlength { ++ BdrvPollCo poll_state; ++ int64_t ret; ++ BlockDriverState *bs; ++} BdrvGetlength; ++ ++static void coroutine_fn bdrv_co_getlength_entry(void *opaque) ++{ ++ BdrvGetlength *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_getlength(s->bs); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int64_t bdrv_getlength(BlockDriverState *bs) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_getlength(bs); ++ } else { ++ BdrvGetlength s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_getlength_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_get_allocated_file_size ++ */ ++ ++typedef struct BdrvGetAllocatedFileSize { ++ BdrvPollCo poll_state; ++ int64_t ret; ++ BlockDriverState *bs; ++} BdrvGetAllocatedFileSize; ++ ++static void coroutine_fn bdrv_co_get_allocated_file_size_entry(void *opaque) ++{ ++ BdrvGetAllocatedFileSize *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_get_allocated_file_size(s->bs); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int64_t bdrv_get_allocated_file_size(BlockDriverState *bs) ++{ ++ BdrvGetAllocatedFileSize s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_get_allocated_file_size_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_block_status ++ */ ++ ++typedef struct BdrvBlockStatus { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++ int64_t *map; ++ BlockDriverState **file; ++} BdrvBlockStatus; ++ ++static void coroutine_fn bdrv_co_block_status_entry(void *opaque) ++{ ++ BdrvBlockStatus *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_block_status(s->bs, s->offset, s->bytes, s->pnum, s->map, s->file); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_block_status(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_block_status(bs, offset, bytes, pnum, map, file); ++ } else { ++ BdrvBlockStatus s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ .map = map, ++ .file = file, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_block_status_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_block_status_above ++ */ ++ ++typedef struct BdrvBlockStatusAbove { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BlockDriverState *base; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++ int64_t *map; ++ BlockDriverState **file; ++} BdrvBlockStatusAbove; ++ ++static void coroutine_fn bdrv_co_block_status_above_entry(void *opaque) ++{ ++ BdrvBlockStatusAbove *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_block_status_above(s->bs, s->base, s->offset, s->bytes, s->pnum, s->map, s->file); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_block_status_above(BlockDriverState *bs, BlockDriverState *base, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_block_status_above(bs, base, offset, bytes, pnum, map, file); ++ } else { ++ BdrvBlockStatusAbove s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .base = base, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ .map = map, ++ .file = file, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_block_status_above_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_is_allocated ++ */ ++ ++typedef struct BdrvIsAllocated { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++} BdrvIsAllocated; ++ ++static void coroutine_fn bdrv_co_is_allocated_entry(void *opaque) ++{ ++ BdrvIsAllocated *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_is_allocated(s->bs, s->offset, s->bytes, s->pnum); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_is_allocated(bs, offset, bytes, pnum); ++ } else { ++ BdrvIsAllocated s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_is_allocated_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_is_allocated_above ++ */ ++ ++typedef struct BdrvIsAllocatedAbove { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BlockDriverState *base; ++ bool include_base; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++} BdrvIsAllocatedAbove; ++ ++static void coroutine_fn bdrv_co_is_allocated_above_entry(void *opaque) ++{ ++ BdrvIsAllocatedAbove *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_is_allocated_above(s->bs, s->base, s->include_base, s->offset, s->bytes, s->pnum); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_is_allocated_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, int64_t offset, int64_t bytes, int64_t *pnum) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_is_allocated_above(bs, base, include_base, offset, bytes, pnum); ++ } else { ++ BdrvIsAllocatedAbove s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .base = base, ++ .include_base = include_base, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_is_allocated_above_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_is_inserted ++ */ ++ ++typedef struct BdrvIsInserted { ++ BdrvPollCo poll_state; ++ bool ret; ++ BlockDriverState *bs; ++} BdrvIsInserted; ++ ++static void coroutine_fn bdrv_co_is_inserted_entry(void *opaque) ++{ ++ BdrvIsInserted *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_is_inserted(s->bs); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++bool bdrv_is_inserted(BlockDriverState *bs) ++{ ++ BdrvIsInserted s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_is_inserted_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_get_info ++ */ ++ ++typedef struct BdrvGetInfo { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BlockDriverInfo *bdi; ++} BdrvGetInfo; ++ ++static void coroutine_fn bdrv_co_get_info_entry(void *opaque) ++{ ++ BdrvGetInfo *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_get_info(s->bs, s->bdi); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_get_info(bs, bdi); ++ } else { ++ BdrvGetInfo s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .bdi = bdi, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_get_info_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_change_backing_file ++ */ ++ ++typedef struct BdrvChangeBackingFile { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ const char *backing_file; ++ const char *backing_fmt; ++ bool warn; ++} BdrvChangeBackingFile; ++ ++static void coroutine_fn bdrv_co_change_backing_file_entry(void *opaque) ++{ ++ BdrvChangeBackingFile *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_change_backing_file(s->bs, s->backing_file, s->backing_fmt, s->warn); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt, bool warn) ++{ ++ BdrvChangeBackingFile s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .backing_file = backing_file, ++ .backing_fmt = backing_fmt, ++ .warn = warn, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_change_backing_file_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_debug_event ++ */ ++ ++typedef struct BdrvDebugEvent { ++ BdrvPollCo poll_state; ++ ++ BlockDriverState *bs; ++ BlkdebugEvent event; ++} BdrvDebugEvent; ++ ++static void coroutine_fn bdrv_co_debug_event_entry(void *opaque) ++{ ++ BdrvDebugEvent *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ bdrv_co_debug_event(s->bs, s->event); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ bdrv_co_debug_event(bs, event); ++ } else { ++ BdrvDebugEvent s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .event = event, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_debug_event_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_can_store_new_dirty_bitmap ++ */ ++ ++typedef struct BdrvCanStoreNewDirtyBitmap { ++ BdrvPollCo poll_state; ++ bool ret; ++ BlockDriverState *bs; ++ const char *name; ++ uint32_t granularity; ++ Error **errp; ++} BdrvCanStoreNewDirtyBitmap; ++ ++static void coroutine_fn bdrv_co_can_store_new_dirty_bitmap_entry(void *opaque) ++{ ++ BdrvCanStoreNewDirtyBitmap *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_can_store_new_dirty_bitmap(s->bs, s->name, s->granularity, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, uint32_t granularity, Error **errp) ++{ ++ BdrvCanStoreNewDirtyBitmap s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .name = name, ++ .granularity = granularity, ++ .errp = errp, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_can_store_new_dirty_bitmap_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_truncate ++ */ ++ ++typedef struct BdrvTruncate { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ bool exact; ++ PreallocMode prealloc; ++ BdrvRequestFlags flags; ++ Error **errp; ++} BdrvTruncate; ++ ++static void coroutine_fn bdrv_co_truncate_entry(void *opaque) ++{ ++ BdrvTruncate *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_truncate(s->child, s->offset, s->exact, s->prealloc, s->flags, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_truncate(BdrvChild *child, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_truncate(child, offset, exact, prealloc, flags, errp); ++ } else { ++ BdrvTruncate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .exact = exact, ++ .prealloc = prealloc, ++ .flags = flags, ++ .errp = errp, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_truncate_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_check ++ */ ++ ++typedef struct BdrvCheck { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BdrvCheckResult *res; ++ BdrvCheckMode fix; ++} BdrvCheck; ++ ++static void coroutine_fn bdrv_co_check_entry(void *opaque) ++{ ++ BdrvCheck *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_check(s->bs, s->res, s->fix); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_check(bs, res, fix); ++ } else { ++ BdrvCheck s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .res = res, ++ .fix = fix, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_check_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_invalidate_cache ++ */ ++ ++typedef struct BdrvInvalidateCache { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ Error **errp; ++} BdrvInvalidateCache; ++ ++static void coroutine_fn bdrv_co_invalidate_cache_entry(void *opaque) ++{ ++ BdrvInvalidateCache *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_invalidate_cache(s->bs, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_invalidate_cache(bs, errp); ++ } else { ++ BdrvInvalidateCache s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .errp = errp, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_invalidate_cache_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_flush ++ */ ++ ++typedef struct BdrvFlush { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++} BdrvFlush; ++ ++static void coroutine_fn bdrv_co_flush_entry(void *opaque) ++{ ++ BdrvFlush *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_flush(s->bs); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_flush(BlockDriverState *bs) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_flush(bs); ++ } else { ++ BdrvFlush s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_flush_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_pdiscard ++ */ ++ ++typedef struct BdrvPdiscard { ++ BdrvPollCo poll_state; ++ int ret; ++ BdrvChild *child; ++ int64_t offset; ++ int64_t bytes; ++} BdrvPdiscard; ++ ++static void coroutine_fn bdrv_co_pdiscard_entry(void *opaque) ++{ ++ BdrvPdiscard *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_pdiscard(s->child, s->offset, s->bytes); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_pdiscard(child, offset, bytes); ++ } else { ++ BdrvPdiscard s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .child = child, ++ .offset = offset, ++ .bytes = bytes, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_pdiscard_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_readv_vmstate ++ */ ++ ++typedef struct BdrvReadvVmstate { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ QEMUIOVector *qiov; ++ int64_t pos; ++} BdrvReadvVmstate; ++ ++static void coroutine_fn bdrv_co_readv_vmstate_entry(void *opaque) ++{ ++ BdrvReadvVmstate *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_readv_vmstate(s->bs, s->qiov, s->pos); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_readv_vmstate(bs, qiov, pos); ++ } else { ++ BdrvReadvVmstate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .qiov = qiov, ++ .pos = pos, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_readv_vmstate_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for bdrv_co_writev_vmstate ++ */ ++ ++typedef struct BdrvWritevVmstate { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ QEMUIOVector *qiov; ++ int64_t pos; ++} BdrvWritevVmstate; ++ ++static void coroutine_fn bdrv_co_writev_vmstate_entry(void *opaque) ++{ ++ BdrvWritevVmstate *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_writev_vmstate(s->bs, s->qiov, s->pos); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_writev_vmstate(bs, qiov, pos); ++ } else { ++ BdrvWritevVmstate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .qiov = qiov, ++ .pos = pos, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_writev_vmstate_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_remove_persistent_dirty_bitmap ++ */ ++ ++typedef struct BdrvRemovePersistentDirtyBitmap { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ const char *name; ++ Error **errp; ++} BdrvRemovePersistentDirtyBitmap; ++ ++static void coroutine_fn bdrv_co_remove_persistent_dirty_bitmap_entry(void *opaque) ++{ ++ BdrvRemovePersistentDirtyBitmap *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_remove_persistent_dirty_bitmap(s->bs, s->name, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, Error **errp) ++{ ++ BdrvRemovePersistentDirtyBitmap s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .name = name, ++ .errp = errp, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_remove_persistent_dirty_bitmap_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_refresh_total_sectors ++ */ ++ ++typedef struct BdrvRefreshTotalSectors { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ int64_t hint; ++} BdrvRefreshTotalSectors; ++ ++static void coroutine_fn bdrv_co_refresh_total_sectors_entry(void *opaque) ++{ ++ BdrvRefreshTotalSectors *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_refresh_total_sectors(s->bs, s->hint); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_refresh_total_sectors(bs, hint); ++ } else { ++ BdrvRefreshTotalSectors s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .hint = hint, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_refresh_total_sectors_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_create ++ */ ++ ++typedef struct BdrvCreate { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriver *drv; ++ const char *filename; ++ QemuOpts *opts; ++ Error **errp; ++} BdrvCreate; ++ ++static void coroutine_fn bdrv_co_create_entry(void *opaque) ++{ ++ BdrvCreate *s = opaque; ++ ++ ++ s->ret = bdrv_co_create(s->drv, s->filename, s->opts, s->errp); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_create(BlockDriver *drv, const char *filename, QemuOpts *opts, Error **errp) ++{ ++ BdrvCreate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .drv = drv, ++ .filename = filename, ++ .opts = opts, ++ .errp = errp, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_create_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_open_child ++ */ ++ ++typedef struct BdrvCoOpenChild { ++ Coroutine *co; ++ BdrvChild * ret; ++ const char *filename; ++ QDict *options; ++ const char *bdref_key; ++ BlockDriverState *parent; ++ const BdrvChildClass *child_class; ++ BdrvChildRole child_role; ++ bool allow_none; ++ Error **errp; ++} BdrvCoOpenChild; ++ ++static void bdrv_open_child_bh(void *opaque) ++{ ++ BdrvCoOpenChild *s = opaque; ++ ++ ++ s->ret = bdrv_open_child(s->filename, s->options, s->bdref_key, s->parent, s->child_class, s->child_role, s->allow_none, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BdrvChild * coroutine_fn bdrv_co_open_child(const char *filename, QDict *options, const char *bdref_key, BlockDriverState *parent, const BdrvChildClass *child_class, BdrvChildRole child_role, bool allow_none, Error **errp) ++{ ++ BdrvCoOpenChild s = { ++ .co = qemu_coroutine_self(), ++ .filename = filename, ++ .options = options, ++ .bdref_key = bdref_key, ++ .parent = parent, ++ .child_class = child_class, ++ .child_role = child_role, ++ .allow_none = allow_none, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_open_child_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_open_blockdev_ref ++ */ ++ ++typedef struct BdrvCoOpenBlockdevRef { ++ Coroutine *co; ++ BlockDriverState * ret; ++ BlockdevRef *ref; ++ Error **errp; ++} BdrvCoOpenBlockdevRef; ++ ++static void bdrv_open_blockdev_ref_bh(void *opaque) ++{ ++ BdrvCoOpenBlockdevRef *s = opaque; ++ ++ ++ s->ret = bdrv_open_blockdev_ref(s->ref, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BlockDriverState * coroutine_fn bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp) ++{ ++ BdrvCoOpenBlockdevRef s = { ++ .co = qemu_coroutine_self(), ++ .ref = ref, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_open_blockdev_ref_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_open ++ */ ++ ++typedef struct BdrvCoOpen { ++ Coroutine *co; ++ BlockDriverState * ret; ++ const char *filename; ++ const char *reference; ++ QDict *options; ++ int flags; ++ Error **errp; ++} BdrvCoOpen; ++ ++static void bdrv_open_bh(void *opaque) ++{ ++ BdrvCoOpen *s = opaque; ++ ++ ++ s->ret = bdrv_open(s->filename, s->reference, s->options, s->flags, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BlockDriverState * coroutine_fn bdrv_co_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp) ++{ ++ BdrvCoOpen s = { ++ .co = qemu_coroutine_self(), ++ .filename = filename, ++ .reference = reference, ++ .options = options, ++ .flags = flags, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_open_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_activate ++ */ ++ ++typedef struct BdrvCoActivate { ++ Coroutine *co; ++ int ret; ++ BlockDriverState *bs; ++ Error **errp; ++} BdrvCoActivate; ++ ++static void bdrv_activate_bh(void *opaque) ++{ ++ BdrvCoActivate *s = opaque; ++ ++ bdrv_graph_rdlock_main_loop(); ++ s->ret = bdrv_activate(s->bs, s->errp); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ aio_co_wake(s->co); ++} ++ ++int coroutine_fn bdrv_co_activate(BlockDriverState *bs, Error **errp) ++{ ++ BdrvCoActivate s = { ++ .co = qemu_coroutine_self(), ++ .bs = bs, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_activate_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for bdrv_unref ++ */ ++ ++typedef struct BdrvCoUnref { ++ Coroutine *co; ++ ++ BlockDriverState *bs; ++} BdrvCoUnref; ++ ++static void bdrv_unref_bh(void *opaque) ++{ ++ BdrvCoUnref *s = opaque; ++ ++ ++ bdrv_unref(s->bs); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++void coroutine_fn bdrv_co_unref(BlockDriverState *bs) ++{ ++ BdrvCoUnref s = { ++ .co = qemu_coroutine_self(), ++ .bs = bs, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_unref_bh, &s); ++ qemu_coroutine_yield(); ++ ++ ++} ++ ++ ++/* ++ * Wrappers for bdrv_unref_child ++ */ ++ ++typedef struct BdrvCoUnrefChild { ++ Coroutine *co; ++ ++ BlockDriverState *parent; ++ BdrvChild *child; ++} BdrvCoUnrefChild; ++ ++static void bdrv_unref_child_bh(void *opaque) ++{ ++ BdrvCoUnrefChild *s = opaque; ++ ++ bdrv_graph_wrlock(); ++ bdrv_unref_child(s->parent, s->child); ++ bdrv_graph_wrunlock(); ++ ++ aio_co_wake(s->co); ++} ++ ++void coroutine_fn bdrv_co_unref_child(BlockDriverState *parent, BdrvChild *child) ++{ ++ BdrvCoUnrefChild s = { ++ .co = qemu_coroutine_self(), ++ .parent = parent, ++ .child = child, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), bdrv_unref_child_bh, &s); ++ qemu_coroutine_yield(); ++ ++ ++} ++ ++ ++ ++/* ++ * Wrappers for blk_new_with_bs ++ */ ++ ++typedef struct BlkCoNewWithBs { ++ Coroutine *co; ++ BlockBackend * ret; ++ BlockDriverState *bs; ++ uint64_t perm; ++ uint64_t shared_perm; ++ Error **errp; ++} BlkCoNewWithBs; ++ ++static void blk_new_with_bs_bh(void *opaque) ++{ ++ BlkCoNewWithBs *s = opaque; ++ ++ ++ s->ret = blk_new_with_bs(s->bs, s->perm, s->shared_perm, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BlockBackend * coroutine_fn blk_co_new_with_bs(BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, Error **errp) ++{ ++ BlkCoNewWithBs s = { ++ .co = qemu_coroutine_self(), ++ .bs = bs, ++ .perm = perm, ++ .shared_perm = shared_perm, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_new_with_bs_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for blk_new_open ++ */ ++ ++typedef struct BlkCoNewOpen { ++ Coroutine *co; ++ BlockBackend * ret; ++ const char *filename; ++ const char *reference; ++ QDict *options; ++ int flags; ++ Error **errp; ++} BlkCoNewOpen; ++ ++static void blk_new_open_bh(void *opaque) ++{ ++ BlkCoNewOpen *s = opaque; ++ ++ ++ s->ret = blk_new_open(s->filename, s->reference, s->options, s->flags, s->errp); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++BlockBackend * coroutine_fn blk_co_new_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp) ++{ ++ BlkCoNewOpen s = { ++ .co = qemu_coroutine_self(), ++ .filename = filename, ++ .reference = reference, ++ .options = options, ++ .flags = flags, ++ .errp = errp, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_new_open_bh, &s); ++ qemu_coroutine_yield(); ++ ++ return s.ret; ++} ++ ++ ++/* ++ * Wrappers for blk_unref ++ */ ++ ++typedef struct BlkCoUnref { ++ Coroutine *co; ++ ++ BlockBackend *blk; ++} BlkCoUnref; ++ ++static void blk_unref_bh(void *opaque) ++{ ++ BlkCoUnref *s = opaque; ++ ++ ++ blk_unref(s->blk); ++ ++ ++ aio_co_wake(s->co); ++} ++ ++void coroutine_fn blk_co_unref(BlockBackend *blk) ++{ ++ BlkCoUnref s = { ++ .co = qemu_coroutine_self(), ++ .blk = blk, ++ }; ++ assert(qemu_in_coroutine()); ++ ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), blk_unref_bh, &s); ++ qemu_coroutine_yield(); ++ ++ ++} ++ ++ ++ ++/* ++ * Wrappers for blk_co_is_inserted ++ */ ++ ++typedef struct BlkIsInserted { ++ BdrvPollCo poll_state; ++ bool ret; ++ BlockBackend *blk; ++} BlkIsInserted; ++ ++static void coroutine_fn blk_co_is_inserted_entry(void *opaque) ++{ ++ BlkIsInserted *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = blk_co_is_inserted(s->blk); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++bool blk_is_inserted(BlockBackend *blk) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return blk_co_is_inserted(blk); ++ } else { ++ BlkIsInserted s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_is_inserted_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_is_available ++ */ ++ ++typedef struct BlkIsAvailable { ++ BdrvPollCo poll_state; ++ bool ret; ++ BlockBackend *blk; ++} BlkIsAvailable; ++ ++static void coroutine_fn blk_co_is_available_entry(void *opaque) ++{ ++ BlkIsAvailable *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = blk_co_is_available(s->blk); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++bool blk_is_available(BlockBackend *blk) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return blk_co_is_available(blk); ++ } else { ++ BlkIsAvailable s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_is_available_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_lock_medium ++ */ ++ ++typedef struct BlkLockMedium { ++ BdrvPollCo poll_state; ++ ++ BlockBackend *blk; ++ bool locked; ++} BlkLockMedium; ++ ++static void coroutine_fn blk_co_lock_medium_entry(void *opaque) ++{ ++ BlkLockMedium *s = opaque; ++ ++ ++ blk_co_lock_medium(s->blk, s->locked); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++void blk_lock_medium(BlockBackend *blk, bool locked) ++{ ++ BlkLockMedium s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .locked = locked, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_lock_medium_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ ++} ++ ++ ++/* ++ * Wrappers for blk_co_eject ++ */ ++ ++typedef struct BlkEject { ++ BdrvPollCo poll_state; ++ ++ BlockBackend *blk; ++ bool eject_flag; ++} BlkEject; ++ ++static void coroutine_fn blk_co_eject_entry(void *opaque) ++{ ++ BlkEject *s = opaque; ++ ++ ++ blk_co_eject(s->blk, s->eject_flag); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++void blk_eject(BlockBackend *blk, bool eject_flag) ++{ ++ BlkEject s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .eject_flag = eject_flag, ++ }; ++ assert(!qemu_in_coroutine()); ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_eject_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ ++} ++ ++ ++/* ++ * Wrappers for blk_co_getlength ++ */ ++ ++typedef struct BlkGetlength { ++ BdrvPollCo poll_state; ++ int64_t ret; ++ BlockBackend *blk; ++} BlkGetlength; ++ ++static void coroutine_fn blk_co_getlength_entry(void *opaque) ++{ ++ BlkGetlength *s = opaque; ++ ++ ++ s->ret = blk_co_getlength(s->blk); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int64_t blk_getlength(BlockBackend *blk) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_getlength(blk); ++ } else { ++ BlkGetlength s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_getlength_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pread ++ */ ++ ++typedef struct BlkPread { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ void *buf; ++ BdrvRequestFlags flags; ++} BlkPread; ++ ++static void coroutine_fn blk_co_pread_entry(void *opaque) ++{ ++ BlkPread *s = opaque; ++ ++ ++ s->ret = blk_co_pread(s->blk, s->offset, s->bytes, s->buf, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pread(BlockBackend *blk, int64_t offset, int64_t bytes, void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pread(blk, offset, bytes, buf, flags); ++ } else { ++ BlkPread s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pread_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_preadv ++ */ ++ ++typedef struct BlkPreadv { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ QEMUIOVector *qiov; ++ BdrvRequestFlags flags; ++} BlkPreadv; ++ ++static void coroutine_fn blk_co_preadv_entry(void *opaque) ++{ ++ BlkPreadv *s = opaque; ++ ++ ++ s->ret = blk_co_preadv(s->blk, s->offset, s->bytes, s->qiov, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_preadv(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_preadv(blk, offset, bytes, qiov, flags); ++ } else { ++ BlkPreadv s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov = qiov, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_preadv_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_preadv_part ++ */ ++ ++typedef struct BlkPreadvPart { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ QEMUIOVector *qiov; ++ size_t qiov_offset; ++ BdrvRequestFlags flags; ++} BlkPreadvPart; ++ ++static void coroutine_fn blk_co_preadv_part_entry(void *opaque) ++{ ++ BlkPreadvPart *s = opaque; ++ ++ ++ s->ret = blk_co_preadv_part(s->blk, s->offset, s->bytes, s->qiov, s->qiov_offset, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_preadv_part(blk, offset, bytes, qiov, qiov_offset, flags); ++ } else { ++ BlkPreadvPart s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov = qiov, ++ .qiov_offset = qiov_offset, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_preadv_part_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwrite ++ */ ++ ++typedef struct BlkPwrite { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ const void *buf; ++ BdrvRequestFlags flags; ++} BlkPwrite; ++ ++static void coroutine_fn blk_co_pwrite_entry(void *opaque) ++{ ++ BlkPwrite *s = opaque; ++ ++ ++ s->ret = blk_co_pwrite(s->blk, s->offset, s->bytes, s->buf, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwrite(BlockBackend *blk, int64_t offset, int64_t bytes, const void *buf, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwrite(blk, offset, bytes, buf, flags); ++ } else { ++ BlkPwrite s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwrite_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwritev ++ */ ++ ++typedef struct BlkPwritev { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ QEMUIOVector *qiov; ++ BdrvRequestFlags flags; ++} BlkPwritev; ++ ++static void coroutine_fn blk_co_pwritev_entry(void *opaque) ++{ ++ BlkPwritev *s = opaque; ++ ++ ++ s->ret = blk_co_pwritev(s->blk, s->offset, s->bytes, s->qiov, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwritev(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwritev(blk, offset, bytes, qiov, flags); ++ } else { ++ BlkPwritev s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov = qiov, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwritev_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwritev_part ++ */ ++ ++typedef struct BlkPwritevPart { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ QEMUIOVector *qiov; ++ size_t qiov_offset; ++ BdrvRequestFlags flags; ++} BlkPwritevPart; ++ ++static void coroutine_fn blk_co_pwritev_part_entry(void *opaque) ++{ ++ BlkPwritevPart *s = opaque; ++ ++ ++ s->ret = blk_co_pwritev_part(s->blk, s->offset, s->bytes, s->qiov, s->qiov_offset, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags); ++ } else { ++ BlkPwritevPart s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov = qiov, ++ .qiov_offset = qiov_offset, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwritev_part_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwrite_compressed ++ */ ++ ++typedef struct BlkPwriteCompressed { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ const void *buf; ++} BlkPwriteCompressed; ++ ++static void coroutine_fn blk_co_pwrite_compressed_entry(void *opaque) ++{ ++ BlkPwriteCompressed *s = opaque; ++ ++ ++ s->ret = blk_co_pwrite_compressed(s->blk, s->offset, s->bytes, s->buf); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, int64_t bytes, const void *buf) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwrite_compressed(blk, offset, bytes, buf); ++ } else { ++ BlkPwriteCompressed s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .buf = buf, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwrite_compressed_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pwrite_zeroes ++ */ ++ ++typedef struct BlkPwriteZeroes { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++ BdrvRequestFlags flags; ++} BlkPwriteZeroes; ++ ++static void coroutine_fn blk_co_pwrite_zeroes_entry(void *opaque) ++{ ++ BlkPwriteZeroes *s = opaque; ++ ++ ++ s->ret = blk_co_pwrite_zeroes(s->blk, s->offset, s->bytes, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pwrite_zeroes(blk, offset, bytes, flags); ++ } else { ++ BlkPwriteZeroes s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pwrite_zeroes_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_zone_report ++ */ ++ ++typedef struct BlkZoneReport { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ unsigned int *nr_zones; ++ BlockZoneDescriptor *zones; ++} BlkZoneReport; ++ ++static void coroutine_fn blk_co_zone_report_entry(void *opaque) ++{ ++ BlkZoneReport *s = opaque; ++ ++ ++ s->ret = blk_co_zone_report(s->blk, s->offset, s->nr_zones, s->zones); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_zone_report(BlockBackend *blk, int64_t offset, unsigned int *nr_zones, BlockZoneDescriptor *zones) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_zone_report(blk, offset, nr_zones, zones); ++ } else { ++ BlkZoneReport s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .nr_zones = nr_zones, ++ .zones = zones, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_zone_report_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_zone_mgmt ++ */ ++ ++typedef struct BlkZoneMgmt { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ BlockZoneOp op; ++ int64_t offset; ++ int64_t len; ++} BlkZoneMgmt; ++ ++static void coroutine_fn blk_co_zone_mgmt_entry(void *opaque) ++{ ++ BlkZoneMgmt *s = opaque; ++ ++ ++ s->ret = blk_co_zone_mgmt(s->blk, s->op, s->offset, s->len); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_zone_mgmt(BlockBackend *blk, BlockZoneOp op, int64_t offset, int64_t len) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_zone_mgmt(blk, op, offset, len); ++ } else { ++ BlkZoneMgmt s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .op = op, ++ .offset = offset, ++ .len = len, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_zone_mgmt_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_zone_append ++ */ ++ ++typedef struct BlkZoneAppend { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t *offset; ++ QEMUIOVector *qiov; ++ BdrvRequestFlags flags; ++} BlkZoneAppend; ++ ++static void coroutine_fn blk_co_zone_append_entry(void *opaque) ++{ ++ BlkZoneAppend *s = opaque; ++ ++ ++ s->ret = blk_co_zone_append(s->blk, s->offset, s->qiov, s->flags); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_zone_append(BlockBackend *blk, int64_t *offset, QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_zone_append(blk, offset, qiov, flags); ++ } else { ++ BlkZoneAppend s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .qiov = qiov, ++ .flags = flags, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_zone_append_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_pdiscard ++ */ ++ ++typedef struct BlkPdiscard { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ int64_t bytes; ++} BlkPdiscard; ++ ++static void coroutine_fn blk_co_pdiscard_entry(void *opaque) ++{ ++ BlkPdiscard *s = opaque; ++ ++ ++ s->ret = blk_co_pdiscard(s->blk, s->offset, s->bytes); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_pdiscard(blk, offset, bytes); ++ } else { ++ BlkPdiscard s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .bytes = bytes, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_pdiscard_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_flush ++ */ ++ ++typedef struct BlkFlush { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++} BlkFlush; ++ ++static void coroutine_fn blk_co_flush_entry(void *opaque) ++{ ++ BlkFlush *s = opaque; ++ ++ ++ s->ret = blk_co_flush(s->blk); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_flush(BlockBackend *blk) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_flush(blk); ++ } else { ++ BlkFlush s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_flush_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_ioctl ++ */ ++ ++typedef struct BlkIoctl { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ unsigned long int req; ++ void *buf; ++} BlkIoctl; ++ ++static void coroutine_fn blk_co_ioctl_entry(void *opaque) ++{ ++ BlkIoctl *s = opaque; ++ ++ ++ s->ret = blk_co_ioctl(s->blk, s->req, s->buf); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_ioctl(blk, req, buf); ++ } else { ++ BlkIoctl s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .req = req, ++ .buf = buf, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_ioctl_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for blk_co_truncate ++ */ ++ ++typedef struct BlkTruncate { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockBackend *blk; ++ int64_t offset; ++ bool exact; ++ PreallocMode prealloc; ++ BdrvRequestFlags flags; ++ Error **errp; ++} BlkTruncate; ++ ++static void coroutine_fn blk_co_truncate_entry(void *opaque) ++{ ++ BlkTruncate *s = opaque; ++ ++ ++ s->ret = blk_co_truncate(s->blk, s->offset, s->exact, s->prealloc, s->flags, s->errp); ++ ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int blk_truncate(BlockBackend *blk, int64_t offset, bool exact, PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) ++{ ++ if (qemu_in_coroutine()) { ++ ++ return blk_co_truncate(blk, offset, exact, prealloc, flags, errp); ++ } else { ++ BlkTruncate s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .blk = blk, ++ .offset = offset, ++ .exact = exact, ++ .prealloc = prealloc, ++ .flags = flags, ++ .errp = errp, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(blk_co_truncate_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++ ++/* ++ * Wrappers for bdrv_co_common_block_status_above ++ */ ++ ++typedef struct BdrvCommonBlockStatusAbove { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ BlockDriverState *base; ++ bool include_base; ++ bool want_zero; ++ int64_t offset; ++ int64_t bytes; ++ int64_t *pnum; ++ int64_t *map; ++ BlockDriverState **file; ++ int *depth; ++} BdrvCommonBlockStatusAbove; ++ ++static void coroutine_fn bdrv_co_common_block_status_above_entry(void *opaque) ++{ ++ BdrvCommonBlockStatusAbove *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = bdrv_co_common_block_status_above(s->bs, s->base, s->include_base, s->want_zero, s->offset, s->bytes, s->pnum, s->map, s->file, s->depth); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file, int *depth) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return bdrv_co_common_block_status_above(bs, base, include_base, want_zero, offset, bytes, pnum, map, file, depth); ++ } else { ++ BdrvCommonBlockStatusAbove s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .base = base, ++ .include_base = include_base, ++ .want_zero = want_zero, ++ .offset = offset, ++ .bytes = bytes, ++ .pnum = pnum, ++ .map = map, ++ .file = file, ++ .depth = depth, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(bdrv_co_common_block_status_above_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} ++ ++ ++/* ++ * Wrappers for nbd_co_do_establish_connection ++ */ ++ ++typedef struct NbdDoEstablishConnection { ++ BdrvPollCo poll_state; ++ int ret; ++ BlockDriverState *bs; ++ bool blocking; ++ Error **errp; ++} NbdDoEstablishConnection; ++ ++static void coroutine_fn nbd_co_do_establish_connection_entry(void *opaque) ++{ ++ NbdDoEstablishConnection *s = opaque; ++ ++ bdrv_graph_co_rdlock(); ++ s->ret = nbd_co_do_establish_connection(s->bs, s->blocking, s->errp); ++ bdrv_graph_co_rdunlock(); ++ s->poll_state.in_progress = false; ++ ++ aio_wait_kick(); ++} ++ ++int nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp) ++{ ++ if (qemu_in_coroutine()) { ++ assume_graph_lock(); ++ return nbd_co_do_establish_connection(bs, blocking, errp); ++ } else { ++ NbdDoEstablishConnection s = { ++ .poll_state.ctx = qemu_get_current_aio_context(), ++ .poll_state.in_progress = true, ++ ++ .bs = bs, ++ .blocking = blocking, ++ .errp = errp, ++ }; ++ ++ s.poll_state.co = qemu_coroutine_create(nbd_co_do_establish_connection_entry, &s); ++ ++ bdrv_poll_co(&s.poll_state); ++ return s.ret; ++ } ++} +diff --git a/qcow2/lib/block/block-gen.h b/qcow2/lib/block/block-gen.h +new file mode 100644 +index 00000000..89b7daaa +--- /dev/null ++++ b/qcow2/lib/block/block-gen.h +@@ -0,0 +1,46 @@ ++/* ++ * Block coroutine wrapping core, used by auto-generated block/block-gen.c ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2020 Virtuozzo International GmbH ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_BLOCK_GEN_H ++#define BLOCK_BLOCK_GEN_H ++ ++#include "block/block_int.h" ++ ++/* Base structure for argument packing structures */ ++typedef struct BdrvPollCo { ++ AioContext *ctx; ++ bool in_progress; ++ Coroutine *co; /* Keep pointer here for debugging */ ++} BdrvPollCo; ++ ++static inline void bdrv_poll_co(BdrvPollCo *s) ++{ ++ assert(!qemu_in_coroutine()); ++ ++ aio_co_enter(s->ctx, s->co); ++ AIO_WAIT_WHILE(s->ctx, s->in_progress); ++} ++ ++#endif /* BLOCK_BLOCK_GEN_H */ +diff --git a/qcow2/lib/block/commit.c b/qcow2/lib/block/commit.c +new file mode 100644 +index 00000000..7c3fdcb0 +--- /dev/null ++++ b/qcow2/lib/block/commit.c +@@ -0,0 +1,600 @@ ++/* ++ * Live block commit ++ * ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Authors: ++ * Jeff Cody ++ * Based on stream.c by Stefan Hajnoczi ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "trace.h" ++#include "block/block_int.h" ++#include "block/blockjob_int.h" ++#include "qapi/error.h" ++#include "qemu/ratelimit.h" ++#include "qemu/memalign.h" ++#include "sysemu/block-backend.h" ++ ++enum { ++ /* ++ * Size of data buffer for populating the image file. This should be large ++ * enough to process multiple clusters in a single call, so that populating ++ * contiguous regions of the image is efficient. ++ */ ++ COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ ++}; ++ ++typedef struct CommitBlockJob { ++ BlockJob common; ++ BlockDriverState *commit_top_bs; ++ BlockBackend *top; ++ BlockBackend *base; ++ BlockDriverState *base_bs; ++ BlockDriverState *base_overlay; ++ BlockdevOnError on_error; ++ bool base_read_only; ++ bool chain_frozen; ++ char *backing_file_str; ++ bool backing_mask_protocol; ++} CommitBlockJob; ++ ++static int commit_prepare(Job *job) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); ++ s->chain_frozen = false; ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before ++ * the normal backing chain can be restored. */ ++ blk_unref(s->base); ++ s->base = NULL; ++ ++ /* FIXME: bdrv_drop_intermediate treats total failures and partial failures ++ * identically. Further work is needed to disambiguate these cases. */ ++ return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs, ++ s->backing_file_str, ++ s->backing_mask_protocol); ++} ++ ++static void commit_abort(Job *job) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ BlockDriverState *top_bs = blk_bs(s->top); ++ BlockDriverState *commit_top_backing_bs; ++ ++ if (s->chain_frozen) { ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); ++ bdrv_graph_rdunlock_main_loop(); ++ } ++ ++ /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ ++ bdrv_ref(top_bs); ++ bdrv_ref(s->commit_top_bs); ++ ++ if (s->base) { ++ blk_unref(s->base); ++ } ++ ++ /* free the blockers on the intermediate nodes so that bdrv_replace_nodes ++ * can succeed */ ++ block_job_remove_all_bdrv(&s->common); ++ ++ /* If bdrv_drop_intermediate() failed (or was not invoked), remove the ++ * commit filter driver from the backing chain now. Do this as the final ++ * step so that the 'consistent read' permission can be granted. ++ * ++ * XXX Can (or should) we somehow keep 'consistent read' blocked even ++ * after the failed/cancelled commit job is gone? If we already wrote ++ * something to base, the intermediate images aren't valid any more. */ ++ bdrv_graph_rdlock_main_loop(); ++ commit_top_backing_bs = s->commit_top_bs->backing->bs; ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_drained_begin(commit_top_backing_bs); ++ bdrv_graph_wrlock(); ++ bdrv_replace_node(s->commit_top_bs, commit_top_backing_bs, &error_abort); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(commit_top_backing_bs); ++ ++ bdrv_unref(s->commit_top_bs); ++ bdrv_unref(top_bs); ++} ++ ++static void commit_clean(Job *job) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ ++ /* restore base open flags here if appropriate (e.g., change the base back ++ * to r/o). These reopens do not need to be atomic, since we won't abort ++ * even on failure here */ ++ if (s->base_read_only) { ++ bdrv_reopen_set_read_only(s->base_bs, true, NULL); ++ } ++ ++ g_free(s->backing_file_str); ++ blk_unref(s->top); ++} ++ ++static int coroutine_fn commit_run(Job *job, Error **errp) ++{ ++ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); ++ int64_t offset; ++ int ret = 0; ++ int64_t n = 0; /* bytes */ ++ QEMU_AUTO_VFREE void *buf = NULL; ++ int64_t len, base_len; ++ ++ len = blk_co_getlength(s->top); ++ if (len < 0) { ++ return len; ++ } ++ job_progress_set_remaining(&s->common.job, len); ++ ++ base_len = blk_co_getlength(s->base); ++ if (base_len < 0) { ++ return base_len; ++ } ++ ++ if (base_len < len) { ++ ret = blk_co_truncate(s->base, len, false, PREALLOC_MODE_OFF, 0, NULL); ++ if (ret) { ++ return ret; ++ } ++ } ++ ++ buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); ++ ++ for (offset = 0; offset < len; offset += n) { ++ bool copy; ++ bool error_in_source = true; ++ ++ /* Note that even when no rate limit is applied we need to yield ++ * with no pending I/O here so that bdrv_drain_all() returns. ++ */ ++ block_job_ratelimit_sleep(&s->common); ++ if (job_is_cancelled(&s->common.job)) { ++ break; ++ } ++ /* Copy if allocated above the base */ ++ ret = blk_co_is_allocated_above(s->top, s->base_overlay, true, ++ offset, COMMIT_BUFFER_SIZE, &n); ++ copy = (ret > 0); ++ trace_commit_one_iteration(s, offset, n, ret); ++ if (copy) { ++ assert(n < SIZE_MAX); ++ ++ ret = blk_co_pread(s->top, offset, n, buf, 0); ++ if (ret >= 0) { ++ ret = blk_co_pwrite(s->base, offset, n, buf, 0); ++ if (ret < 0) { ++ error_in_source = false; ++ } ++ } ++ } ++ if (ret < 0) { ++ BlockErrorAction action = ++ block_job_error_action(&s->common, s->on_error, ++ error_in_source, -ret); ++ if (action == BLOCK_ERROR_ACTION_REPORT) { ++ return ret; ++ } else { ++ n = 0; ++ continue; ++ } ++ } ++ /* Publish progress */ ++ job_progress_update(&s->common.job, n); ++ ++ if (copy) { ++ block_job_ratelimit_processed_bytes(&s->common, n); ++ } ++ } ++ ++ return 0; ++} ++ ++static const BlockJobDriver commit_job_driver = { ++ .job_driver = { ++ .instance_size = sizeof(CommitBlockJob), ++ .job_type = JOB_TYPE_COMMIT, ++ .free = block_job_free, ++ .user_resume = block_job_user_resume, ++ .run = commit_run, ++ .prepare = commit_prepare, ++ .abort = commit_abort, ++ .clean = commit_clean ++ }, ++}; ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_commit_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); ++} ++ ++static GRAPH_RDLOCK void bdrv_commit_top_refresh_filename(BlockDriverState *bs) ++{ ++ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), ++ bs->backing->bs->filename); ++} ++ ++static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ *nperm = 0; ++ *nshared = BLK_PERM_ALL; ++} ++ ++/* Dummy node that provides consistent read to its users without requiring it ++ * from its backing file and that allows writes on the backing file chain. */ ++static BlockDriver bdrv_commit_top = { ++ .format_name = "commit_top", ++ .bdrv_co_preadv = bdrv_commit_top_preadv, ++ .bdrv_refresh_filename = bdrv_commit_top_refresh_filename, ++ .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, ++ BlockDriverState *base, BlockDriverState *top, ++ int creation_flags, int64_t speed, ++ BlockdevOnError on_error, const char *backing_file_str, ++ bool backing_mask_protocol, ++ const char *filter_node_name, Error **errp) ++{ ++ CommitBlockJob *s; ++ BlockDriverState *iter; ++ BlockDriverState *commit_top_bs = NULL; ++ BlockDriverState *filtered_base; ++ int64_t base_size, top_size; ++ uint64_t base_perms, iter_shared_perms; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ assert(top != bs); ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_skip_filters(top) == bdrv_skip_filters(base)) { ++ error_setg(errp, "Invalid files for merge: top and base are the same"); ++ bdrv_graph_rdunlock_main_loop(); ++ return; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ base_size = bdrv_getlength(base); ++ if (base_size < 0) { ++ error_setg_errno(errp, -base_size, "Could not inquire base image size"); ++ return; ++ } ++ ++ top_size = bdrv_getlength(top); ++ if (top_size < 0) { ++ error_setg_errno(errp, -top_size, "Could not inquire top image size"); ++ return; ++ } ++ ++ base_perms = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE; ++ if (base_size < top_size) { ++ base_perms |= BLK_PERM_RESIZE; ++ } ++ ++ s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, ++ speed, creation_flags, NULL, NULL, errp); ++ if (!s) { ++ return; ++ } ++ ++ /* convert base to r/w, if necessary */ ++ s->base_read_only = bdrv_is_read_only(base); ++ if (s->base_read_only) { ++ if (bdrv_reopen_set_read_only(base, false, errp) != 0) { ++ goto fail; ++ } ++ } ++ ++ /* Insert commit_top block node above top, so we can block consistent read ++ * on the backing chain below it */ ++ commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, filter_node_name, 0, ++ errp); ++ if (commit_top_bs == NULL) { ++ goto fail; ++ } ++ if (!filter_node_name) { ++ commit_top_bs->implicit = true; ++ } ++ ++ /* So that we can always drop this node */ ++ commit_top_bs->never_freeze = true; ++ ++ commit_top_bs->total_sectors = top->total_sectors; ++ ++ ret = bdrv_append(commit_top_bs, top, errp); ++ bdrv_unref(commit_top_bs); /* referenced by new parents or failed */ ++ if (ret < 0) { ++ commit_top_bs = NULL; ++ goto fail; ++ } ++ ++ s->commit_top_bs = commit_top_bs; ++ ++ /* ++ * Block all nodes between top and base, because they will ++ * disappear from the chain after this operation. ++ * Note that this assumes that the user is fine with removing all ++ * nodes (including R/W filters) between top and base. Assuring ++ * this is the responsibility of the interface (i.e. whoever calls ++ * commit_start()). ++ */ ++ bdrv_graph_wrlock(); ++ s->base_overlay = bdrv_find_overlay(top, base); ++ assert(s->base_overlay); ++ ++ /* ++ * The topmost node with ++ * bdrv_skip_filters(filtered_base) == bdrv_skip_filters(base) ++ */ ++ filtered_base = bdrv_cow_bs(s->base_overlay); ++ assert(bdrv_skip_filters(filtered_base) == bdrv_skip_filters(base)); ++ ++ /* ++ * XXX BLK_PERM_WRITE needs to be allowed so we don't block ourselves ++ * at s->base (if writes are blocked for a node, they are also blocked ++ * for its backing file). The other options would be a second filter ++ * driver above s->base. ++ */ ++ iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE; ++ ++ for (iter = top; iter != base; iter = bdrv_filter_or_cow_bs(iter)) { ++ if (iter == filtered_base) { ++ /* ++ * From here on, all nodes are filters on the base. This ++ * allows us to share BLK_PERM_CONSISTENT_READ. ++ */ ++ iter_shared_perms |= BLK_PERM_CONSISTENT_READ; ++ } ++ ++ ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, ++ iter_shared_perms, errp); ++ if (ret < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ } ++ ++ if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ s->chain_frozen = true; ++ ++ ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); ++ bdrv_graph_wrunlock(); ++ ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->base = blk_new(s->common.job.aio_context, ++ base_perms, ++ BLK_PERM_CONSISTENT_READ ++ | BLK_PERM_WRITE_UNCHANGED); ++ ret = blk_insert_bs(s->base, base, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ blk_set_disable_request_queuing(s->base, true); ++ s->base_bs = base; ++ ++ /* Required permissions are already taken with block_job_add_bdrv() */ ++ s->top = blk_new(s->common.job.aio_context, 0, BLK_PERM_ALL); ++ ret = blk_insert_bs(s->top, top, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ blk_set_disable_request_queuing(s->top, true); ++ ++ s->backing_file_str = g_strdup(backing_file_str); ++ s->backing_mask_protocol = backing_mask_protocol; ++ s->on_error = on_error; ++ ++ trace_commit_start(bs, base, top, s); ++ job_start(&s->common.job); ++ return; ++ ++fail: ++ if (s->chain_frozen) { ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_unfreeze_backing_chain(commit_top_bs, base); ++ bdrv_graph_rdunlock_main_loop(); ++ } ++ if (s->base) { ++ blk_unref(s->base); ++ } ++ if (s->top) { ++ blk_unref(s->top); ++ } ++ if (s->base_read_only) { ++ bdrv_reopen_set_read_only(base, true, NULL); ++ } ++ job_early_fail(&s->common.job); ++ /* commit_top_bs has to be replaced after deleting the block job, ++ * otherwise this would fail because of lack of permissions. */ ++ if (commit_top_bs) { ++ bdrv_drained_begin(top); ++ bdrv_graph_wrlock(); ++ bdrv_replace_node(commit_top_bs, top, &error_abort); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(top); ++ } ++} ++ ++ ++#define COMMIT_BUF_SIZE (2048 * BDRV_SECTOR_SIZE) ++ ++/* commit COW file into the raw image */ ++int bdrv_commit(BlockDriverState *bs) ++{ ++ BlockBackend *src, *backing; ++ BlockDriverState *backing_file_bs = NULL; ++ BlockDriverState *commit_top_bs = NULL; ++ BlockDriver *drv = bs->drv; ++ AioContext *ctx; ++ int64_t offset, length, backing_length; ++ int ro; ++ int64_t n; ++ int ret = 0; ++ QEMU_AUTO_VFREE uint8_t *buf = NULL; ++ Error *local_err = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!drv) ++ return -ENOMEDIUM; ++ ++ backing_file_bs = bdrv_cow_bs(bs); ++ ++ if (!backing_file_bs) { ++ return -ENOTSUP; ++ } ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || ++ bdrv_op_is_blocked(backing_file_bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) ++ { ++ return -EBUSY; ++ } ++ ++ ro = bdrv_is_read_only(backing_file_bs); ++ ++ if (ro) { ++ if (bdrv_reopen_set_read_only(backing_file_bs, false, NULL)) { ++ return -EACCES; ++ } ++ } ++ ++ ctx = bdrv_get_aio_context(bs); ++ /* WRITE_UNCHANGED is required for bdrv_make_empty() */ ++ src = blk_new(ctx, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED, ++ BLK_PERM_ALL); ++ backing = blk_new(ctx, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); ++ ++ ret = blk_insert_bs(src, bs, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto ro_cleanup; ++ } ++ ++ /* Insert commit_top block node above backing, so we can write to it */ ++ commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, BDRV_O_RDWR, ++ &local_err); ++ if (commit_top_bs == NULL) { ++ error_report_err(local_err); ++ goto ro_cleanup; ++ } ++ ++ bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort); ++ bdrv_set_backing_hd(bs, commit_top_bs, &error_abort); ++ ++ ret = blk_insert_bs(backing, backing_file_bs, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto ro_cleanup; ++ } ++ ++ length = blk_getlength(src); ++ if (length < 0) { ++ ret = length; ++ goto ro_cleanup; ++ } ++ ++ backing_length = blk_getlength(backing); ++ if (backing_length < 0) { ++ ret = backing_length; ++ goto ro_cleanup; ++ } ++ ++ /* If our top snapshot is larger than the backing file image, ++ * grow the backing file image if possible. If not possible, ++ * we must return an error */ ++ if (length > backing_length) { ++ ret = blk_truncate(backing, length, false, PREALLOC_MODE_OFF, 0, ++ &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto ro_cleanup; ++ } ++ } ++ ++ /* blk_try_blockalign() for src will choose an alignment that works for ++ * backing as well, so no need to compare the alignment manually. */ ++ buf = blk_try_blockalign(src, COMMIT_BUF_SIZE); ++ if (buf == NULL) { ++ ret = -ENOMEM; ++ goto ro_cleanup; ++ } ++ ++ for (offset = 0; offset < length; offset += n) { ++ ret = bdrv_is_allocated(bs, offset, COMMIT_BUF_SIZE, &n); ++ if (ret < 0) { ++ goto ro_cleanup; ++ } ++ if (ret) { ++ ret = blk_pread(src, offset, n, buf, 0); ++ if (ret < 0) { ++ goto ro_cleanup; ++ } ++ ++ ret = blk_pwrite(backing, offset, n, buf, 0); ++ if (ret < 0) { ++ goto ro_cleanup; ++ } ++ } ++ } ++ ++ ret = blk_make_empty(src, NULL); ++ /* Ignore -ENOTSUP */ ++ if (ret < 0 && ret != -ENOTSUP) { ++ goto ro_cleanup; ++ } ++ ++ blk_flush(src); ++ ++ /* ++ * Make sure all data we wrote to the backing device is actually ++ * stable on disk. ++ */ ++ blk_flush(backing); ++ ++ ret = 0; ++ro_cleanup: ++ blk_unref(backing); ++ if (bdrv_cow_bs(bs) != backing_file_bs) { ++ bdrv_set_backing_hd(bs, backing_file_bs, &error_abort); ++ } ++ bdrv_unref(commit_top_bs); ++ blk_unref(src); ++ ++ if (ro) { ++ /* ignoring error return here */ ++ bdrv_reopen_set_read_only(backing_file_bs, true, NULL); ++ } ++ ++ return ret; ++} +diff --git a/qcow2/lib/block/coroutines.h b/qcow2/lib/block/coroutines.h +new file mode 100644 +index 00000000..f3226682 +--- /dev/null ++++ b/qcow2/lib/block/coroutines.h +@@ -0,0 +1,92 @@ ++/* ++ * Block layer I/O functions ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#ifndef BLOCK_COROUTINES_H ++#define BLOCK_COROUTINES_H ++ ++#include "block/block_int.h" ++ ++/* For blk_bs() in generated block/block-gen.c */ ++#include "sysemu/block-backend.h" ++ ++/* ++ * I/O API functions. These functions are thread-safe. ++ * ++ * See include/block/block-io.h for more information about ++ * the I/O API. ++ */ ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_common_block_status_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ bool include_base, ++ bool want_zero, ++ int64_t offset, ++ int64_t bytes, ++ int64_t *pnum, ++ int64_t *map, ++ BlockDriverState **file, ++ int *depth); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++int coroutine_fn GRAPH_RDLOCK ++bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); ++ ++int coroutine_fn GRAPH_RDLOCK ++nbd_co_do_establish_connection(BlockDriverState *bs, bool blocking, ++ Error **errp); ++ ++ ++/* ++ * "I/O or GS" API functions. These functions can run without ++ * the BQL, but only in one specific iothread/main loop. ++ * ++ * See include/block/block-io.h for more information about ++ * the "I/O or GS" API. ++ */ ++ ++int co_wrapper_mixed_bdrv_rdlock ++bdrv_common_block_status_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ bool include_base, ++ bool want_zero, ++ int64_t offset, ++ int64_t bytes, ++ int64_t *pnum, ++ int64_t *map, ++ BlockDriverState **file, ++ int *depth); ++ ++int co_wrapper_mixed_bdrv_rdlock ++nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **errp); ++ ++#endif /* BLOCK_COROUTINES_H */ +diff --git a/qcow2/lib/block/dirty-bitmap.c b/qcow2/lib/block/dirty-bitmap.c +new file mode 100644 +index 00000000..13a19797 +--- /dev/null ++++ b/qcow2/lib/block/dirty-bitmap.c +@@ -0,0 +1,890 @@ ++/* ++ * Block Dirty Bitmap ++ * ++ * Copyright (c) 2016-2017 Red Hat. Inc ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "trace.h" ++#include "block/block-io.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "block/dirty-bitmap.h" ++#include "qemu/main-loop.h" ++ ++struct BdrvDirtyBitmap { ++ BlockDriverState *bs; ++ HBitmap *bitmap; /* Dirty bitmap implementation */ ++ bool busy; /* Bitmap is busy, it can't be used via QMP */ ++ BdrvDirtyBitmap *successor; /* Anonymous child, if any. */ ++ char *name; /* Optional non-empty unique ID */ ++ int64_t size; /* Size of the bitmap, in bytes */ ++ bool disabled; /* Bitmap is disabled. It ignores all writes to ++ the device */ ++ int active_iterators; /* How many iterators are active */ ++ bool readonly; /* Bitmap is read-only. This field also ++ prevents the respective image from being ++ modified (i.e. blocks writes and discards). ++ Such operations must fail and both the image ++ and this bitmap must remain unchanged while ++ this flag is set. */ ++ bool persistent; /* bitmap must be saved to owner disk image */ ++ bool inconsistent; /* bitmap is persistent, but inconsistent. ++ It cannot be used at all in any way, except ++ a QMP user can remove it. */ ++ bool skip_store; /* We are either migrating or deleting this ++ * bitmap; it should not be stored on the next ++ * inactivation. */ ++ QLIST_ENTRY(BdrvDirtyBitmap) list; ++}; ++ ++struct BdrvDirtyBitmapIter { ++ HBitmapIter hbi; ++ BdrvDirtyBitmap *bitmap; ++}; ++ ++static inline void bdrv_dirty_bitmaps_lock(BlockDriverState *bs) ++{ ++ qemu_mutex_lock(&bs->dirty_bitmap_mutex); ++} ++ ++static inline void bdrv_dirty_bitmaps_unlock(BlockDriverState *bs) ++{ ++ qemu_mutex_unlock(&bs->dirty_bitmap_mutex); ++} ++ ++void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++} ++ ++void bdrv_dirty_bitmap_unlock(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called with BQL or dirty_bitmap lock taken. */ ++BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) ++{ ++ BdrvDirtyBitmap *bm; ++ ++ assert(name); ++ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { ++ if (bm->name && !strcmp(name, bm->name)) { ++ return bm; ++ } ++ } ++ return NULL; ++} ++ ++/* Called with BQL taken. */ ++BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, ++ uint32_t granularity, ++ const char *name, ++ Error **errp) ++{ ++ int64_t bitmap_size; ++ BdrvDirtyBitmap *bitmap; ++ ++ assert(is_power_of_2(granularity) && granularity >= BDRV_SECTOR_SIZE); ++ ++ if (name) { ++ if (bdrv_find_dirty_bitmap(bs, name)) { ++ error_setg(errp, "Bitmap already exists: %s", name); ++ return NULL; ++ } ++ if (strlen(name) > BDRV_BITMAP_MAX_NAME_SIZE) { ++ error_setg(errp, "Bitmap name too long: %s", name); ++ return NULL; ++ } ++ } ++ bitmap_size = bdrv_getlength(bs); ++ if (bitmap_size < 0) { ++ error_setg_errno(errp, -bitmap_size, "could not get length of device"); ++ errno = -bitmap_size; ++ return NULL; ++ } ++ bitmap = g_new0(BdrvDirtyBitmap, 1); ++ bitmap->bs = bs; ++ bitmap->bitmap = hbitmap_alloc(bitmap_size, ctz32(granularity)); ++ bitmap->size = bitmap_size; ++ bitmap->name = g_strdup(name); ++ bitmap->disabled = false; ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_INSERT_HEAD(&bs->dirty_bitmaps, bitmap, list); ++ bdrv_dirty_bitmaps_unlock(bs); ++ return bitmap; ++} ++ ++int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->size; ++} ++ ++const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->name; ++} ++ ++/* Called with BQL taken. */ ++bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->successor; ++} ++ ++static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->busy; ++} ++ ++void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->busy = busy; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called with BQL taken. */ ++bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) ++{ ++ return !bitmap->disabled; ++} ++ ++/* Called with BQL taken. */ ++static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) ++{ ++ return !bitmap->disabled || (bitmap->successor && ++ !bitmap->successor->disabled); ++} ++ ++int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, ++ Error **errp) ++{ ++ if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) { ++ error_setg(errp, "Bitmap '%s' is currently in use by another" ++ " operation and cannot be used", bitmap->name); ++ return -1; ++ } ++ ++ if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) { ++ error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", ++ bitmap->name); ++ return -1; ++ } ++ ++ if ((flags & BDRV_BITMAP_INCONSISTENT) && ++ bdrv_dirty_bitmap_inconsistent(bitmap)) { ++ error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used", ++ bitmap->name); ++ error_append_hint(errp, "Try block-dirty-bitmap-remove to delete" ++ " this bitmap from disk\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Create a successor bitmap destined to replace this bitmap after an operation. ++ * Requires that the bitmap is not marked busy and has no successor. ++ * The successor will be enabled if the parent bitmap was. ++ * Called with BQL taken. ++ */ ++int bdrv_dirty_bitmap_create_successor(BdrvDirtyBitmap *bitmap, Error **errp) ++{ ++ uint64_t granularity; ++ BdrvDirtyBitmap *child; ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { ++ return -1; ++ } ++ if (bdrv_dirty_bitmap_has_successor(bitmap)) { ++ error_setg(errp, "Cannot create a successor for a bitmap that already " ++ "has one"); ++ return -1; ++ } ++ ++ /* Create an anonymous successor */ ++ granularity = bdrv_dirty_bitmap_granularity(bitmap); ++ child = bdrv_create_dirty_bitmap(bitmap->bs, granularity, NULL, errp); ++ if (!child) { ++ return -1; ++ } ++ ++ /* Successor will be on or off based on our current state. */ ++ child->disabled = bitmap->disabled; ++ bitmap->disabled = true; ++ ++ /* Install the successor and mark the parent as busy */ ++ bitmap->successor = child; ++ bitmap->busy = true; ++ return 0; ++} ++ ++void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) ++{ ++ bitmap->disabled = false; ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) ++{ ++ assert(bitmap->bs == bitmap->successor->bs); ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bdrv_enable_dirty_bitmap_locked(bitmap->successor); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */ ++static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) ++{ ++ assert(!bitmap->active_iterators); ++ assert(!bdrv_dirty_bitmap_busy(bitmap)); ++ assert(!bdrv_dirty_bitmap_has_successor(bitmap)); ++ QLIST_REMOVE(bitmap, list); ++ hbitmap_free(bitmap->bitmap); ++ g_free(bitmap->name); ++ g_free(bitmap); ++} ++ ++/** ++ * For a bitmap with a successor, yield our name to the successor, ++ * delete the old bitmap, and return a handle to the new bitmap. ++ * Called with BQL taken. ++ */ ++BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BdrvDirtyBitmap *bitmap, ++ Error **errp) ++{ ++ char *name; ++ BdrvDirtyBitmap *successor = bitmap->successor; ++ ++ if (successor == NULL) { ++ error_setg(errp, "Cannot relinquish control if " ++ "there's no successor present"); ++ return NULL; ++ } ++ ++ name = bitmap->name; ++ bitmap->name = NULL; ++ successor->name = name; ++ bitmap->successor = NULL; ++ successor->persistent = bitmap->persistent; ++ bitmap->persistent = false; ++ bitmap->busy = false; ++ bdrv_release_dirty_bitmap(bitmap); ++ ++ return successor; ++} ++ ++/** ++ * In cases of failure where we can no longer safely delete the parent, ++ * we may wish to re-join the parent and child/successor. ++ * The merged parent will be marked as not busy. ++ * The marged parent will be enabled if and only if the successor was enabled. ++ * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. ++ */ ++BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *parent, ++ Error **errp) ++{ ++ BdrvDirtyBitmap *successor = parent->successor; ++ ++ if (!successor) { ++ error_setg(errp, "Cannot reclaim a successor when none is present"); ++ return NULL; ++ } ++ ++ hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap); ++ ++ parent->disabled = successor->disabled; ++ parent->busy = false; ++ bdrv_release_dirty_bitmap_locked(successor); ++ parent->successor = NULL; ++ ++ return parent; ++} ++ ++/* Called with BQL taken. */ ++BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BdrvDirtyBitmap *parent, ++ Error **errp) ++{ ++ BdrvDirtyBitmap *ret; ++ ++ bdrv_dirty_bitmaps_lock(parent->bs); ++ ret = bdrv_reclaim_dirty_bitmap_locked(parent, errp); ++ bdrv_dirty_bitmaps_unlock(parent->bs); ++ ++ return ret; ++} ++ ++/** ++ * Truncates _all_ bitmaps attached to a BDS. ++ * Called with BQL taken. ++ */ ++void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) ++{ ++ BdrvDirtyBitmap *bitmap; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { ++ assert(!bdrv_dirty_bitmap_busy(bitmap)); ++ assert(!bdrv_dirty_bitmap_has_successor(bitmap)); ++ assert(!bitmap->active_iterators); ++ hbitmap_truncate(bitmap->bitmap, bytes); ++ bitmap->size = bytes; ++ } ++ bdrv_dirty_bitmaps_unlock(bs); ++} ++ ++/* Called with BQL taken. */ ++void bdrv_release_dirty_bitmap(BdrvDirtyBitmap *bitmap) ++{ ++ BlockDriverState *bs = bitmap->bs; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ bdrv_release_dirty_bitmap_locked(bitmap); ++ bdrv_dirty_bitmaps_unlock(bs); ++} ++ ++/** ++ * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). ++ * There must not be any busy bitmaps attached. ++ * This function does not remove persistent bitmaps from the storage. ++ * Called with BQL taken. ++ */ ++void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) ++{ ++ BdrvDirtyBitmap *bm, *next; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { ++ if (bdrv_dirty_bitmap_name(bm)) { ++ bdrv_release_dirty_bitmap_locked(bm); ++ } ++ } ++ bdrv_dirty_bitmaps_unlock(bs); ++} ++ ++/** ++ * Remove persistent dirty bitmap from the storage if it exists. ++ * Absence of bitmap is not an error, because we have the following scenario: ++ * BdrvDirtyBitmap can have .persistent = true but not yet saved and have no ++ * stored version. For such bitmap bdrv_remove_persistent_dirty_bitmap() should ++ * not fail. ++ * This function doesn't release corresponding BdrvDirtyBitmap. ++ */ ++int coroutine_fn ++bdrv_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, ++ Error **errp) ++{ ++ assert_bdrv_graph_readable(); ++ if (bs->drv && bs->drv->bdrv_co_remove_persistent_dirty_bitmap) { ++ return bs->drv->bdrv_co_remove_persistent_dirty_bitmap(bs, name, errp); ++ } ++ ++ return 0; ++} ++ ++bool ++bdrv_supports_persistent_dirty_bitmap(BlockDriverState *bs) ++{ ++ if (bs->drv && bs->drv->bdrv_supports_persistent_dirty_bitmap) { ++ return bs->drv->bdrv_supports_persistent_dirty_bitmap(bs); ++ } ++ return false; ++} ++ ++bool coroutine_fn ++bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ error_setg_errno(errp, ENOMEDIUM, ++ "Can't store persistent bitmaps to %s", ++ bdrv_get_device_or_node_name(bs)); ++ return false; ++ } ++ ++ if (!drv->bdrv_co_can_store_new_dirty_bitmap) { ++ error_setg_errno(errp, ENOTSUP, ++ "Can't store persistent bitmaps to %s", ++ bdrv_get_device_or_node_name(bs)); ++ return false; ++ } ++ ++ return drv->bdrv_co_can_store_new_dirty_bitmap(bs, name, granularity, errp); ++} ++ ++void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->disabled = true; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bdrv_enable_dirty_bitmap_locked(bitmap); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) ++{ ++ BdrvDirtyBitmap *bm; ++ BlockDirtyInfoList *list = NULL; ++ BlockDirtyInfoList **tail = &list; ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { ++ BlockDirtyInfo *info = g_new0(BlockDirtyInfo, 1); ++ ++ info->count = bdrv_get_dirty_count(bm); ++ info->granularity = bdrv_dirty_bitmap_granularity(bm); ++ info->name = g_strdup(bm->name); ++ info->recording = bdrv_dirty_bitmap_recording(bm); ++ info->busy = bdrv_dirty_bitmap_busy(bm); ++ info->persistent = bm->persistent; ++ info->has_inconsistent = bm->inconsistent; ++ info->inconsistent = bm->inconsistent; ++ QAPI_LIST_APPEND(tail, info); ++ } ++ bdrv_dirty_bitmaps_unlock(bs); ++ ++ return list; ++} ++ ++/* Called within bdrv_dirty_bitmap_lock..unlock */ ++bool bdrv_dirty_bitmap_get_locked(BdrvDirtyBitmap *bitmap, int64_t offset) ++{ ++ return hbitmap_get(bitmap->bitmap, offset); ++} ++ ++bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset) ++{ ++ bool ret; ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ ret = bdrv_dirty_bitmap_get_locked(bitmap, offset); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++ ++ return ret; ++} ++ ++/** ++ * Chooses a default granularity based on the existing cluster size, ++ * but clamped between [4K, 64K]. Defaults to 64K in the case that there ++ * is no cluster size information available. ++ */ ++uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs) ++{ ++ BlockDriverInfo bdi; ++ uint32_t granularity; ++ ++ if (bdrv_get_info(bs, &bdi) >= 0 && bdi.cluster_size > 0) { ++ granularity = MAX(4096, bdi.cluster_size); ++ granularity = MIN(65536, granularity); ++ } else { ++ granularity = 65536; ++ } ++ ++ return granularity; ++} ++ ++uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap) ++{ ++ return 1U << hbitmap_granularity(bitmap->bitmap); ++} ++ ++BdrvDirtyBitmapIter *bdrv_dirty_iter_new(BdrvDirtyBitmap *bitmap) ++{ ++ BdrvDirtyBitmapIter *iter = g_new(BdrvDirtyBitmapIter, 1); ++ hbitmap_iter_init(&iter->hbi, bitmap->bitmap, 0); ++ iter->bitmap = bitmap; ++ bitmap->active_iterators++; ++ return iter; ++} ++ ++void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) ++{ ++ if (!iter) { ++ return; ++ } ++ assert(iter->bitmap->active_iterators > 0); ++ iter->bitmap->active_iterators--; ++ g_free(iter); ++} ++ ++int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) ++{ ++ return hbitmap_iter_next(&iter->hbi); ++} ++ ++/* Called within bdrv_dirty_bitmap_lock..unlock */ ++void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes) ++{ ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ hbitmap_set(bitmap->bitmap, offset, bytes); ++} ++ ++void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bdrv_set_dirty_bitmap_locked(bitmap, offset, bytes); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called within bdrv_dirty_bitmap_lock..unlock */ ++void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes) ++{ ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ hbitmap_reset(bitmap->bitmap, offset, bytes); ++} ++ ++void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, ++ int64_t offset, int64_t bytes) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bdrv_reset_dirty_bitmap_locked(bitmap, offset, bytes); ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) ++{ ++ IO_CODE(); ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ if (!out) { ++ hbitmap_reset_all(bitmap->bitmap); ++ } else { ++ HBitmap *backup = bitmap->bitmap; ++ bitmap->bitmap = hbitmap_alloc(bitmap->size, ++ hbitmap_granularity(backup)); ++ *out = backup; ++ } ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) ++{ ++ HBitmap *tmp = bitmap->bitmap; ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ GLOBAL_STATE_CODE(); ++ bitmap->bitmap = backup; ++ hbitmap_free(tmp); ++} ++ ++uint64_t bdrv_dirty_bitmap_serialization_size(const BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes) ++{ ++ return hbitmap_serialization_size(bitmap->bitmap, offset, bytes); ++} ++ ++uint64_t bdrv_dirty_bitmap_serialization_align(const BdrvDirtyBitmap *bitmap) ++{ ++ return hbitmap_serialization_align(bitmap->bitmap); ++} ++ ++/* Return the disk size covered by a chunk of serialized bitmap data. */ ++uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size, ++ const BdrvDirtyBitmap *bitmap) ++{ ++ uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); ++ uint64_t limit = granularity * (serialized_chunk_size << 3); ++ ++ assert(QEMU_IS_ALIGNED(limit, ++ bdrv_dirty_bitmap_serialization_align(bitmap))); ++ return limit; ++} ++ ++ ++void bdrv_dirty_bitmap_serialize_part(const BdrvDirtyBitmap *bitmap, ++ uint8_t *buf, uint64_t offset, ++ uint64_t bytes) ++{ ++ hbitmap_serialize_part(bitmap->bitmap, buf, offset, bytes); ++} ++ ++void bdrv_dirty_bitmap_deserialize_part(BdrvDirtyBitmap *bitmap, ++ uint8_t *buf, uint64_t offset, ++ uint64_t bytes, bool finish) ++{ ++ hbitmap_deserialize_part(bitmap->bitmap, buf, offset, bytes, finish); ++} ++ ++void bdrv_dirty_bitmap_deserialize_zeroes(BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes, ++ bool finish) ++{ ++ hbitmap_deserialize_zeroes(bitmap->bitmap, offset, bytes, finish); ++} ++ ++void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, ++ uint64_t offset, uint64_t bytes, ++ bool finish) ++{ ++ hbitmap_deserialize_ones(bitmap->bitmap, offset, bytes, finish); ++} ++ ++void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap) ++{ ++ hbitmap_deserialize_finish(bitmap->bitmap); ++} ++ ++void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BdrvDirtyBitmap *bitmap; ++ IO_CODE(); ++ ++ if (QLIST_EMPTY(&bs->dirty_bitmaps)) { ++ return; ++ } ++ ++ bdrv_dirty_bitmaps_lock(bs); ++ QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { ++ if (!bdrv_dirty_bitmap_enabled(bitmap)) { ++ continue; ++ } ++ assert(!bdrv_dirty_bitmap_readonly(bitmap)); ++ hbitmap_set(bitmap->bitmap, offset, bytes); ++ } ++ bdrv_dirty_bitmaps_unlock(bs); ++} ++ ++/** ++ * Advance a BdrvDirtyBitmapIter to an arbitrary offset. ++ */ ++void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *iter, int64_t offset) ++{ ++ hbitmap_iter_init(&iter->hbi, iter->hbi.hb, offset); ++} ++ ++int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap) ++{ ++ return hbitmap_count(bitmap->bitmap); ++} ++ ++bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->readonly; ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->readonly = value; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) ++{ ++ BdrvDirtyBitmap *bm; ++ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { ++ if (bm->readonly) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++bool bdrv_has_named_bitmaps(BlockDriverState *bs) ++{ ++ BdrvDirtyBitmap *bm; ++ ++ QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { ++ if (bdrv_dirty_bitmap_name(bm)) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->persistent = persistent; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ assert(bitmap->persistent == true); ++ bitmap->inconsistent = true; ++ bitmap->disabled = true; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++/* Called with BQL taken. */ ++void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip) ++{ ++ bdrv_dirty_bitmaps_lock(bitmap->bs); ++ bitmap->skip_store = skip; ++ bdrv_dirty_bitmaps_unlock(bitmap->bs); ++} ++ ++bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->persistent && !bitmap->skip_store; ++} ++ ++bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap) ++{ ++ return bitmap->inconsistent; ++} ++ ++BdrvDirtyBitmap *bdrv_dirty_bitmap_first(BlockDriverState *bs) ++{ ++ return QLIST_FIRST(&bs->dirty_bitmaps); ++} ++ ++BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap) ++{ ++ return QLIST_NEXT(bitmap, list); ++} ++ ++char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) ++{ ++ return hbitmap_sha256(bitmap->bitmap, errp); ++} ++ ++int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes) ++{ ++ return hbitmap_next_dirty(bitmap->bitmap, offset, bytes); ++} ++ ++int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes) ++{ ++ return hbitmap_next_zero(bitmap->bitmap, offset, bytes); ++} ++ ++bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, ++ int64_t start, int64_t end, int64_t max_dirty_count, ++ int64_t *dirty_start, int64_t *dirty_count) ++{ ++ return hbitmap_next_dirty_area(bitmap->bitmap, start, end, max_dirty_count, ++ dirty_start, dirty_count); ++} ++ ++bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset, ++ int64_t bytes, int64_t *count) ++{ ++ return hbitmap_status(bitmap->bitmap, offset, bytes, count); ++} ++ ++/** ++ * bdrv_merge_dirty_bitmap: merge src into dest. ++ * Ensures permissions on bitmaps are reasonable; use for public API. ++ * ++ * @backup: If provided, make a copy of dest here prior to merge. ++ * ++ * Returns true on success, false on failure. In case of failure bitmaps are ++ * untouched. ++ */ ++bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, ++ HBitmap **backup, Error **errp) ++{ ++ bool ret = false; ++ ++ bdrv_dirty_bitmaps_lock(dest->bs); ++ if (src->bs != dest->bs) { ++ bdrv_dirty_bitmaps_lock(src->bs); ++ } ++ ++ if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) { ++ goto out; ++ } ++ ++ if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) { ++ goto out; ++ } ++ ++ if (bdrv_dirty_bitmap_size(src) != bdrv_dirty_bitmap_size(dest)) { ++ error_setg(errp, "Bitmaps are of different sizes (destination size is %" ++ PRId64 ", source size is %" PRId64 ") and can't be merged", ++ bdrv_dirty_bitmap_size(dest), bdrv_dirty_bitmap_size(src)); ++ goto out; ++ } ++ ++ bdrv_dirty_bitmap_merge_internal(dest, src, backup, false); ++ ret = true; ++ ++out: ++ bdrv_dirty_bitmaps_unlock(dest->bs); ++ if (src->bs != dest->bs) { ++ bdrv_dirty_bitmaps_unlock(src->bs); ++ } ++ ++ return ret; ++} ++ ++/** ++ * bdrv_dirty_bitmap_merge_internal: merge src into dest. ++ * Does NOT check bitmap permissions; not suitable for use as public API. ++ * @dest, @src and @backup (if not NULL) must have same size. ++ * ++ * @backup: If provided, make a copy of dest here prior to merge. ++ * @lock: If true, lock and unlock bitmaps on the way in/out. ++ */ ++void bdrv_dirty_bitmap_merge_internal(BdrvDirtyBitmap *dest, ++ const BdrvDirtyBitmap *src, ++ HBitmap **backup, ++ bool lock) ++{ ++ IO_CODE(); ++ ++ assert(!bdrv_dirty_bitmap_readonly(dest)); ++ assert(!bdrv_dirty_bitmap_inconsistent(dest)); ++ assert(!bdrv_dirty_bitmap_inconsistent(src)); ++ ++ if (lock) { ++ bdrv_dirty_bitmaps_lock(dest->bs); ++ if (src->bs != dest->bs) { ++ bdrv_dirty_bitmaps_lock(src->bs); ++ } ++ } ++ ++ if (backup) { ++ *backup = dest->bitmap; ++ dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); ++ hbitmap_merge(*backup, src->bitmap, dest->bitmap); ++ } else { ++ hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); ++ } ++ ++ if (lock) { ++ bdrv_dirty_bitmaps_unlock(dest->bs); ++ if (src->bs != dest->bs) { ++ bdrv_dirty_bitmaps_unlock(src->bs); ++ } ++ } ++} +diff --git a/qcow2/lib/block/file-posix.c b/qcow2/lib/block/file-posix.c +new file mode 100644 +index 00000000..ff928b5e +--- /dev/null ++++ b/qcow2/lib/block/file-posix.c +@@ -0,0 +1,4565 @@ ++/* ++ * Block driver for RAW files (posix) ++ * ++ * Copyright (c) 2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/cutils.h" ++#include "qemu/error-report.h" ++#include "block/block-io.h" ++#include "block/block_int.h" ++#include "qemu/module.h" ++#include "qemu/option.h" ++#include "qemu/units.h" ++#include "qemu/memalign.h" ++#include "trace.h" ++#include "block/thread-pool.h" ++#include "qemu/iov.h" ++#include "block/raw-aio.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qstring.h" ++ ++#include "scsi/pr-manager.h" ++#include "scsi/constants.h" ++ ++#if defined(__APPLE__) && (__MACH__) ++#include ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++//#include ++#include ++#include ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#endif ++ ++#ifdef __sun__ ++#define _POSIX_PTHREAD_SEMANTICS 1 ++#include ++#endif ++#ifdef __linux__ ++#include ++#include ++#include ++#include ++#if defined(CONFIG_BLKZONED) ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef __s390__ ++#include ++#endif ++#ifndef FS_NOCOW_FL ++#define FS_NOCOW_FL 0x00800000 /* Do not cow file */ ++#endif ++#endif ++#if defined(CONFIG_FALLOCATE_PUNCH_HOLE) || defined(CONFIG_FALLOCATE_ZERO_RANGE) ++#include ++#endif ++#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) ++#include ++#include ++#endif ++ ++#ifdef __OpenBSD__ ++#include ++#include ++#include ++#endif ++ ++#ifdef __NetBSD__ ++#include ++#include ++#include ++#include ++#endif ++ ++#ifdef __DragonFly__ ++#include ++#include ++#endif ++ ++/* OS X does not have O_DSYNC */ ++#ifndef O_DSYNC ++#ifdef O_SYNC ++#define O_DSYNC O_SYNC ++#elif defined(O_FSYNC) ++#define O_DSYNC O_FSYNC ++#endif ++#endif ++ ++/* Approximate O_DIRECT with O_DSYNC if O_DIRECT isn't available */ ++#ifndef O_DIRECT ++#define O_DIRECT O_DSYNC ++#endif ++ ++#define FTYPE_FILE 0 ++#define FTYPE_CD 1 ++ ++#define MAX_BLOCKSIZE 4096 ++ ++/* Posix file locking bytes. Libvirt takes byte 0, we start from higher bytes, ++ * leaving a few more bytes for its future use. */ ++#define RAW_LOCK_PERM_BASE 100 ++#define RAW_LOCK_SHARED_BASE 200 ++ ++typedef struct BDRVRawState { ++ int fd; ++ bool use_lock; ++ int type; ++ int open_flags; ++ size_t buf_align; ++ ++ /* The current permissions. */ ++ uint64_t perm; ++ uint64_t shared_perm; ++ ++ /* The perms bits whose corresponding bytes are already locked in ++ * s->fd. */ ++ uint64_t locked_perm; ++ uint64_t locked_shared_perm; ++ ++ uint64_t aio_max_batch; ++ ++ int perm_change_fd; ++ int perm_change_flags; ++ BDRVReopenState *reopen_state; ++ ++ bool has_discard:1; ++ bool has_write_zeroes:1; ++ bool use_linux_aio:1; ++ bool has_laio_fdsync:1; ++ bool use_linux_io_uring:1; ++ int page_cache_inconsistent; /* errno from fdatasync failure */ ++ bool has_fallocate; ++ bool needs_alignment; ++ bool force_alignment; ++ bool drop_cache; ++ bool check_cache_dropped; ++ struct { ++ uint64_t discard_nb_ok; ++ uint64_t discard_nb_failed; ++ uint64_t discard_bytes_ok; ++ } stats; ++ ++ PRManager *pr_mgr; ++} BDRVRawState; ++ ++typedef struct BDRVRawReopenState { ++ int open_flags; ++ bool drop_cache; ++ bool check_cache_dropped; ++} BDRVRawReopenState; ++ ++static int fd_open(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ /* this is just to ensure s->fd is sane (its called by io ops) */ ++ if (s->fd >= 0) { ++ return 0; ++ } ++ return -EIO; ++} ++ ++static int64_t raw_getlength(BlockDriverState *bs); ++ ++typedef struct RawPosixAIOData { ++ BlockDriverState *bs; ++ int aio_type; ++ int aio_fildes; ++ ++ off_t aio_offset; ++ uint64_t aio_nbytes; ++ ++ union { ++ struct { ++ struct iovec *iov; ++ int niov; ++ } io; ++ struct { ++ uint64_t cmd; ++ void *buf; ++ } ioctl; ++ struct { ++ int aio_fd2; ++ off_t aio_offset2; ++ } copy_range; ++ struct { ++ PreallocMode prealloc; ++ Error **errp; ++ } truncate; ++ struct { ++ unsigned int *nr_zones; ++ BlockZoneDescriptor *zones; ++ } zone_report; ++ struct { ++ unsigned long op; ++ } zone_mgmt; ++ }; ++} RawPosixAIOData; ++ ++#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ++static int cdrom_reopen(BlockDriverState *bs); ++#endif ++ ++/* ++ * Elide EAGAIN and EACCES details when failing to lock, as this ++ * indicates that the specified file region is already locked by ++ * another process, which is considered a common scenario. ++ */ ++#define raw_lock_error_setg_errno(errp, err, fmt, ...) \ ++ do { \ ++ if ((err) == EAGAIN || (err) == EACCES) { \ ++ error_setg((errp), (fmt), ## __VA_ARGS__); \ ++ } else { \ ++ error_setg_errno((errp), (err), (fmt), ## __VA_ARGS__); \ ++ } \ ++ } while (0) ++ ++#if defined(__NetBSD__) ++static int raw_normalize_devicepath(const char **filename, Error **errp) ++{ ++ static char namebuf[PATH_MAX]; ++ const char *dp, *fname; ++ struct stat sb; ++ ++ fname = *filename; ++ dp = strrchr(fname, '/'); ++ if (lstat(fname, &sb) < 0) { ++ error_setg_file_open(errp, errno, fname); ++ return -errno; ++ } ++ ++ if (!S_ISBLK(sb.st_mode)) { ++ return 0; ++ } ++ ++ if (dp == NULL) { ++ snprintf(namebuf, PATH_MAX, "r%s", fname); ++ } else { ++ snprintf(namebuf, PATH_MAX, "%.*s/r%s", ++ (int)(dp - fname), fname, dp + 1); ++ } ++ *filename = namebuf; ++ warn_report("%s is a block device, using %s", fname, *filename); ++ ++ return 0; ++} ++#else ++static int raw_normalize_devicepath(const char **filename, Error **errp) ++{ ++ return 0; ++} ++#endif ++ ++/* ++ * Get logical block size via ioctl. On success store it in @sector_size_p. ++ */ ++static int probe_logical_blocksize(int fd, unsigned int *sector_size_p) ++{ ++ unsigned int sector_size; ++ bool success = false; ++ int i; ++ ++ errno = ENOTSUP; ++ static const unsigned long ioctl_list[] = { ++#ifdef BLKSSZGET ++ BLKSSZGET, ++#endif ++#ifdef DKIOCGETBLOCKSIZE ++ DKIOCGETBLOCKSIZE, ++#endif ++#ifdef DIOCGSECTORSIZE ++ DIOCGSECTORSIZE, ++#endif ++ }; ++ ++ /* Try a few ioctls to get the right size */ ++ for (i = 0; i < (int)ARRAY_SIZE(ioctl_list); i++) { ++ if (ioctl(fd, ioctl_list[i], §or_size) >= 0) { ++ *sector_size_p = sector_size; ++ success = true; ++ } ++ } ++ ++ return success ? 0 : -errno; ++} ++ ++/** ++ * Get physical block size of @fd. ++ * On success, store it in @blk_size and return 0. ++ * On failure, return -errno. ++ */ ++static int probe_physical_blocksize(int fd, unsigned int *blk_size) ++{ ++#ifdef BLKPBSZGET ++ if (ioctl(fd, BLKPBSZGET, blk_size) < 0) { ++ return -errno; ++ } ++ return 0; ++#else ++ return -ENOTSUP; ++#endif ++} ++ ++/* ++ * Returns true if no alignment restrictions are necessary even for files ++ * opened with O_DIRECT. ++ * ++ * raw_probe_alignment() probes the required alignment and assume that 1 means ++ * the probing failed, so it falls back to a safe default of 4k. This can be ++ * avoided if we know that byte alignment is okay for the file. ++ */ ++static bool dio_byte_aligned(int fd) ++{ ++#ifdef __linux__ ++ struct statfs buf; ++ int ret; ++ ++ ret = fstatfs(fd, &buf); ++ if (ret == 0 && buf.f_type == NFS_SUPER_MAGIC) { ++ return true; ++ } ++#endif ++ return false; ++} ++ ++static bool raw_needs_alignment(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if ((bs->open_flags & BDRV_O_NOCACHE) != 0 && !dio_byte_aligned(s->fd)) { ++ return true; ++ } ++ ++ return s->force_alignment; ++} ++ ++/* Check if read is allowed with given memory buffer and length. ++ * ++ * This function is used to check O_DIRECT memory buffer and request alignment. ++ */ ++static bool raw_is_io_aligned(int fd, void *buf, size_t len) ++{ ++ ssize_t ret = pread(fd, buf, len, 0); ++ ++ if (ret >= 0) { ++ return true; ++ } ++ ++#ifdef __linux__ ++ /* The Linux kernel returns EINVAL for misaligned O_DIRECT reads. Ignore ++ * other errors (e.g. real I/O error), which could happen on a failed ++ * drive, since we only care about probing alignment. ++ */ ++ if (errno != EINVAL) { ++ return true; ++ } ++#endif ++ ++ return false; ++} ++ ++static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ char *buf; ++ size_t max_align = MAX(MAX_BLOCKSIZE, qemu_real_host_page_size()); ++ size_t alignments[] = {1, 512, 1024, 2048, 4096}; ++ ++ /* For SCSI generic devices the alignment is not really used. ++ With buffered I/O, we don't have any restrictions. */ ++ if (bdrv_is_sg(bs) || !s->needs_alignment) { ++ bs->bl.request_alignment = 1; ++ s->buf_align = 1; ++ return; ++ } ++ ++ bs->bl.request_alignment = 0; ++ s->buf_align = 0; ++ /* Let's try to use the logical blocksize for the alignment. */ ++ if (probe_logical_blocksize(fd, &bs->bl.request_alignment) < 0) { ++ bs->bl.request_alignment = 0; ++ } ++ ++#ifdef __linux__ ++ /* ++ * The XFS ioctl definitions are shipped in extra packages that might ++ * not always be available. Since we just need the XFS_IOC_DIOINFO ioctl ++ * here, we simply use our own definition instead: ++ */ ++ struct xfs_dioattr { ++ uint32_t d_mem; ++ uint32_t d_miniosz; ++ uint32_t d_maxiosz; ++ } da; ++ if (ioctl(fd, _IOR('X', 30, struct xfs_dioattr), &da) >= 0) { ++ bs->bl.request_alignment = da.d_miniosz; ++ /* The kernel returns wrong information for d_mem */ ++ /* s->buf_align = da.d_mem; */ ++ } ++#endif ++ ++ /* ++ * If we could not get the sizes so far, we can only guess them. First try ++ * to detect request alignment, since it is more likely to succeed. Then ++ * try to detect buf_align, which cannot be detected in some cases (e.g. ++ * Gluster). If buf_align cannot be detected, we fallback to the value of ++ * request_alignment. ++ */ ++ ++ if (!bs->bl.request_alignment) { ++ int i; ++ size_t align; ++ buf = qemu_memalign(max_align, max_align); ++ for (i = 0; i < ARRAY_SIZE(alignments); i++) { ++ align = alignments[i]; ++ if (raw_is_io_aligned(fd, buf, align)) { ++ /* Fallback to safe value. */ ++ bs->bl.request_alignment = (align != 1) ? align : max_align; ++ break; ++ } ++ } ++ qemu_vfree(buf); ++ } ++ ++ if (!s->buf_align) { ++ int i; ++ size_t align; ++ buf = qemu_memalign(max_align, 2 * max_align); ++ for (i = 0; i < ARRAY_SIZE(alignments); i++) { ++ align = alignments[i]; ++ if (raw_is_io_aligned(fd, buf + align, max_align)) { ++ /* Fallback to request_alignment. */ ++ s->buf_align = (align != 1) ? align : bs->bl.request_alignment; ++ break; ++ } ++ } ++ qemu_vfree(buf); ++ } ++ ++ if (!s->buf_align || !bs->bl.request_alignment) { ++ error_setg(errp, "Could not find working O_DIRECT alignment"); ++ error_append_hint(errp, "Try cache.direct=off\n"); ++ } ++} ++ ++static int check_hdev_writable(int fd) ++{ ++#if defined(BLKROGET) ++ /* Linux block devices can be configured "read-only" using blockdev(8). ++ * This is independent of device node permissions and therefore open(2) ++ * with O_RDWR succeeds. Actual writes fail with EPERM. ++ * ++ * bdrv_open() is supposed to fail if the disk is read-only. Explicitly ++ * check for read-only block devices so that Linux block devices behave ++ * properly. ++ */ ++ struct stat st; ++ int readonly = 0; ++ ++ if (fstat(fd, &st)) { ++ return -errno; ++ } ++ ++ if (!S_ISBLK(st.st_mode)) { ++ return 0; ++ } ++ ++ if (ioctl(fd, BLKROGET, &readonly) < 0) { ++ return -errno; ++ } ++ ++ if (readonly) { ++ return -EACCES; ++ } ++#endif /* defined(BLKROGET) */ ++ return 0; ++} ++ ++static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers) ++{ ++ bool read_write = false; ++ assert(open_flags != NULL); ++ ++ *open_flags |= O_BINARY; ++ *open_flags &= ~O_ACCMODE; ++ ++ if (bdrv_flags & BDRV_O_AUTO_RDONLY) { ++ read_write = has_writers; ++ } else if (bdrv_flags & BDRV_O_RDWR) { ++ read_write = true; ++ } ++ ++ if (read_write) { ++ *open_flags |= O_RDWR; ++ } else { ++ *open_flags |= O_RDONLY; ++ } ++ ++ /* Use O_DSYNC for write-through caching, no flags for write-back caching, ++ * and O_DIRECT for no caching. */ ++ if ((bdrv_flags & BDRV_O_NOCACHE)) { ++ *open_flags |= O_DIRECT; ++ } ++} ++ ++static void raw_parse_filename(const char *filename, QDict *options, ++ Error **errp) ++{ ++ bdrv_parse_filename_strip_prefix(filename, "file:", options); ++} ++ ++static QemuOptsList raw_runtime_opts = { ++ .name = "raw", ++ .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), ++ .desc = { ++ { ++ .name = "filename", ++ .type = QEMU_OPT_STRING, ++ .help = "File name of the image", ++ }, ++ { ++ .name = "aio", ++ .type = QEMU_OPT_STRING, ++ .help = "host AIO implementation (threads, native, io_uring)", ++ }, ++ { ++ .name = "aio-max-batch", ++ .type = QEMU_OPT_NUMBER, ++ .help = "AIO max batch size (0 = auto handled by AIO backend, default: 0)", ++ }, ++ { ++ .name = "locking", ++ .type = QEMU_OPT_STRING, ++ .help = "file locking mode (on/off/auto, default: auto)", ++ }, ++ { ++ .name = "pr-manager", ++ .type = QEMU_OPT_STRING, ++ .help = "id of persistent reservation manager object (default: none)", ++ }, ++#if defined(__linux__) ++ { ++ .name = "drop-cache", ++ .type = QEMU_OPT_BOOL, ++ .help = "invalidate page cache during live migration (default: on)", ++ }, ++#endif ++ { ++ .name = "x-check-cache-dropped", ++ .type = QEMU_OPT_BOOL, ++ .help = "check that page cache was dropped on live migration (default: off)" ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++static const char *const mutable_opts[] = { "x-check-cache-dropped", NULL }; ++ ++static int raw_open_common(BlockDriverState *bs, QDict *options, ++ int bdrv_flags, int open_flags, ++ bool device, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ QemuOpts *opts; ++ Error *local_err = NULL; ++ const char *filename = NULL; ++ const char *str; ++ BlockdevAioOptions aio, aio_default; ++ int fd, ret; ++ struct stat st; ++ OnOffAuto locking; ++ ++ opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ filename = qemu_opt_get(opts, "filename"); ++ ++ ret = raw_normalize_devicepath(&filename, errp); ++ if (ret != 0) { ++ goto fail; ++ } ++ ++ if (bdrv_flags & BDRV_O_NATIVE_AIO) { ++ aio_default = BLOCKDEV_AIO_OPTIONS_NATIVE; ++#ifdef CONFIG_LINUX_IO_URING ++ } else if (bdrv_flags & BDRV_O_IO_URING) { ++ aio_default = BLOCKDEV_AIO_OPTIONS_IO_URING; ++#endif ++ } else { ++ aio_default = BLOCKDEV_AIO_OPTIONS_THREADS; ++ } ++ ++ aio = qapi_enum_parse(&BlockdevAioOptions_lookup, ++ qemu_opt_get(opts, "aio"), ++ aio_default, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ s->use_linux_aio = (aio == BLOCKDEV_AIO_OPTIONS_NATIVE); ++#ifdef CONFIG_LINUX_IO_URING ++ s->use_linux_io_uring = (aio == BLOCKDEV_AIO_OPTIONS_IO_URING); ++#endif ++ ++ s->aio_max_batch = qemu_opt_get_number(opts, "aio-max-batch", 0); ++ ++ locking = qapi_enum_parse(&OnOffAuto_lookup, ++ qemu_opt_get(opts, "locking"), ++ ON_OFF_AUTO_AUTO, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail; ++ } ++ switch (locking) { ++ case ON_OFF_AUTO_ON: ++ s->use_lock = true; ++ if (!qemu_has_ofd_lock()) { ++ warn_report("File lock requested but OFD locking syscall is " ++ "unavailable, falling back to POSIX file locks"); ++ error_printf("Due to the implementation, locks can be lost " ++ "unexpectedly.\n"); ++ } ++ break; ++ case ON_OFF_AUTO_OFF: ++ s->use_lock = false; ++ break; ++ case ON_OFF_AUTO_AUTO: ++ s->use_lock = qemu_has_ofd_lock(); ++ break; ++ default: ++ abort(); ++ } ++ ++ str = qemu_opt_get(opts, "pr-manager"); ++ if (str) { ++ s->pr_mgr = pr_manager_lookup(str, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ ++ s->drop_cache = qemu_opt_get_bool(opts, "drop-cache", true); ++ s->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", ++ false); ++ ++ s->open_flags = open_flags; ++ raw_parse_flags(bdrv_flags, &s->open_flags, false); ++ ++ s->fd = -1; ++ fd = qemu_open(filename, s->open_flags, errp); ++ ret = fd < 0 ? -errno : 0; ++ ++ if (ret < 0) { ++ if (ret == -EROFS) { ++ ret = -EACCES; ++ } ++ goto fail; ++ } ++ s->fd = fd; ++ ++ /* Check s->open_flags rather than bdrv_flags due to auto-read-only */ ++ if (s->open_flags & O_RDWR) { ++ ret = check_hdev_writable(s->fd); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "The device is not writable"); ++ goto fail; ++ } ++ } ++ ++ s->perm = 0; ++ s->shared_perm = BLK_PERM_ALL; ++ ++#ifdef CONFIG_LINUX_AIO ++ /* Currently Linux does AIO only for files opened with O_DIRECT */ ++ if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) { ++ error_setg(errp, "aio=native was specified, but it requires " ++ "cache.direct=on, which was not specified."); ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (s->use_linux_aio) { ++ s->has_laio_fdsync = laio_has_fdsync(s->fd); ++ } ++#else ++ if (s->use_linux_aio) { ++ error_setg(errp, "aio=native was specified, but is not supported " ++ "in this build."); ++ ret = -EINVAL; ++ goto fail; ++ } ++#endif /* !defined(CONFIG_LINUX_AIO) */ ++ ++#ifndef CONFIG_LINUX_IO_URING ++ if (s->use_linux_io_uring) { ++ error_setg(errp, "aio=io_uring was specified, but is not supported " ++ "in this build."); ++ ret = -EINVAL; ++ goto fail; ++ } ++#endif /* !defined(CONFIG_LINUX_IO_URING) */ ++ ++ s->has_discard = true; ++ s->has_write_zeroes = true; ++ ++ if (fstat(s->fd, &st) < 0) { ++ ret = -errno; ++ error_setg_errno(errp, errno, "Could not stat file"); ++ goto fail; ++ } ++ ++ if (!device) { ++ if (!S_ISREG(st.st_mode)) { ++ error_setg(errp, "'%s' driver requires '%s' to be a regular file", ++ bs->drv->format_name, bs->filename); ++ ret = -EINVAL; ++ goto fail; ++ } else { ++ s->has_fallocate = true; ++ } ++ } else { ++ if (!(S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { ++ error_setg(errp, "'%s' driver requires '%s' to be either " ++ "a character or block device", ++ bs->drv->format_name, bs->filename); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++#ifdef CONFIG_BLKZONED ++ /* ++ * The kernel page cache does not reliably work for writes to SWR zones ++ * of zoned block device because it can not guarantee the order of writes. ++ */ ++ if ((bs->bl.zoned != BLK_Z_NONE) && ++ (!(s->open_flags & O_DIRECT))) { ++ error_setg(errp, "The driver supports zoned devices, and it requires " ++ "cache.direct=on, which was not specified."); ++ return -EINVAL; /* No host kernel page cache */ ++ } ++#endif ++ ++ if (S_ISBLK(st.st_mode)) { ++#ifdef __linux__ ++ /* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do ++ * not rely on the contents of discarded blocks unless using O_DIRECT. ++ * Same for BLKZEROOUT. ++ */ ++ if (!(bs->open_flags & BDRV_O_NOCACHE)) { ++ s->has_write_zeroes = false; ++ } ++#endif ++ } ++#ifdef __FreeBSD__ ++ if (S_ISCHR(st.st_mode)) { ++ /* ++ * The file is a char device (disk), which on FreeBSD isn't behind ++ * a pager, so force all requests to be aligned. This is needed ++ * so QEMU makes sure all IO operations on the device are aligned ++ * to sector size, or else FreeBSD will reject them with EINVAL. ++ */ ++ s->force_alignment = true; ++ } ++#endif ++ s->needs_alignment = raw_needs_alignment(bs); ++ ++ bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; ++ if (S_ISREG(st.st_mode)) { ++ /* When extending regular files, we get zeros from the OS */ ++ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; ++ } ++ ret = 0; ++fail: ++ if (ret < 0 && s->fd != -1) { ++ qemu_close(s->fd); ++ } ++ if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { ++ unlink(filename); ++ } ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static int raw_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ s->type = FTYPE_FILE; ++ return raw_open_common(bs, options, flags, 0, false, errp); ++} ++ ++typedef enum { ++ RAW_PL_PREPARE, ++ RAW_PL_COMMIT, ++ RAW_PL_ABORT, ++} RawPermLockOp; ++ ++#define PERM_FOREACH(i) \ ++ for ((i) = 0; (1ULL << (i)) <= BLK_PERM_ALL; i++) ++ ++/* Lock bytes indicated by @perm_lock_bits and @shared_perm_lock_bits in the ++ * file; if @unlock == true, also unlock the unneeded bytes. ++ * @shared_perm_lock_bits is the mask of all permissions that are NOT shared. ++ */ ++static int raw_apply_lock_bytes(BDRVRawState *s, int fd, ++ uint64_t perm_lock_bits, ++ uint64_t shared_perm_lock_bits, ++ bool unlock, Error **errp) ++{ ++ int ret; ++ int i; ++ uint64_t locked_perm, locked_shared_perm; ++ ++ if (s) { ++ locked_perm = s->locked_perm; ++ locked_shared_perm = s->locked_shared_perm; ++ } else { ++ /* ++ * We don't have the previous bits, just lock/unlock for each of the ++ * requested bits. ++ */ ++ if (unlock) { ++ locked_perm = BLK_PERM_ALL; ++ locked_shared_perm = BLK_PERM_ALL; ++ } else { ++ locked_perm = 0; ++ locked_shared_perm = 0; ++ } ++ } ++ ++ PERM_FOREACH(i) { ++ int off = RAW_LOCK_PERM_BASE + i; ++ uint64_t bit = (1ULL << i); ++ if ((perm_lock_bits & bit) && !(locked_perm & bit)) { ++ ret = qemu_lock_fd(fd, off, 1, false); ++ if (ret) { ++ raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d", ++ off); ++ return ret; ++ } else if (s) { ++ s->locked_perm |= bit; ++ } ++ } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) { ++ ret = qemu_unlock_fd(fd, off, 1); ++ if (ret) { ++ error_setg_errno(errp, -ret, "Failed to unlock byte %d", off); ++ return ret; ++ } else if (s) { ++ s->locked_perm &= ~bit; ++ } ++ } ++ } ++ PERM_FOREACH(i) { ++ int off = RAW_LOCK_SHARED_BASE + i; ++ uint64_t bit = (1ULL << i); ++ if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) { ++ ret = qemu_lock_fd(fd, off, 1, false); ++ if (ret) { ++ raw_lock_error_setg_errno(errp, -ret, "Failed to lock byte %d", ++ off); ++ return ret; ++ } else if (s) { ++ s->locked_shared_perm |= bit; ++ } ++ } else if (unlock && (locked_shared_perm & bit) && ++ !(shared_perm_lock_bits & bit)) { ++ ret = qemu_unlock_fd(fd, off, 1); ++ if (ret) { ++ error_setg_errno(errp, -ret, "Failed to unlock byte %d", off); ++ return ret; ++ } else if (s) { ++ s->locked_shared_perm &= ~bit; ++ } ++ } ++ } ++ return 0; ++} ++ ++/* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */ ++static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, ++ Error **errp) ++{ ++ int ret; ++ int i; ++ ++ PERM_FOREACH(i) { ++ int off = RAW_LOCK_SHARED_BASE + i; ++ uint64_t p = 1ULL << i; ++ if (perm & p) { ++ ret = qemu_lock_fd_test(fd, off, 1, true); ++ if (ret) { ++ char *perm_name = bdrv_perm_names(p); ++ ++ raw_lock_error_setg_errno(errp, -ret, ++ "Failed to get \"%s\" lock", ++ perm_name); ++ g_free(perm_name); ++ return ret; ++ } ++ } ++ } ++ PERM_FOREACH(i) { ++ int off = RAW_LOCK_PERM_BASE + i; ++ uint64_t p = 1ULL << i; ++ if (!(shared_perm & p)) { ++ ret = qemu_lock_fd_test(fd, off, 1, true); ++ if (ret) { ++ char *perm_name = bdrv_perm_names(p); ++ ++ raw_lock_error_setg_errno(errp, -ret, ++ "Failed to get shared \"%s\" lock", ++ perm_name); ++ g_free(perm_name); ++ return ret; ++ } ++ } ++ } ++ return 0; ++} ++ ++static int raw_handle_perm_lock(BlockDriverState *bs, ++ RawPermLockOp op, ++ uint64_t new_perm, uint64_t new_shared, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret = 0; ++ Error *local_err = NULL; ++ ++ if (!s->use_lock) { ++ return 0; ++ } ++ ++ if (bdrv_get_flags(bs) & BDRV_O_INACTIVE) { ++ return 0; ++ } ++ ++ switch (op) { ++ case RAW_PL_PREPARE: ++ if ((s->perm | new_perm) == s->perm && ++ (s->shared_perm & new_shared) == s->shared_perm) ++ { ++ /* ++ * We are going to unlock bytes, it should not fail. If it fail due ++ * to some fs-dependent permission-unrelated reasons (which occurs ++ * sometimes on NFS and leads to abort in bdrv_replace_child) we ++ * can't prevent such errors by any check here. And we ignore them ++ * anyway in ABORT and COMMIT. ++ */ ++ return 0; ++ } ++ ret = raw_apply_lock_bytes(s, s->fd, s->perm | new_perm, ++ ~s->shared_perm | ~new_shared, ++ false, errp); ++ if (!ret) { ++ ret = raw_check_lock_bytes(s->fd, new_perm, new_shared, errp); ++ if (!ret) { ++ return 0; ++ } ++ error_append_hint(errp, ++ "Is another process using the image [%s]?\n", ++ bs->filename); ++ } ++ /* fall through to unlock bytes. */ ++ case RAW_PL_ABORT: ++ raw_apply_lock_bytes(s, s->fd, s->perm, ~s->shared_perm, ++ true, &local_err); ++ if (local_err) { ++ /* Theoretically the above call only unlocks bytes and it cannot ++ * fail. Something weird happened, report it. ++ */ ++ warn_report_err(local_err); ++ } ++ break; ++ case RAW_PL_COMMIT: ++ raw_apply_lock_bytes(s, s->fd, new_perm, ~new_shared, ++ true, &local_err); ++ if (local_err) { ++ /* Theoretically the above call only unlocks bytes and it cannot ++ * fail. Something weird happened, report it. ++ */ ++ warn_report_err(local_err); ++ } ++ break; ++ } ++ return ret; ++} ++ ++/* Sets a specific flag */ ++static int fcntl_setfl(int fd, int flag) ++{ ++ int flags; ++ ++ flags = fcntl(fd, F_GETFL); ++ if (flags == -1) { ++ return -errno; ++ } ++ if (fcntl(fd, F_SETFL, flags | flag) == -1) { ++ return -errno; ++ } ++ return 0; ++} ++ ++static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, ++ int *open_flags, uint64_t perm, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd = -1; ++ int ret; ++ bool has_writers = perm & ++ (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_RESIZE); ++ int fcntl_flags = O_APPEND | O_NONBLOCK; ++#ifdef O_NOATIME ++ fcntl_flags |= O_NOATIME; ++#endif ++ ++ *open_flags = 0; ++ if (s->type == FTYPE_CD) { ++ *open_flags |= O_NONBLOCK; ++ } ++ ++ raw_parse_flags(flags, open_flags, has_writers); ++ ++#ifdef O_ASYNC ++ /* Not all operating systems have O_ASYNC, and those that don't ++ * will not let us track the state into rs->open_flags (typically ++ * you achieve the same effect with an ioctl, for example I_SETSIG ++ * on Solaris). But we do not use O_ASYNC, so that's fine. ++ */ ++ assert((s->open_flags & O_ASYNC) == 0); ++#endif ++ ++ if (*open_flags == s->open_flags) { ++ /* We're lucky, the existing fd is fine */ ++ return s->fd; ++ } ++ ++ if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { ++ /* dup the original fd */ ++ fd = qemu_dup(s->fd); ++ if (fd >= 0) { ++ ret = fcntl_setfl(fd, *open_flags); ++ if (ret) { ++ qemu_close(fd); ++ fd = -1; ++ } ++ } ++ } ++ ++ /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ ++ if (fd == -1) { ++ const char *normalized_filename = bs->filename; ++ ret = raw_normalize_devicepath(&normalized_filename, errp); ++ if (ret >= 0) { ++ fd = qemu_open(normalized_filename, *open_flags, errp); ++ if (fd == -1) { ++ return -1; ++ } ++ } ++ } ++ ++ if (fd != -1 && (*open_flags & O_RDWR)) { ++ ret = check_hdev_writable(fd); ++ if (ret < 0) { ++ qemu_close(fd); ++ error_setg_errno(errp, -ret, "The device is not writable"); ++ return -1; ++ } ++ } ++ ++ return fd; ++} ++ ++static int raw_reopen_prepare(BDRVReopenState *state, ++ BlockReopenQueue *queue, Error **errp) ++{ ++ BDRVRawState *s; ++ BDRVRawReopenState *rs; ++ QemuOpts *opts; ++ int ret; ++ ++ assert(state != NULL); ++ assert(state->bs != NULL); ++ ++ s = state->bs->opaque; ++ ++ state->opaque = g_new0(BDRVRawReopenState, 1); ++ rs = state->opaque; ++ ++ /* Handle options changes */ ++ opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, state->options, errp)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ rs->drop_cache = qemu_opt_get_bool_del(opts, "drop-cache", true); ++ rs->check_cache_dropped = ++ qemu_opt_get_bool_del(opts, "x-check-cache-dropped", false); ++ ++ /* This driver's reopen function doesn't currently allow changing ++ * other options, so let's put them back in the original QDict and ++ * bdrv_reopen_prepare() will detect changes and complain. */ ++ qemu_opts_to_qdict(opts, state->options); ++ ++ /* ++ * As part of reopen prepare we also want to create new fd by ++ * raw_reconfigure_getfd(). But it wants updated "perm", when in ++ * bdrv_reopen_multiple() .bdrv_reopen_prepare() callback called prior to ++ * permission update. Happily, permission update is always a part ++ * (a separate stage) of bdrv_reopen_multiple() so we can rely on this ++ * fact and reconfigure fd in raw_check_perm(). ++ */ ++ ++ s->reopen_state = state; ++ ret = 0; ++ ++out: ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static void raw_reopen_commit(BDRVReopenState *state) ++{ ++ BDRVRawReopenState *rs = state->opaque; ++ BDRVRawState *s = state->bs->opaque; ++ ++ s->drop_cache = rs->drop_cache; ++ s->check_cache_dropped = rs->check_cache_dropped; ++ s->open_flags = rs->open_flags; ++ g_free(state->opaque); ++ state->opaque = NULL; ++ ++ assert(s->reopen_state == state); ++ s->reopen_state = NULL; ++} ++ ++ ++static void raw_reopen_abort(BDRVReopenState *state) ++{ ++ BDRVRawReopenState *rs = state->opaque; ++ BDRVRawState *s = state->bs->opaque; ++ ++ /* nothing to do if NULL, we didn't get far enough */ ++ if (rs == NULL) { ++ return; ++ } ++ ++ g_free(state->opaque); ++ state->opaque = NULL; ++ ++ assert(s->reopen_state == state); ++ s->reopen_state = NULL; ++} ++ ++static int hdev_get_max_hw_transfer(int fd, struct stat *st) ++{ ++#ifdef BLKSECTGET ++ if (S_ISBLK(st->st_mode)) { ++ unsigned short max_sectors = 0; ++ if (ioctl(fd, BLKSECTGET, &max_sectors) == 0) { ++ return max_sectors * 512; ++ } ++ } else { ++ int max_bytes = 0; ++ if (ioctl(fd, BLKSECTGET, &max_bytes) == 0) { ++ return max_bytes; ++ } ++ } ++ return -errno; ++#else ++ return -ENOSYS; ++#endif ++} ++ ++/* ++ * Get a sysfs attribute value as character string. ++ */ ++#ifdef CONFIG_LINUX ++static int get_sysfs_str_val(struct stat *st, const char *attribute, ++ char **val) { ++ g_autofree char *sysfspath = NULL; ++ size_t len; ++ ++ if (!S_ISBLK(st->st_mode)) { ++ return -ENOTSUP; ++ } ++ ++ sysfspath = g_strdup_printf("/sys/dev/block/%u:%u/queue/%s", ++ major(st->st_rdev), minor(st->st_rdev), ++ attribute); ++ if (!g_file_get_contents(sysfspath, val, &len, NULL)) { ++ return -ENOENT; ++ } ++ ++ /* The file is ended with '\n' */ ++ char *p; ++ p = *val; ++ if (*(p + len - 1) == '\n') { ++ *(p + len - 1) = '\0'; ++ } ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_BLKZONED) ++static int get_sysfs_zoned_model(struct stat *st, BlockZoneModel *zoned) ++{ ++ g_autofree char *val = NULL; ++ int ret; ++ ++ ret = get_sysfs_str_val(st, "zoned", &val); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (strcmp(val, "host-managed") == 0) { ++ *zoned = BLK_Z_HM; ++ } else if (strcmp(val, "host-aware") == 0) { ++ *zoned = BLK_Z_HA; ++ } else if (strcmp(val, "none") == 0) { ++ *zoned = BLK_Z_NONE; ++ } else { ++ return -ENOTSUP; ++ } ++ return 0; ++} ++#endif /* defined(CONFIG_BLKZONED) */ ++ ++/* ++ * Get a sysfs attribute value as a long integer. ++ */ ++#ifdef CONFIG_LINUX ++static long get_sysfs_long_val(struct stat *st, const char *attribute) ++{ ++ g_autofree char *str = NULL; ++ const char *end; ++ long val; ++ int ret; ++ ++ ret = get_sysfs_str_val(st, attribute, &str); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* The file is ended with '\n', pass 'end' to accept that. */ ++ ret = qemu_strtol(str, &end, 10, &val); ++ if (ret == 0 && end && *end == '\0') { ++ ret = val; ++ } ++ return ret; ++} ++#endif ++ ++static int hdev_get_max_segments(int fd, struct stat *st) ++{ ++#ifdef CONFIG_LINUX ++ int ret; ++ ++ if (S_ISCHR(st->st_mode)) { ++ if (ioctl(fd, SG_GET_SG_TABLESIZE, &ret) == 0) { ++ return ret; ++ } ++ return -ENOTSUP; ++ } ++ return get_sysfs_long_val(st, "max_segments"); ++#else ++ return -ENOTSUP; ++#endif ++} ++ ++#if defined(CONFIG_BLKZONED) ++/* ++ * If the reset_all flag is true, then the wps of zone whose state is ++ * not readonly or offline should be all reset to the start sector. ++ * Else, take the real wp of the device. ++ */ ++static int get_zones_wp(BlockDriverState *bs, int fd, int64_t offset, ++ unsigned int nrz, bool reset_all) ++{ ++ struct blk_zone *blkz; ++ size_t rep_size; ++ uint64_t sector = offset >> BDRV_SECTOR_BITS; ++ BlockZoneWps *wps = bs->wps; ++ unsigned int j = offset / bs->bl.zone_size; ++ unsigned int n = 0, i = 0; ++ int ret; ++ rep_size = sizeof(struct blk_zone_report) + nrz * sizeof(struct blk_zone); ++ g_autofree struct blk_zone_report *rep = NULL; ++ ++ rep = g_malloc(rep_size); ++ blkz = (struct blk_zone *)(rep + 1); ++ while (n < nrz) { ++ memset(rep, 0, rep_size); ++ rep->sector = sector; ++ rep->nr_zones = nrz - n; ++ ++ do { ++ ret = ioctl(fd, BLKREPORTZONE, rep); ++ } while (ret != 0 && errno == EINTR); ++ if (ret != 0) { ++ error_report("%d: ioctl BLKREPORTZONE at %" PRId64 " failed %d", ++ fd, offset, errno); ++ return -errno; ++ } ++ ++ if (!rep->nr_zones) { ++ break; ++ } ++ ++ for (i = 0; i < rep->nr_zones; ++i, ++n, ++j) { ++ /* ++ * The wp tracking cares only about sequential writes required and ++ * sequential write preferred zones so that the wp can advance to ++ * the right location. ++ * Use the most significant bit of the wp location to indicate the ++ * zone type: 0 for SWR/SWP zones and 1 for conventional zones. ++ */ ++ if (blkz[i].type == BLK_ZONE_TYPE_CONVENTIONAL) { ++ wps->wp[j] |= 1ULL << 63; ++ } else { ++ switch(blkz[i].cond) { ++ case BLK_ZONE_COND_FULL: ++ case BLK_ZONE_COND_READONLY: ++ /* Zone not writable */ ++ wps->wp[j] = (blkz[i].start + blkz[i].len) << BDRV_SECTOR_BITS; ++ break; ++ case BLK_ZONE_COND_OFFLINE: ++ /* Zone not writable nor readable */ ++ wps->wp[j] = (blkz[i].start) << BDRV_SECTOR_BITS; ++ break; ++ default: ++ if (reset_all) { ++ wps->wp[j] = blkz[i].start << BDRV_SECTOR_BITS; ++ } else { ++ wps->wp[j] = blkz[i].wp << BDRV_SECTOR_BITS; ++ } ++ break; ++ } ++ } ++ } ++ sector = blkz[i - 1].start + blkz[i - 1].len; ++ } ++ ++ return 0; ++} ++ ++static void update_zones_wp(BlockDriverState *bs, int fd, int64_t offset, ++ unsigned int nrz) ++{ ++ if (get_zones_wp(bs, fd, offset, nrz, 0) < 0) { ++ error_report("update zone wp failed"); ++ } ++} ++ ++static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ BlockZoneModel zoned; ++ int ret; ++ ++ ret = get_sysfs_zoned_model(st, &zoned); ++ if (ret < 0 || zoned == BLK_Z_NONE) { ++ goto no_zoned; ++ } ++ bs->bl.zoned = zoned; ++ ++ ret = get_sysfs_long_val(st, "max_open_zones"); ++ if (ret >= 0) { ++ bs->bl.max_open_zones = ret; ++ } ++ ++ ret = get_sysfs_long_val(st, "max_active_zones"); ++ if (ret >= 0) { ++ bs->bl.max_active_zones = ret; ++ } ++ ++ /* ++ * The zoned device must at least have zone size and nr_zones fields. ++ */ ++ ret = get_sysfs_long_val(st, "chunk_sectors"); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Unable to read chunk_sectors " ++ "sysfs attribute"); ++ goto no_zoned; ++ } else if (!ret) { ++ error_setg(errp, "Read 0 from chunk_sectors sysfs attribute"); ++ goto no_zoned; ++ } ++ bs->bl.zone_size = ret << BDRV_SECTOR_BITS; ++ ++ ret = get_sysfs_long_val(st, "nr_zones"); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Unable to read nr_zones " ++ "sysfs attribute"); ++ goto no_zoned; ++ } else if (!ret) { ++ error_setg(errp, "Read 0 from nr_zones sysfs attribute"); ++ goto no_zoned; ++ } ++ bs->bl.nr_zones = ret; ++ ++ ret = get_sysfs_long_val(st, "zone_append_max_bytes"); ++ if (ret > 0) { ++ bs->bl.max_append_sectors = ret >> BDRV_SECTOR_BITS; ++ } ++ ++ ret = get_sysfs_long_val(st, "physical_block_size"); ++ if (ret >= 0) { ++ bs->bl.write_granularity = ret; ++ } ++ ++ /* The refresh_limits() function can be called multiple times. */ ++ g_free(bs->wps); ++ bs->wps = g_malloc(sizeof(BlockZoneWps) + ++ sizeof(int64_t) * bs->bl.nr_zones); ++ ret = get_zones_wp(bs, s->fd, 0, bs->bl.nr_zones, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "report wps failed"); ++ goto no_zoned; ++ } ++ qemu_co_mutex_init(&bs->wps->colock); ++ return; ++ ++no_zoned: ++ bs->bl.zoned = BLK_Z_NONE; ++ g_free(bs->wps); ++ bs->wps = NULL; ++} ++#else /* !defined(CONFIG_BLKZONED) */ ++static void raw_refresh_zoned_limits(BlockDriverState *bs, struct stat *st, ++ Error **errp) ++{ ++ bs->bl.zoned = BLK_Z_NONE; ++} ++#endif /* !defined(CONFIG_BLKZONED) */ ++ ++static void raw_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ struct stat st; ++ ++ s->needs_alignment = raw_needs_alignment(bs); ++ raw_probe_alignment(bs, s->fd, errp); ++ ++ bs->bl.min_mem_alignment = s->buf_align; ++ bs->bl.opt_mem_alignment = MAX(s->buf_align, qemu_real_host_page_size()); ++ ++ /* ++ * Maximum transfers are best effort, so it is okay to ignore any ++ * errors. That said, based on the man page errors in fstat would be ++ * very much unexpected; the only possible case seems to be ENOMEM. ++ */ ++ if (fstat(s->fd, &st)) { ++ return; ++ } ++ ++#if defined(__APPLE__) && (__MACH__) ++ struct statfs buf; ++ ++ if (!fstatfs(s->fd, &buf)) { ++ bs->bl.opt_transfer = buf.f_iosize; ++ bs->bl.pdiscard_alignment = buf.f_bsize; ++ } ++#endif ++ ++ if (bdrv_is_sg(bs) || S_ISBLK(st.st_mode)) { ++ int ret = hdev_get_max_hw_transfer(s->fd, &st); ++ ++ if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) { ++ bs->bl.max_hw_transfer = ret; ++ } ++ ++ ret = hdev_get_max_segments(s->fd, &st); ++ if (ret > 0) { ++ bs->bl.max_hw_iov = ret; ++ } ++ } ++ ++ raw_refresh_zoned_limits(bs, &st, errp); ++} ++ ++static int check_for_dasd(int fd) ++{ ++#ifdef BIODASDINFO2 ++ struct dasd_information2_t info = {0}; ++ ++ return ioctl(fd, BIODASDINFO2, &info); ++#else ++ return -1; ++#endif ++} ++ ++/** ++ * Try to get @bs's logical and physical block size. ++ * On success, store them in @bsz and return zero. ++ * On failure, return negative errno. ++ */ ++static int hdev_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ /* If DASD or zoned devices, get blocksizes */ ++ if (check_for_dasd(s->fd) < 0) { ++ /* zoned devices are not DASD */ ++ if (bs->bl.zoned == BLK_Z_NONE) { ++ return -ENOTSUP; ++ } ++ } ++ ret = probe_logical_blocksize(s->fd, &bsz->log); ++ if (ret < 0) { ++ return ret; ++ } ++ return probe_physical_blocksize(s->fd, &bsz->phys); ++} ++ ++/** ++ * Try to get @bs's geometry: cyls, heads, sectors. ++ * On success, store them in @geo and return 0. ++ * On failure return -errno. ++ * (Allows block driver to assign default geometry values that guest sees) ++ */ ++#ifdef __linux__ ++static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) ++{ ++ BDRVRawState *s = bs->opaque; ++ struct hd_geometry ioctl_geo = {0}; ++ ++ /* If DASD, get its geometry */ ++ if (check_for_dasd(s->fd) < 0) { ++ return -ENOTSUP; ++ } ++ if (ioctl(s->fd, HDIO_GETGEO, &ioctl_geo) < 0) { ++ return -errno; ++ } ++ /* HDIO_GETGEO may return success even though geo contains zeros ++ (e.g. certain multipath setups) */ ++ if (!ioctl_geo.heads || !ioctl_geo.sectors || !ioctl_geo.cylinders) { ++ return -ENOTSUP; ++ } ++ /* Do not return a geometry for partition */ ++ if (ioctl_geo.start != 0) { ++ return -ENOTSUP; ++ } ++ geo->heads = ioctl_geo.heads; ++ geo->sectors = ioctl_geo.sectors; ++ geo->cylinders = ioctl_geo.cylinders; ++ ++ return 0; ++} ++#else /* __linux__ */ ++static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) ++{ ++ return -ENOTSUP; ++} ++#endif ++ ++#if defined(__linux__) ++static int handle_aiocb_ioctl(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int ret; ++ ++ ret = RETRY_ON_EINTR( ++ ioctl(aiocb->aio_fildes, aiocb->ioctl.cmd, aiocb->ioctl.buf) ++ ); ++ if (ret == -1) { ++ return -errno; ++ } ++ ++ return 0; ++} ++#endif /* linux */ ++ ++static int handle_aiocb_flush(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ BDRVRawState *s = aiocb->bs->opaque; ++ int ret; ++ ++ if (s->page_cache_inconsistent) { ++ return -s->page_cache_inconsistent; ++ } ++ ++ ret = qemu_fdatasync(aiocb->aio_fildes); ++ if (ret == -1) { ++ trace_file_flush_fdatasync_failed(errno); ++ ++ /* There is no clear definition of the semantics of a failing fsync(), ++ * so we may have to assume the worst. The sad truth is that this ++ * assumption is correct for Linux. Some pages are now probably marked ++ * clean in the page cache even though they are inconsistent with the ++ * on-disk contents. The next fdatasync() call would succeed, but no ++ * further writeback attempt will be made. We can't get back to a state ++ * in which we know what is on disk (we would have to rewrite ++ * everything that was touched since the last fdatasync() at least), so ++ * make bdrv_flush() fail permanently. Given that the behaviour isn't ++ * really defined, I have little hope that other OSes are doing better. ++ * ++ * Obviously, this doesn't affect O_DIRECT, which bypasses the page ++ * cache. */ ++ if ((s->open_flags & O_DIRECT) == 0) { ++ s->page_cache_inconsistent = errno; ++ } ++ return -errno; ++ } ++ return 0; ++} ++ ++#ifdef CONFIG_PREADV ++ ++static bool preadv_present = true; ++ ++static ssize_t ++qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset) ++{ ++ return preadv(fd, iov, nr_iov, offset); ++} ++ ++static ssize_t ++qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset) ++{ ++ return pwritev(fd, iov, nr_iov, offset); ++} ++ ++#else ++ ++static bool preadv_present = false; ++ ++static ssize_t ++qemu_preadv(int fd, const struct iovec *iov, int nr_iov, off_t offset) ++{ ++ return -ENOSYS; ++} ++ ++static ssize_t ++qemu_pwritev(int fd, const struct iovec *iov, int nr_iov, off_t offset) ++{ ++ return -ENOSYS; ++} ++ ++#endif ++ ++static ssize_t handle_aiocb_rw_vector(RawPosixAIOData *aiocb) ++{ ++ ssize_t len; ++ ++ len = RETRY_ON_EINTR( ++ (aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) ? ++ qemu_pwritev(aiocb->aio_fildes, ++ aiocb->io.iov, ++ aiocb->io.niov, ++ aiocb->aio_offset) : ++ qemu_preadv(aiocb->aio_fildes, ++ aiocb->io.iov, ++ aiocb->io.niov, ++ aiocb->aio_offset) ++ ); ++ ++ if (len == -1) { ++ return -errno; ++ } ++ return len; ++} ++ ++/* ++ * Read/writes the data to/from a given linear buffer. ++ * ++ * Returns the number of bytes handles or -errno in case of an error. Short ++ * reads are only returned if the end of the file is reached. ++ */ ++static ssize_t handle_aiocb_rw_linear(RawPosixAIOData *aiocb, char *buf) ++{ ++ ssize_t offset = 0; ++ ssize_t len; ++ ++ while (offset < aiocb->aio_nbytes) { ++ if (aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) { ++ len = pwrite(aiocb->aio_fildes, ++ (const char *)buf + offset, ++ aiocb->aio_nbytes - offset, ++ aiocb->aio_offset + offset); ++ } else { ++ len = pread(aiocb->aio_fildes, ++ buf + offset, ++ aiocb->aio_nbytes - offset, ++ aiocb->aio_offset + offset); ++ } ++ if (len == -1 && errno == EINTR) { ++ continue; ++ } else if (len == -1 && errno == EINVAL && ++ (aiocb->bs->open_flags & BDRV_O_NOCACHE) && ++ !(aiocb->aio_type & QEMU_AIO_WRITE) && ++ offset > 0) { ++ /* O_DIRECT pread() may fail with EINVAL when offset is unaligned ++ * after a short read. Assume that O_DIRECT short reads only occur ++ * at EOF. Therefore this is a short read, not an I/O error. ++ */ ++ break; ++ } else if (len == -1) { ++ offset = -errno; ++ break; ++ } else if (len == 0) { ++ break; ++ } ++ offset += len; ++ } ++ ++ return offset; ++} ++ ++static int handle_aiocb_rw(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ ssize_t nbytes; ++ char *buf; ++ ++ if (!(aiocb->aio_type & QEMU_AIO_MISALIGNED)) { ++ /* ++ * If there is just a single buffer, and it is properly aligned ++ * we can just use plain pread/pwrite without any problems. ++ */ ++ if (aiocb->io.niov == 1) { ++ nbytes = handle_aiocb_rw_linear(aiocb, aiocb->io.iov->iov_base); ++ goto out; ++ } ++ /* ++ * We have more than one iovec, and all are properly aligned. ++ * ++ * Try preadv/pwritev first and fall back to linearizing the ++ * buffer if it's not supported. ++ */ ++ if (preadv_present) { ++ nbytes = handle_aiocb_rw_vector(aiocb); ++ if (nbytes == aiocb->aio_nbytes || ++ (nbytes < 0 && nbytes != -ENOSYS)) { ++ goto out; ++ } ++ preadv_present = false; ++ } ++ ++ /* ++ * XXX(hch): short read/write. no easy way to handle the reminder ++ * using these interfaces. For now retry using plain ++ * pread/pwrite? ++ */ ++ } ++ ++ /* ++ * Ok, we have to do it the hard way, copy all segments into ++ * a single aligned buffer. ++ */ ++ buf = qemu_try_blockalign(aiocb->bs, aiocb->aio_nbytes); ++ if (buf == NULL) { ++ nbytes = -ENOMEM; ++ goto out; ++ } ++ ++ if (aiocb->aio_type & QEMU_AIO_WRITE) { ++ char *p = buf; ++ int i; ++ ++ for (i = 0; i < aiocb->io.niov; ++i) { ++ memcpy(p, aiocb->io.iov[i].iov_base, aiocb->io.iov[i].iov_len); ++ p += aiocb->io.iov[i].iov_len; ++ } ++ assert(p - buf == aiocb->aio_nbytes); ++ } ++ ++ nbytes = handle_aiocb_rw_linear(aiocb, buf); ++ if (!(aiocb->aio_type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND))) { ++ char *p = buf; ++ size_t count = aiocb->aio_nbytes, copy; ++ int i; ++ ++ for (i = 0; i < aiocb->io.niov && count; ++i) { ++ copy = count; ++ if (copy > aiocb->io.iov[i].iov_len) { ++ copy = aiocb->io.iov[i].iov_len; ++ } ++ memcpy(aiocb->io.iov[i].iov_base, p, copy); ++ assert(count >= copy); ++ p += copy; ++ count -= copy; ++ } ++ assert(count == 0); ++ } ++ qemu_vfree(buf); ++ ++out: ++ if (nbytes == aiocb->aio_nbytes) { ++ return 0; ++ } else if (nbytes >= 0 && nbytes < aiocb->aio_nbytes) { ++ if (aiocb->aio_type & QEMU_AIO_WRITE) { ++ return -EINVAL; ++ } else { ++ iov_memset(aiocb->io.iov, aiocb->io.niov, nbytes, ++ 0, aiocb->aio_nbytes - nbytes); ++ return 0; ++ } ++ } else { ++ assert(nbytes < 0); ++ return nbytes; ++ } ++} ++ ++#if defined(CONFIG_FALLOCATE) || defined(BLKZEROOUT) || defined(BLKDISCARD) ++static int translate_err(int err) ++{ ++ if (err == -ENODEV || err == -ENOSYS || err == -EOPNOTSUPP || ++ err == -ENOTTY) { ++ err = -ENOTSUP; ++ } ++ return err; ++} ++#endif ++ ++#ifdef CONFIG_FALLOCATE ++static int do_fallocate(int fd, int mode, off_t offset, off_t len) ++{ ++ do { ++ if (fallocate(fd, mode, offset, len) == 0) { ++ return 0; ++ } ++ } while (errno == EINTR); ++ return translate_err(-errno); ++} ++#endif ++ ++static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) ++{ ++ int ret = -ENOTSUP; ++ BDRVRawState *s = aiocb->bs->opaque; ++ ++ if (!s->has_write_zeroes) { ++ return -ENOTSUP; ++ } ++ ++#ifdef BLKZEROOUT ++ /* The BLKZEROOUT implementation in the kernel doesn't set ++ * BLKDEV_ZERO_NOFALLBACK, so we can't call this if we have to avoid slow ++ * fallbacks. */ ++ if (!(aiocb->aio_type & QEMU_AIO_NO_FALLBACK)) { ++ do { ++ uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; ++ if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) { ++ return 0; ++ } ++ } while (errno == EINTR); ++ ++ ret = translate_err(-errno); ++ if (ret == -ENOTSUP) { ++ s->has_write_zeroes = false; ++ } ++ } ++#endif ++ ++ return ret; ++} ++ ++static int handle_aiocb_write_zeroes(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++#ifdef CONFIG_FALLOCATE ++ BDRVRawState *s = aiocb->bs->opaque; ++ int64_t len; ++#endif ++ ++ if (aiocb->aio_type & QEMU_AIO_BLKDEV) { ++ return handle_aiocb_write_zeroes_block(aiocb); ++ } ++ ++#ifdef CONFIG_FALLOCATE_ZERO_RANGE ++ if (s->has_write_zeroes) { ++ int ret = do_fallocate(s->fd, FALLOC_FL_ZERO_RANGE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret == -ENOTSUP) { ++ s->has_write_zeroes = false; ++ } else if (ret == 0 || ret != -EINVAL) { ++ return ret; ++ } ++ /* ++ * Note: Some file systems do not like unaligned byte ranges, and ++ * return EINVAL in such a case, though they should not do it according ++ * to the man-page of fallocate(). Thus we simply ignore this return ++ * value and try the other fallbacks instead. ++ */ ++ } ++#endif ++ ++#ifdef CONFIG_FALLOCATE_PUNCH_HOLE ++ if (s->has_discard && s->has_fallocate) { ++ int ret = do_fallocate(s->fd, ++ FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret == 0) { ++ ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret == 0 || ret != -ENOTSUP) { ++ return ret; ++ } ++ s->has_fallocate = false; ++ } else if (ret == -EINVAL) { ++ /* ++ * Some file systems like older versions of GPFS do not like un- ++ * aligned byte ranges, and return EINVAL in such a case, though ++ * they should not do it according to the man-page of fallocate(). ++ * Warn about the bad filesystem and try the final fallback instead. ++ */ ++ warn_report_once("Your file system is misbehaving: " ++ "fallocate(FALLOC_FL_PUNCH_HOLE) returned EINVAL. " ++ "Please report this bug to your file system " ++ "vendor."); ++ } else if (ret != -ENOTSUP) { ++ return ret; ++ } else { ++ s->has_discard = false; ++ } ++ } ++#endif ++ ++#ifdef CONFIG_FALLOCATE ++ /* Last resort: we are trying to extend the file with zeroed data. This ++ * can be done via fallocate(fd, 0) */ ++ len = raw_getlength(aiocb->bs); ++ if (s->has_fallocate && len >= 0 && aiocb->aio_offset >= len) { ++ int ret = do_fallocate(s->fd, 0, aiocb->aio_offset, aiocb->aio_nbytes); ++ if (ret == 0 || ret != -ENOTSUP) { ++ return ret; ++ } ++ s->has_fallocate = false; ++ } ++#endif ++ ++ return -ENOTSUP; ++} ++ ++static int handle_aiocb_write_zeroes_unmap(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ BDRVRawState *s G_GNUC_UNUSED = aiocb->bs->opaque; ++ ++ /* First try to write zeros and unmap at the same time */ ++ ++#ifdef CONFIG_FALLOCATE_PUNCH_HOLE ++ int ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ switch (ret) { ++ case -ENOTSUP: ++ case -EINVAL: ++ case -EBUSY: ++ break; ++ default: ++ return ret; ++ } ++#endif ++ ++ /* If we couldn't manage to unmap while guaranteed that the area reads as ++ * all-zero afterwards, just write zeroes without unmapping */ ++ return handle_aiocb_write_zeroes(aiocb); ++} ++ ++#ifndef HAVE_COPY_FILE_RANGE ++static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, ++ off_t *out_off, size_t len, unsigned int flags) ++{ ++#ifdef __NR_copy_file_range ++ return syscall(__NR_copy_file_range, in_fd, in_off, out_fd, ++ out_off, len, flags); ++#else ++ errno = ENOSYS; ++ return -1; ++#endif ++} ++#endif ++ ++/* ++ * parse_zone - Fill a zone descriptor ++ */ ++#if defined(CONFIG_BLKZONED) ++static inline int parse_zone(struct BlockZoneDescriptor *zone, ++ const struct blk_zone *blkz) { ++ zone->start = blkz->start << BDRV_SECTOR_BITS; ++ zone->length = blkz->len << BDRV_SECTOR_BITS; ++ zone->wp = blkz->wp << BDRV_SECTOR_BITS; ++ ++#ifdef HAVE_BLK_ZONE_REP_CAPACITY ++ zone->cap = blkz->capacity << BDRV_SECTOR_BITS; ++#else ++ zone->cap = blkz->len << BDRV_SECTOR_BITS; ++#endif ++ ++ switch (blkz->type) { ++ case BLK_ZONE_TYPE_SEQWRITE_REQ: ++ zone->type = BLK_ZT_SWR; ++ break; ++ case BLK_ZONE_TYPE_SEQWRITE_PREF: ++ zone->type = BLK_ZT_SWP; ++ break; ++ case BLK_ZONE_TYPE_CONVENTIONAL: ++ zone->type = BLK_ZT_CONV; ++ break; ++ default: ++ error_report("Unsupported zone type: 0x%x", blkz->type); ++ return -ENOTSUP; ++ } ++ ++ switch (blkz->cond) { ++ case BLK_ZONE_COND_NOT_WP: ++ zone->state = BLK_ZS_NOT_WP; ++ break; ++ case BLK_ZONE_COND_EMPTY: ++ zone->state = BLK_ZS_EMPTY; ++ break; ++ case BLK_ZONE_COND_IMP_OPEN: ++ zone->state = BLK_ZS_IOPEN; ++ break; ++ case BLK_ZONE_COND_EXP_OPEN: ++ zone->state = BLK_ZS_EOPEN; ++ break; ++ case BLK_ZONE_COND_CLOSED: ++ zone->state = BLK_ZS_CLOSED; ++ break; ++ case BLK_ZONE_COND_READONLY: ++ zone->state = BLK_ZS_RDONLY; ++ break; ++ case BLK_ZONE_COND_FULL: ++ zone->state = BLK_ZS_FULL; ++ break; ++ case BLK_ZONE_COND_OFFLINE: ++ zone->state = BLK_ZS_OFFLINE; ++ break; ++ default: ++ error_report("Unsupported zone state: 0x%x", blkz->cond); ++ return -ENOTSUP; ++ } ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_BLKZONED) ++static int handle_aiocb_zone_report(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int fd = aiocb->aio_fildes; ++ unsigned int *nr_zones = aiocb->zone_report.nr_zones; ++ BlockZoneDescriptor *zones = aiocb->zone_report.zones; ++ /* zoned block devices use 512-byte sectors */ ++ uint64_t sector = aiocb->aio_offset / 512; ++ ++ struct blk_zone *blkz; ++ size_t rep_size; ++ unsigned int nrz; ++ int ret; ++ unsigned int n = 0, i = 0; ++ ++ nrz = *nr_zones; ++ rep_size = sizeof(struct blk_zone_report) + nrz * sizeof(struct blk_zone); ++ g_autofree struct blk_zone_report *rep = NULL; ++ rep = g_malloc(rep_size); ++ ++ blkz = (struct blk_zone *)(rep + 1); ++ while (n < nrz) { ++ memset(rep, 0, rep_size); ++ rep->sector = sector; ++ rep->nr_zones = nrz - n; ++ ++ do { ++ ret = ioctl(fd, BLKREPORTZONE, rep); ++ } while (ret != 0 && errno == EINTR); ++ if (ret != 0) { ++ error_report("%d: ioctl BLKREPORTZONE at %" PRId64 " failed %d", ++ fd, sector, errno); ++ return -errno; ++ } ++ ++ if (!rep->nr_zones) { ++ break; ++ } ++ ++ for (i = 0; i < rep->nr_zones; i++, n++) { ++ ret = parse_zone(&zones[n], &blkz[i]); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ /* The next report should start after the last zone reported */ ++ sector = blkz[i].start + blkz[i].len; ++ } ++ } ++ ++ *nr_zones = n; ++ return 0; ++} ++#endif ++ ++#if defined(CONFIG_BLKZONED) ++static int handle_aiocb_zone_mgmt(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int fd = aiocb->aio_fildes; ++ uint64_t sector = aiocb->aio_offset / 512; ++ int64_t nr_sectors = aiocb->aio_nbytes / 512; ++ struct blk_zone_range range; ++ int ret; ++ ++ /* Execute the operation */ ++ range.sector = sector; ++ range.nr_sectors = nr_sectors; ++ do { ++ ret = ioctl(fd, aiocb->zone_mgmt.op, &range); ++ } while (ret != 0 && errno == EINTR); ++ ++ return ret < 0 ? -errno : ret; ++} ++#endif ++ ++static int handle_aiocb_copy_range(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ uint64_t bytes = aiocb->aio_nbytes; ++ off_t in_off = aiocb->aio_offset; ++ off_t out_off = aiocb->copy_range.aio_offset2; ++ ++ while (bytes) { ++ ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, ++ aiocb->copy_range.aio_fd2, &out_off, ++ bytes, 0); ++ trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, ++ aiocb->copy_range.aio_fd2, out_off, bytes, ++ 0, ret); ++ if (ret == 0) { ++ /* No progress (e.g. when beyond EOF), let the caller fall back to ++ * buffer I/O. */ ++ return -ENOSPC; ++ } ++ if (ret < 0) { ++ switch (errno) { ++ case ENOSYS: ++ return -ENOTSUP; ++ case EINTR: ++ continue; ++ default: ++ return -errno; ++ } ++ } ++ bytes -= ret; ++ } ++ return 0; ++} ++ ++static int handle_aiocb_discard(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int ret = -ENOTSUP; ++ BDRVRawState *s = aiocb->bs->opaque; ++ ++ if (!s->has_discard) { ++ return -ENOTSUP; ++ } ++ ++ if (aiocb->aio_type & QEMU_AIO_BLKDEV) { ++#ifdef BLKDISCARD ++ do { ++ uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; ++ if (ioctl(aiocb->aio_fildes, BLKDISCARD, range) == 0) { ++ return 0; ++ } ++ } while (errno == EINTR); ++ ++ ret = translate_err(-errno); ++#endif ++ } else { ++#ifdef CONFIG_FALLOCATE_PUNCH_HOLE ++ ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ++ aiocb->aio_offset, aiocb->aio_nbytes); ++ ret = translate_err(ret); ++#elif defined(__APPLE__) && (__MACH__) ++ fpunchhole_t fpunchhole; ++ fpunchhole.fp_flags = 0; ++ fpunchhole.reserved = 0; ++ fpunchhole.fp_offset = aiocb->aio_offset; ++ fpunchhole.fp_length = aiocb->aio_nbytes; ++ if (fcntl(s->fd, F_PUNCHHOLE, &fpunchhole) == -1) { ++ ret = errno == ENODEV ? -ENOTSUP : -errno; ++ } else { ++ ret = 0; ++ } ++#endif ++ } ++ ++ if (ret == -ENOTSUP) { ++ s->has_discard = false; ++ } ++ return ret; ++} ++ ++/* ++ * Help alignment probing by allocating the first block. ++ * ++ * When reading with direct I/O from unallocated area on Gluster backed by XFS, ++ * reading succeeds regardless of request length. In this case we fallback to ++ * safe alignment which is not optimal. Allocating the first block avoids this ++ * fallback. ++ * ++ * fd may be opened with O_DIRECT, but we don't know the buffer alignment or ++ * request alignment, so we use safe values. ++ * ++ * Returns: 0 on success, -errno on failure. Since this is an optimization, ++ * caller may ignore failures. ++ */ ++static int allocate_first_block(int fd, size_t max_size) ++{ ++ size_t write_size = (max_size < MAX_BLOCKSIZE) ++ ? BDRV_SECTOR_SIZE ++ : MAX_BLOCKSIZE; ++ size_t max_align = MAX(MAX_BLOCKSIZE, qemu_real_host_page_size()); ++ void *buf; ++ ssize_t n; ++ int ret; ++ ++ buf = qemu_memalign(max_align, write_size); ++ memset(buf, 0, write_size); ++ ++ n = RETRY_ON_EINTR(pwrite(fd, buf, write_size, 0)); ++ ++ ret = (n == -1) ? -errno : 0; ++ ++ qemu_vfree(buf); ++ return ret; ++} ++ ++static int handle_aiocb_truncate(void *opaque) ++{ ++ RawPosixAIOData *aiocb = opaque; ++ int result = 0; ++ int64_t current_length = 0; ++ char *buf = NULL; ++ struct stat st; ++ int fd = aiocb->aio_fildes; ++ int64_t offset = aiocb->aio_offset; ++ PreallocMode prealloc = aiocb->truncate.prealloc; ++ Error **errp = aiocb->truncate.errp; ++ ++ if (fstat(fd, &st) < 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, "Could not stat file"); ++ return result; ++ } ++ ++ current_length = st.st_size; ++ if (current_length > offset && prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, "Cannot use preallocation for shrinking files"); ++ return -ENOTSUP; ++ } ++ ++ switch (prealloc) { ++#ifdef CONFIG_POSIX_FALLOCATE ++ case PREALLOC_MODE_FALLOC: ++ /* ++ * Truncating before posix_fallocate() makes it about twice slower on ++ * file systems that do not support fallocate(), trying to check if a ++ * block is allocated before allocating it, so don't do that here. ++ */ ++ if (offset != current_length) { ++ result = -posix_fallocate(fd, current_length, ++ offset - current_length); ++ if (result != 0) { ++ /* posix_fallocate() doesn't set errno. */ ++ error_setg_errno(errp, -result, ++ "Could not preallocate new data"); ++ } else if (current_length == 0) { ++ /* ++ * posix_fallocate() uses fallocate() if the filesystem ++ * supports it, or fallback to manually writing zeroes. If ++ * fallocate() was used, unaligned reads from the fallocated ++ * area in raw_probe_alignment() will succeed, hence we need to ++ * allocate the first block. ++ * ++ * Optimize future alignment probing; ignore failures. ++ */ ++ allocate_first_block(fd, offset); ++ } ++ } else { ++ result = 0; ++ } ++ goto out; ++#endif ++ case PREALLOC_MODE_FULL: ++ { ++ int64_t num = 0, left = offset - current_length; ++ off_t seek_result; ++ ++ /* ++ * Knowing the final size from the beginning could allow the file ++ * system driver to do less allocations and possibly avoid ++ * fragmentation of the file. ++ */ ++ if (ftruncate(fd, offset) != 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, "Could not resize file"); ++ goto out; ++ } ++ ++ buf = g_malloc0(65536); ++ ++ seek_result = lseek(fd, current_length, SEEK_SET); ++ if (seek_result < 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, ++ "Failed to seek to the old end of file"); ++ goto out; ++ } ++ ++ while (left > 0) { ++ num = MIN(left, 65536); ++ result = write(fd, buf, num); ++ if (result < 0) { ++ if (errno == EINTR) { ++ continue; ++ } ++ result = -errno; ++ error_setg_errno(errp, -result, ++ "Could not write zeros for preallocation"); ++ goto out; ++ } ++ left -= result; ++ } ++ if (result >= 0) { ++ result = fsync(fd); ++ if (result < 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, ++ "Could not flush file to disk"); ++ goto out; ++ } ++ } ++ goto out; ++ } ++ case PREALLOC_MODE_OFF: ++ if (ftruncate(fd, offset) != 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, "Could not resize file"); ++ } else if (current_length == 0 && offset > current_length) { ++ /* Optimize future alignment probing; ignore failures. */ ++ allocate_first_block(fd, offset); ++ } ++ return result; ++ default: ++ result = -ENOTSUP; ++ error_setg(errp, "Unsupported preallocation mode: %s", ++ PreallocMode_str(prealloc)); ++ return result; ++ } ++ ++out: ++ if (result < 0) { ++ if (ftruncate(fd, current_length) < 0) { ++ error_report("Failed to restore old file length: %s", ++ strerror(errno)); ++ } ++ } ++ ++ g_free(buf); ++ return result; ++} ++ ++static int coroutine_fn raw_thread_pool_submit(ThreadPoolFunc func, void *arg) ++{ ++ return thread_pool_submit_co(func, arg); ++} ++ ++/* ++ * Check if all memory in this vector is sector aligned. ++ */ ++static bool bdrv_qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) ++{ ++ int i; ++ size_t alignment = bdrv_min_mem_align(bs); ++ size_t len = bs->bl.request_alignment; ++ IO_CODE(); ++ ++ for (i = 0; i < qiov->niov; i++) { ++ if ((uintptr_t) qiov->iov[i].iov_base % alignment) { ++ return false; ++ } ++ if (qiov->iov[i].iov_len % len) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++#ifdef CONFIG_LINUX_IO_URING ++static inline bool raw_check_linux_io_uring(BDRVRawState *s) ++{ ++ Error *local_err = NULL; ++ AioContext *ctx; ++ ++ if (!s->use_linux_io_uring) { ++ return false; ++ } ++ ++ ctx = qemu_get_current_aio_context(); ++ if (unlikely(!aio_setup_linux_io_uring(ctx, &local_err))) { ++ error_reportf_err(local_err, "Unable to use linux io_uring, " ++ "falling back to thread pool: "); ++ s->use_linux_io_uring = false; ++ return false; ++ } ++ return true; ++} ++#endif ++ ++#ifdef CONFIG_LINUX_AIO ++static inline bool raw_check_linux_aio(BDRVRawState *s) ++{ ++ Error *local_err = NULL; ++ AioContext *ctx; ++ ++ if (!s->use_linux_aio) { ++ return false; ++ } ++ ++ ctx = qemu_get_current_aio_context(); ++ if (unlikely(!aio_setup_linux_aio(ctx, &local_err))) { ++ error_reportf_err(local_err, "Unable to use Linux AIO, " ++ "falling back to thread pool: "); ++ s->use_linux_aio = false; ++ return false; ++ } ++ return true; ++} ++#endif ++ ++static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, ++ uint64_t bytes, QEMUIOVector *qiov, int type) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int ret; ++ uint64_t offset = *offset_ptr; ++ ++ if (fd_open(bs) < 0) ++ return -EIO; ++#if defined(CONFIG_BLKZONED) ++ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && ++ bs->bl.zoned != BLK_Z_NONE) { ++ qemu_co_mutex_lock(&bs->wps->colock); ++ if (type & QEMU_AIO_ZONE_APPEND) { ++ int index = offset / bs->bl.zone_size; ++ offset = bs->wps->wp[index]; ++ } ++ } ++#endif ++ ++ /* ++ * When using O_DIRECT, the request must be aligned to be able to use ++ * either libaio or io_uring interface. If not fail back to regular thread ++ * pool read/write code which emulates this for us if we ++ * set QEMU_AIO_MISALIGNED. ++ */ ++ if (s->needs_alignment && !bdrv_qiov_is_aligned(bs, qiov)) { ++ type |= QEMU_AIO_MISALIGNED; ++#ifdef CONFIG_LINUX_IO_URING ++ } else if (raw_check_linux_io_uring(s)) { ++ assert(qiov->size == bytes); ++ ret = luring_co_submit(bs, s->fd, offset, qiov, type); ++ goto out; ++#endif ++#ifdef CONFIG_LINUX_AIO ++ } else if (raw_check_linux_aio(s)) { ++ assert(qiov->size == bytes); ++ ret = laio_co_submit(s->fd, offset, qiov, type, ++ s->aio_max_batch); ++ goto out; ++#endif ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = type, ++ .aio_offset = offset, ++ .aio_nbytes = bytes, ++ .io = { ++ .iov = qiov->iov, ++ .niov = qiov->niov, ++ }, ++ }; ++ ++ assert(qiov->size == bytes); ++ ret = raw_thread_pool_submit(handle_aiocb_rw, &acb); ++ goto out; /* Avoid the compiler err of unused label */ ++ ++out: ++#if defined(CONFIG_BLKZONED) ++ if ((type & (QEMU_AIO_WRITE | QEMU_AIO_ZONE_APPEND)) && ++ bs->bl.zoned != BLK_Z_NONE) { ++ BlockZoneWps *wps = bs->wps; ++ if (ret == 0) { ++ uint64_t *wp = &wps->wp[offset / bs->bl.zone_size]; ++ if (!BDRV_ZT_IS_CONV(*wp)) { ++ if (type & QEMU_AIO_ZONE_APPEND) { ++ *offset_ptr = *wp; ++ trace_zbd_zone_append_complete(bs, *offset_ptr ++ >> BDRV_SECTOR_BITS); ++ } ++ /* Advance the wp if needed */ ++ if (offset + bytes > *wp) { ++ *wp = offset + bytes; ++ } ++ } ++ } else { ++ /* ++ * write and append write are not allowed to cross zone boundaries ++ */ ++ update_zones_wp(bs, s->fd, offset, 1); ++ } ++ ++ qemu_co_mutex_unlock(&wps->colock); ++ } ++#endif ++ return ret; ++} ++ ++static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ); ++} ++ ++static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE); ++} ++ ++static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_FLUSH, ++ }; ++ ++#ifdef CONFIG_LINUX_IO_URING ++ if (raw_check_linux_io_uring(s)) { ++ return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH); ++ } ++#endif ++#ifdef CONFIG_LINUX_AIO ++ if (s->has_laio_fdsync && raw_check_linux_aio(s)) { ++ return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0); ++ } ++#endif ++ return raw_thread_pool_submit(handle_aiocb_flush, &acb); ++} ++ ++static void raw_close(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->fd >= 0) { ++#if defined(CONFIG_BLKZONED) ++ g_free(bs->wps); ++#endif ++ qemu_close(s->fd); ++ s->fd = -1; ++ } ++} ++ ++/** ++ * Truncates the given regular file @fd to @offset and, when growing, fills the ++ * new space according to @prealloc. ++ * ++ * Returns: 0 on success, -errno on failure. ++ */ ++static int coroutine_fn ++raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, ++ PreallocMode prealloc, Error **errp) ++{ ++ RawPosixAIOData acb; ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = fd, ++ .aio_type = QEMU_AIO_TRUNCATE, ++ .aio_offset = offset, ++ .truncate = { ++ .prealloc = prealloc, ++ .errp = errp, ++ }, ++ }; ++ ++ return raw_thread_pool_submit(handle_aiocb_truncate, &acb); ++} ++ ++static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, ++ bool exact, PreallocMode prealloc, ++ BdrvRequestFlags flags, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ struct stat st; ++ int ret; ++ ++ if (fstat(s->fd, &st)) { ++ ret = -errno; ++ error_setg_errno(errp, -ret, "Failed to fstat() the file"); ++ return ret; ++ } ++ ++ if (S_ISREG(st.st_mode)) { ++ /* Always resizes to the exact @offset */ ++ return raw_regular_truncate(bs, s->fd, offset, prealloc, errp); ++ } ++ ++ if (prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, "Preallocation mode '%s' unsupported for this " ++ "non-regular file", PreallocMode_str(prealloc)); ++ return -ENOTSUP; ++ } ++ ++ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { ++ int64_t cur_length = raw_getlength(bs); ++ ++ if (offset != cur_length && exact) { ++ error_setg(errp, "Cannot resize device files"); ++ return -ENOTSUP; ++ } else if (offset > cur_length) { ++ error_setg(errp, "Cannot grow device files"); ++ return -EINVAL; ++ } ++ } else { ++ error_setg(errp, "Resizing this file is not supported"); ++ return -ENOTSUP; ++ } ++ ++ return 0; ++} ++ ++#ifdef __OpenBSD__ ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd = s->fd; ++ struct stat st; ++ ++ if (fstat(fd, &st)) ++ return -errno; ++ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { ++ struct disklabel dl; ++ ++ if (ioctl(fd, DIOCGDINFO, &dl)) ++ return -errno; ++ return (uint64_t)dl.d_secsize * ++ dl.d_partitions[DISKPART(st.st_rdev)].p_size; ++ } else ++ return st.st_size; ++} ++#elif defined(__NetBSD__) ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd = s->fd; ++ struct stat st; ++ ++ if (fstat(fd, &st)) ++ return -errno; ++ if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { ++ struct dkwedge_info dkw; ++ ++ if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1) { ++ return dkw.dkw_size * 512; ++ } else { ++ struct disklabel dl; ++ ++ if (ioctl(fd, DIOCGDINFO, &dl)) ++ return -errno; ++ return (uint64_t)dl.d_secsize * ++ dl.d_partitions[DISKPART(st.st_rdev)].p_size; ++ } ++ } else ++ return st.st_size; ++} ++#elif defined(__sun__) ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ struct dk_minfo minfo; ++ int ret; ++ int64_t size; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* ++ * Use the DKIOCGMEDIAINFO ioctl to read the size. ++ */ ++ ret = ioctl(s->fd, DKIOCGMEDIAINFO, &minfo); ++ if (ret != -1) { ++ return minfo.dki_lbsize * minfo.dki_capacity; ++ } ++ ++ /* ++ * There are reports that lseek on some devices fails, but ++ * irc discussion said that contingency on contingency was overkill. ++ */ ++ size = lseek(s->fd, 0, SEEK_END); ++ if (size < 0) { ++ return -errno; ++ } ++ return size; ++} ++#elif defined(CONFIG_BSD) ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd = s->fd; ++ int64_t size; ++ struct stat sb; ++#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) ++ int reopened = 0; ++#endif ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) ++ return ret; ++ ++#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) ++again: ++#endif ++ if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { ++ size = 0; ++#ifdef DIOCGMEDIASIZE ++ if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) { ++ size = 0; ++ } ++#endif ++#ifdef DIOCGPART ++ if (size == 0) { ++ struct partinfo pi; ++ if (ioctl(fd, DIOCGPART, &pi) == 0) { ++ size = pi.media_size; ++ } ++ } ++#endif ++#if defined(DKIOCGETBLOCKCOUNT) && defined(DKIOCGETBLOCKSIZE) ++ if (size == 0) { ++ uint64_t sectors = 0; ++ uint32_t sector_size = 0; ++ ++ if (ioctl(fd, DKIOCGETBLOCKCOUNT, §ors) == 0 ++ && ioctl(fd, DKIOCGETBLOCKSIZE, §or_size) == 0) { ++ size = sectors * sector_size; ++ } ++ } ++#endif ++ if (size == 0) { ++ size = lseek(fd, 0LL, SEEK_END); ++ } ++ if (size < 0) { ++ return -errno; ++ } ++#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ++ switch(s->type) { ++ case FTYPE_CD: ++ /* XXX FreeBSD acd returns UINT_MAX sectors for an empty drive */ ++ if (size == 2048LL * (unsigned)-1) ++ size = 0; ++ /* XXX no disc? maybe we need to reopen... */ ++ if (size <= 0 && !reopened && cdrom_reopen(bs) >= 0) { ++ reopened = 1; ++ goto again; ++ } ++ } ++#endif ++ } else { ++ size = lseek(fd, 0, SEEK_END); ++ if (size < 0) { ++ return -errno; ++ } ++ } ++ return size; ++} ++#else ++static int64_t raw_getlength(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ int64_t size; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ size = lseek(s->fd, 0, SEEK_END); ++ if (size < 0) { ++ return -errno; ++ } ++ return size; ++} ++#endif ++ ++static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs) ++{ ++ return raw_getlength(bs); ++} ++ ++static int64_t coroutine_fn raw_co_get_allocated_file_size(BlockDriverState *bs) ++{ ++ struct stat st; ++ BDRVRawState *s = bs->opaque; ++ ++ if (fstat(s->fd, &st) < 0) { ++ return -errno; ++ } ++ return (int64_t)st.st_blocks * 512; ++} ++ ++static int coroutine_fn ++raw_co_create(BlockdevCreateOptions *options, Error **errp) ++{ ++ BlockdevCreateOptionsFile *file_opts; ++ Error *local_err = NULL; ++ int fd; ++ uint64_t perm, shared; ++ int result = 0; ++ ++ /* Validate options and set default values */ ++ assert(options->driver == BLOCKDEV_DRIVER_FILE); ++ file_opts = &options->u.file; ++ ++ if (!file_opts->has_nocow) { ++ file_opts->nocow = false; ++ } ++ if (!file_opts->has_preallocation) { ++ file_opts->preallocation = PREALLOC_MODE_OFF; ++ } ++ if (!file_opts->has_extent_size_hint) { ++ file_opts->extent_size_hint = 1 * MiB; ++ } ++ if (file_opts->extent_size_hint > UINT32_MAX) { ++ result = -EINVAL; ++ error_setg(errp, "Extent size hint is too large"); ++ goto out; ++ } ++ ++ /* Create file */ ++ fd = qemu_create(file_opts->filename, O_RDWR | O_BINARY, 0644, errp); ++ if (fd < 0) { ++ result = -errno; ++ goto out; ++ } ++ ++ /* Take permissions: We want to discard everything, so we need ++ * BLK_PERM_WRITE; and truncation to the desired size requires ++ * BLK_PERM_RESIZE. ++ * On the other hand, we cannot share the RESIZE permission ++ * because we promise that after this function, the file has the ++ * size given in the options. If someone else were to resize it ++ * concurrently, we could not guarantee that. ++ * Note that after this function, we can no longer guarantee that ++ * the file is not touched by a third party, so it may be resized ++ * then. */ ++ perm = BLK_PERM_WRITE | BLK_PERM_RESIZE; ++ shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; ++ ++ /* Step one: Take locks */ ++ result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp); ++ if (result < 0) { ++ goto out_close; ++ } ++ ++ /* Step two: Check that nobody else has taken conflicting locks */ ++ result = raw_check_lock_bytes(fd, perm, shared, errp); ++ if (result < 0) { ++ error_append_hint(errp, ++ "Is another process using the image [%s]?\n", ++ file_opts->filename); ++ goto out_unlock; ++ } ++ ++ /* Clear the file by truncating it to 0 */ ++ result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp); ++ if (result < 0) { ++ goto out_unlock; ++ } ++ ++ if (file_opts->nocow) { ++#ifdef __linux__ ++ /* Set NOCOW flag to solve performance issue on fs like btrfs. ++ * This is an optimisation. The FS_IOC_SETFLAGS ioctl return value ++ * will be ignored since any failure of this operation should not ++ * block the left work. ++ */ ++ int attr; ++ if (ioctl(fd, FS_IOC_GETFLAGS, &attr) == 0) { ++ attr |= FS_NOCOW_FL; ++ ioctl(fd, FS_IOC_SETFLAGS, &attr); ++ } ++#endif ++ } ++#ifdef FS_IOC_FSSETXATTR ++ /* ++ * Try to set the extent size hint. Failure is not fatal, and a warning is ++ * only printed if the option was explicitly specified. ++ */ ++ { ++ struct fsxattr attr; ++ result = ioctl(fd, FS_IOC_FSGETXATTR, &attr); ++ if (result == 0) { ++ attr.fsx_xflags |= FS_XFLAG_EXTSIZE; ++ attr.fsx_extsize = file_opts->extent_size_hint; ++ result = ioctl(fd, FS_IOC_FSSETXATTR, &attr); ++ } ++ if (result < 0 && file_opts->has_extent_size_hint && ++ file_opts->extent_size_hint) ++ { ++ warn_report("Failed to set extent size hint: %s", ++ strerror(errno)); ++ } ++ } ++#endif ++ ++ /* Resize and potentially preallocate the file to the desired ++ * final size */ ++ result = raw_regular_truncate(NULL, fd, file_opts->size, ++ file_opts->preallocation, errp); ++ if (result < 0) { ++ goto out_unlock; ++ } ++ ++out_unlock: ++ raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err); ++ if (local_err) { ++ /* The above call should not fail, and if it does, that does ++ * not mean the whole creation operation has failed. So ++ * report it the user for their convenience, but do not report ++ * it to the caller. */ ++ warn_report_err(local_err); ++ } ++ ++out_close: ++ if (qemu_close(fd) != 0 && result == 0) { ++ result = -errno; ++ error_setg_errno(errp, -result, "Could not close the new file"); ++ } ++out: ++ return result; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_create_opts(BlockDriver *drv, const char *filename, ++ QemuOpts *opts, Error **errp) ++{ ++ BlockdevCreateOptions options; ++ int64_t total_size = 0; ++ int64_t extent_size_hint = 0; ++ bool has_extent_size_hint = false; ++ bool nocow = false; ++ PreallocMode prealloc; ++ char *buf = NULL; ++ Error *local_err = NULL; ++ ++ /* Skip file: protocol prefix */ ++ strstart(filename, "file:", &filename); ++ ++ /* Read out options */ ++ total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), ++ BDRV_SECTOR_SIZE); ++ if (qemu_opt_get(opts, BLOCK_OPT_EXTENT_SIZE_HINT)) { ++ has_extent_size_hint = true; ++ extent_size_hint = ++ qemu_opt_get_size_del(opts, BLOCK_OPT_EXTENT_SIZE_HINT, -1); ++ } ++ nocow = qemu_opt_get_bool(opts, BLOCK_OPT_NOCOW, false); ++ buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); ++ prealloc = qapi_enum_parse(&PreallocMode_lookup, buf, ++ PREALLOC_MODE_OFF, &local_err); ++ g_free(buf); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ options = (BlockdevCreateOptions) { ++ .driver = BLOCKDEV_DRIVER_FILE, ++ .u.file = { ++ .filename = (char *) filename, ++ .size = total_size, ++ .has_preallocation = true, ++ .preallocation = prealloc, ++ .has_nocow = true, ++ .nocow = nocow, ++ .has_extent_size_hint = has_extent_size_hint, ++ .extent_size_hint = extent_size_hint, ++ }, ++ }; ++ return raw_co_create(&options, errp); ++} ++ ++static int coroutine_fn raw_co_delete_file(BlockDriverState *bs, ++ Error **errp) ++{ ++ struct stat st; ++ int ret; ++ ++ if (!(stat(bs->filename, &st) == 0) || !S_ISREG(st.st_mode)) { ++ error_setg_errno(errp, ENOENT, "%s is not a regular file", ++ bs->filename); ++ return -ENOENT; ++ } ++ ++ ret = unlink(bs->filename); ++ if (ret < 0) { ++ ret = -errno; ++ error_setg_errno(errp, -ret, "Error when deleting file %s", ++ bs->filename); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Find allocation range in @bs around offset @start. ++ * May change underlying file descriptor's file offset. ++ * If @start is not in a hole, store @start in @data, and the ++ * beginning of the next hole in @hole, and return 0. ++ * If @start is in a non-trailing hole, store @start in @hole and the ++ * beginning of the next non-hole in @data, and return 0. ++ * If @start is in a trailing hole or beyond EOF, return -ENXIO. ++ * If we can't find out, return a negative errno other than -ENXIO. ++ */ ++static int find_allocation(BlockDriverState *bs, off_t start, ++ off_t *data, off_t *hole) ++{ ++#if defined SEEK_HOLE && defined SEEK_DATA ++ BDRVRawState *s = bs->opaque; ++ off_t offs; ++ ++ /* ++ * SEEK_DATA cases: ++ * D1. offs == start: start is in data ++ * D2. offs > start: start is in a hole, next data at offs ++ * D3. offs < 0, errno = ENXIO: either start is in a trailing hole ++ * or start is beyond EOF ++ * If the latter happens, the file has been truncated behind ++ * our back since we opened it. All bets are off then. ++ * Treating like a trailing hole is simplest. ++ * D4. offs < 0, errno != ENXIO: we learned nothing ++ */ ++ offs = lseek(s->fd, start, SEEK_DATA); ++ if (offs < 0) { ++ return -errno; /* D3 or D4 */ ++ } ++ ++ if (offs < start) { ++ /* This is not a valid return by lseek(). We are safe to just return ++ * -EIO in this case, and we'll treat it like D4. */ ++ return -EIO; ++ } ++ ++ if (offs > start) { ++ /* D2: in hole, next data at offs */ ++ *hole = start; ++ *data = offs; ++ return 0; ++ } ++ ++ /* D1: in data, end not yet known */ ++ ++ /* ++ * SEEK_HOLE cases: ++ * H1. offs == start: start is in a hole ++ * If this happens here, a hole has been dug behind our back ++ * since the previous lseek(). ++ * H2. offs > start: either start is in data, next hole at offs, ++ * or start is in trailing hole, EOF at offs ++ * Linux treats trailing holes like any other hole: offs == ++ * start. Solaris seeks to EOF instead: offs > start (blech). ++ * If that happens here, a hole has been dug behind our back ++ * since the previous lseek(). ++ * H3. offs < 0, errno = ENXIO: start is beyond EOF ++ * If this happens, the file has been truncated behind our ++ * back since we opened it. Treat it like a trailing hole. ++ * H4. offs < 0, errno != ENXIO: we learned nothing ++ * Pretend we know nothing at all, i.e. "forget" about D1. ++ */ ++ offs = lseek(s->fd, start, SEEK_HOLE); ++ if (offs < 0) { ++ return -errno; /* D1 and (H3 or H4) */ ++ } ++ ++ if (offs < start) { ++ /* This is not a valid return by lseek(). We are safe to just return ++ * -EIO in this case, and we'll treat it like H4. */ ++ return -EIO; ++ } ++ ++ if (offs > start) { ++ /* ++ * D1 and H2: either in data, next hole at offs, or it was in ++ * data but is now in a trailing hole. In the latter case, ++ * all bets are off. Treating it as if it there was data all ++ * the way to EOF is safe, so simply do that. ++ */ ++ *data = start; ++ *hole = offs; ++ return 0; ++ } ++ ++ /* D1 and H1 */ ++ return -EBUSY; ++#else ++ return -ENOTSUP; ++#endif ++} ++ ++/* ++ * Returns the allocation status of the specified offset. ++ * ++ * The block layer guarantees 'offset' and 'bytes' are within bounds. ++ * ++ * 'pnum' is set to the number of bytes (including and immediately following ++ * the specified offset) that are known to be in the same ++ * allocated/unallocated state. ++ * ++ * 'bytes' is a soft cap for 'pnum'. If the information is free, 'pnum' may ++ * well exceed it. ++ */ ++static int coroutine_fn raw_co_block_status(BlockDriverState *bs, ++ bool want_zero, ++ int64_t offset, ++ int64_t bytes, int64_t *pnum, ++ int64_t *map, ++ BlockDriverState **file) ++{ ++ off_t data = 0, hole = 0; ++ int ret; ++ ++ assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment)); ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!want_zero) { ++ *pnum = bytes; ++ *map = offset; ++ *file = bs; ++ return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; ++ } ++ ++ ret = find_allocation(bs, offset, &data, &hole); ++ if (ret == -ENXIO) { ++ /* Trailing hole */ ++ *pnum = bytes; ++ ret = BDRV_BLOCK_ZERO; ++ } else if (ret < 0) { ++ /* No info available, so pretend there are no holes */ ++ *pnum = bytes; ++ ret = BDRV_BLOCK_DATA; ++ } else if (data == offset) { ++ /* On a data extent, compute bytes to the end of the extent, ++ * possibly including a partial sector at EOF. */ ++ *pnum = hole - offset; ++ ++ /* ++ * We are not allowed to return partial sectors, though, so ++ * round up if necessary. ++ */ ++ if (!QEMU_IS_ALIGNED(*pnum, bs->bl.request_alignment)) { ++ int64_t file_length = raw_getlength(bs); ++ if (file_length > 0) { ++ /* Ignore errors, this is just a safeguard */ ++ assert(hole == file_length); ++ } ++ *pnum = ROUND_UP(*pnum, bs->bl.request_alignment); ++ } ++ ++ ret = BDRV_BLOCK_DATA; ++ } else { ++ /* On a hole, compute bytes to the beginning of the next extent. */ ++ assert(hole == offset); ++ *pnum = data - offset; ++ ret = BDRV_BLOCK_ZERO; ++ } ++ *map = offset; ++ *file = bs; ++ return ret | BDRV_BLOCK_OFFSET_VALID; ++} ++ ++#if defined(__linux__) ++/* Verify that the file is not in the page cache */ ++static void check_cache_dropped(BlockDriverState *bs, Error **errp) ++{ ++ const size_t window_size = 128 * 1024 * 1024; ++ BDRVRawState *s = bs->opaque; ++ void *window = NULL; ++ size_t length = 0; ++ unsigned char *vec; ++ size_t page_size; ++ off_t offset; ++ off_t end; ++ ++ /* mincore(2) page status information requires 1 byte per page */ ++ page_size = sysconf(_SC_PAGESIZE); ++ vec = g_malloc(DIV_ROUND_UP(window_size, page_size)); ++ ++ end = raw_getlength(bs); ++ ++ for (offset = 0; offset < end; offset += window_size) { ++ void *new_window; ++ size_t new_length; ++ size_t vec_end; ++ size_t i; ++ int ret; ++ ++ /* Unmap previous window if size has changed */ ++ new_length = MIN(end - offset, window_size); ++ if (new_length != length) { ++ munmap(window, length); ++ window = NULL; ++ length = 0; ++ } ++ ++ new_window = mmap(window, new_length, PROT_NONE, MAP_PRIVATE, ++ s->fd, offset); ++ if (new_window == MAP_FAILED) { ++ error_setg_errno(errp, errno, "mmap failed"); ++ break; ++ } ++ ++ window = new_window; ++ length = new_length; ++ ++ ret = mincore(window, length, vec); ++ if (ret < 0) { ++ error_setg_errno(errp, errno, "mincore failed"); ++ break; ++ } ++ ++ vec_end = DIV_ROUND_UP(length, page_size); ++ for (i = 0; i < vec_end; i++) { ++ if (vec[i] & 0x1) { ++ break; ++ } ++ } ++ if (i < vec_end) { ++ error_setg(errp, "page cache still in use!"); ++ break; ++ } ++ } ++ ++ if (window) { ++ munmap(window, length); ++ } ++ ++ g_free(vec); ++} ++#endif /* __linux__ */ ++ ++static void coroutine_fn GRAPH_RDLOCK ++raw_co_invalidate_cache(BlockDriverState *bs, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "The file descriptor is not open"); ++ return; ++ } ++ ++ if (!s->drop_cache) { ++ return; ++ } ++ ++ if (s->open_flags & O_DIRECT) { ++ return; /* No host kernel page cache */ ++ } ++ ++#if defined(__linux__) ++ /* This sets the scene for the next syscall... */ ++ ret = bdrv_co_flush(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "flush failed"); ++ return; ++ } ++ ++ /* Linux does not invalidate pages that are dirty, locked, or mmapped by a ++ * process. These limitations are okay because we just fsynced the file, ++ * we don't use mmap, and the file should not be in use by other processes. ++ */ ++ ret = posix_fadvise(s->fd, 0, 0, POSIX_FADV_DONTNEED); ++ if (ret != 0) { /* the return value is a positive errno */ ++ error_setg_errno(errp, ret, "fadvise failed"); ++ return; ++ } ++ ++ if (s->check_cache_dropped) { ++ check_cache_dropped(bs, errp); ++ } ++#else /* __linux__ */ ++ /* Do nothing. Live migration to a remote host with cache.direct=off is ++ * unsupported on other host operating systems. Cache consistency issues ++ * may occur but no error is reported here, partly because that's the ++ * historical behavior and partly because it's hard to differentiate valid ++ * configurations that should not cause errors. ++ */ ++#endif /* !__linux__ */ ++} ++ ++static void raw_account_discard(BDRVRawState *s, uint64_t nbytes, int ret) ++{ ++ if (ret) { ++ s->stats.discard_nb_failed++; ++ } else { ++ s->stats.discard_nb_ok++; ++ s->stats.discard_bytes_ok += nbytes; ++ } ++} ++ ++/* ++ * zone report - Get a zone block device's information in the form ++ * of an array of zone descriptors. ++ * zones is an array of zone descriptors to hold zone information on reply; ++ * offset can be any byte within the entire size of the device; ++ * nr_zones is the maximum number of sectors the command should operate on. ++ */ ++#if defined(CONFIG_BLKZONED) ++static int coroutine_fn raw_co_zone_report(BlockDriverState *bs, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones) { ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_ZONE_REPORT, ++ .aio_offset = offset, ++ .zone_report = { ++ .nr_zones = nr_zones, ++ .zones = zones, ++ }, ++ }; ++ ++ trace_zbd_zone_report(bs, *nr_zones, offset >> BDRV_SECTOR_BITS); ++ return raw_thread_pool_submit(handle_aiocb_zone_report, &acb); ++} ++#endif ++ ++/* ++ * zone management operations - Execute an operation on a zone ++ */ ++#if defined(CONFIG_BLKZONED) ++static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, ++ int64_t offset, int64_t len) { ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int64_t zone_size, zone_size_mask; ++ const char *op_name; ++ unsigned long zo; ++ int ret; ++ BlockZoneWps *wps = bs->wps; ++ int64_t capacity = bs->total_sectors << BDRV_SECTOR_BITS; ++ ++ zone_size = bs->bl.zone_size; ++ zone_size_mask = zone_size - 1; ++ if (offset & zone_size_mask) { ++ error_report("sector offset %" PRId64 " is not aligned to zone size " ++ "%" PRId64 "", offset / 512, zone_size / 512); ++ return -EINVAL; ++ } ++ ++ if (((offset + len) < capacity && len & zone_size_mask) || ++ offset + len > capacity) { ++ error_report("number of sectors %" PRId64 " is not aligned to zone size" ++ " %" PRId64 "", len / 512, zone_size / 512); ++ return -EINVAL; ++ } ++ ++ uint32_t i = offset / bs->bl.zone_size; ++ uint32_t nrz = len / bs->bl.zone_size; ++ uint64_t *wp = &wps->wp[i]; ++ if (BDRV_ZT_IS_CONV(*wp) && len != capacity) { ++ error_report("zone mgmt operations are not allowed for conventional zones"); ++ return -EIO; ++ } ++ ++ switch (op) { ++ case BLK_ZO_OPEN: ++ op_name = "BLKOPENZONE"; ++ zo = BLKOPENZONE; ++ break; ++ case BLK_ZO_CLOSE: ++ op_name = "BLKCLOSEZONE"; ++ zo = BLKCLOSEZONE; ++ break; ++ case BLK_ZO_FINISH: ++ op_name = "BLKFINISHZONE"; ++ zo = BLKFINISHZONE; ++ break; ++ case BLK_ZO_RESET: ++ op_name = "BLKRESETZONE"; ++ zo = BLKRESETZONE; ++ break; ++ default: ++ error_report("Unsupported zone op: 0x%x", op); ++ return -ENOTSUP; ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_ZONE_MGMT, ++ .aio_offset = offset, ++ .aio_nbytes = len, ++ .zone_mgmt = { ++ .op = zo, ++ }, ++ }; ++ ++ trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, ++ len >> BDRV_SECTOR_BITS); ++ ret = raw_thread_pool_submit(handle_aiocb_zone_mgmt, &acb); ++ if (ret != 0) { ++ update_zones_wp(bs, s->fd, offset, nrz); ++ error_report("ioctl %s failed %d", op_name, ret); ++ return ret; ++ } ++ ++ if (zo == BLKRESETZONE && len == capacity) { ++ ret = get_zones_wp(bs, s->fd, 0, bs->bl.nr_zones, 1); ++ if (ret < 0) { ++ error_report("reporting single wp failed"); ++ return ret; ++ } ++ } else if (zo == BLKRESETZONE) { ++ for (unsigned int j = 0; j < nrz; ++j) { ++ wp[j] = offset + j * zone_size; ++ } ++ } else if (zo == BLKFINISHZONE) { ++ for (unsigned int j = 0; j < nrz; ++j) { ++ /* The zoned device allows the last zone smaller that the ++ * zone size. */ ++ wp[j] = MIN(offset + (j + 1) * zone_size, offset + len); ++ } ++ } ++ ++ return ret; ++} ++#endif ++ ++#if defined(CONFIG_BLKZONED) ++static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, ++ int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags) { ++ assert(flags == 0); ++ int64_t zone_size_mask = bs->bl.zone_size - 1; ++ int64_t iov_len = 0; ++ int64_t len = 0; ++ ++ if (*offset & zone_size_mask) { ++ error_report("sector offset %" PRId64 " is not aligned to zone size " ++ "%" PRId32 "", *offset / 512, bs->bl.zone_size / 512); ++ return -EINVAL; ++ } ++ ++ int64_t wg = bs->bl.write_granularity; ++ int64_t wg_mask = wg - 1; ++ for (int i = 0; i < qiov->niov; i++) { ++ iov_len = qiov->iov[i].iov_len; ++ if (iov_len & wg_mask) { ++ error_report("len of IOVector[%d] %" PRId64 " is not aligned to " ++ "block size %" PRId64 "", i, iov_len, wg); ++ return -EINVAL; ++ } ++ len += iov_len; ++ } ++ ++ trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); ++ return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND); ++} ++#endif ++ ++static coroutine_fn int ++raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ bool blkdev) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int ret; ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_DISCARD, ++ .aio_offset = offset, ++ .aio_nbytes = bytes, ++ }; ++ ++ if (blkdev) { ++ acb.aio_type |= QEMU_AIO_BLKDEV; ++ } ++ ++ ret = raw_thread_pool_submit(handle_aiocb_discard, &acb); ++ raw_account_discard(s, bytes, ret); ++ return ret; ++} ++ ++static coroutine_fn int ++raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ return raw_do_pdiscard(bs, offset, bytes, false); ++} ++ ++static int coroutine_fn ++raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags, bool blkdev) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ ThreadPoolFunc *handler; ++ ++#ifdef CONFIG_FALLOCATE ++ if (offset + bytes > bs->total_sectors * BDRV_SECTOR_SIZE) { ++ BdrvTrackedRequest *req; ++ ++ /* ++ * This is a workaround for a bug in the Linux XFS driver, ++ * where writes submitted through the AIO interface will be ++ * discarded if they happen beyond a concurrently running ++ * fallocate() that increases the file length (i.e., both the ++ * write and the fallocate() happen beyond the EOF). ++ * ++ * To work around it, we extend the tracked request for this ++ * zero write until INT64_MAX (effectively infinity), and mark ++ * it as serializing. ++ * ++ * We have to enable this workaround for all filesystems and ++ * AIO modes (not just XFS with aio=native), because for ++ * remote filesystems we do not know the host configuration. ++ */ ++ ++ req = bdrv_co_get_self_request(bs); ++ assert(req); ++ assert(req->type == BDRV_TRACKED_WRITE); ++ assert(req->offset <= offset); ++ assert(req->offset + req->bytes >= offset + bytes); ++ ++ req->bytes = BDRV_MAX_LENGTH - req->offset; ++ ++ bdrv_check_request(req->offset, req->bytes, &error_abort); ++ ++ bdrv_make_request_serialising(req, bs->bl.request_alignment); ++ } ++#endif ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_fildes = s->fd, ++ .aio_type = QEMU_AIO_WRITE_ZEROES, ++ .aio_offset = offset, ++ .aio_nbytes = bytes, ++ }; ++ ++ if (blkdev) { ++ acb.aio_type |= QEMU_AIO_BLKDEV; ++ } ++ if (flags & BDRV_REQ_NO_FALLBACK) { ++ acb.aio_type |= QEMU_AIO_NO_FALLBACK; ++ } ++ ++ if (flags & BDRV_REQ_MAY_UNMAP) { ++ acb.aio_type |= QEMU_AIO_DISCARD; ++ handler = handle_aiocb_write_zeroes_unmap; ++ } else { ++ handler = handle_aiocb_write_zeroes; ++ } ++ ++ return raw_thread_pool_submit(handler, &acb); ++} ++ ++static int coroutine_fn raw_co_pwrite_zeroes( ++ BlockDriverState *bs, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags) ++{ ++ return raw_do_pwrite_zeroes(bs, offset, bytes, flags, false); ++} ++ ++static int coroutine_fn ++raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ return 0; ++} ++ ++static ImageInfoSpecific *raw_get_specific_info(BlockDriverState *bs, ++ Error **errp) ++{ ++ ImageInfoSpecificFile *file_info = g_new0(ImageInfoSpecificFile, 1); ++ ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1); ++ ++ *spec_info = (ImageInfoSpecific){ ++ .type = IMAGE_INFO_SPECIFIC_KIND_FILE, ++ .u.file.data = file_info, ++ }; ++ ++#ifdef FS_IOC_FSGETXATTR ++ { ++ BDRVRawState *s = bs->opaque; ++ struct fsxattr attr; ++ int ret; ++ ++ ret = ioctl(s->fd, FS_IOC_FSGETXATTR, &attr); ++ if (!ret && attr.fsx_extsize != 0) { ++ file_info->has_extent_size_hint = true; ++ file_info->extent_size_hint = attr.fsx_extsize; ++ } ++ } ++#endif ++ ++ return spec_info; ++} ++ ++static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ return (BlockStatsSpecificFile) { ++ .discard_nb_ok = s->stats.discard_nb_ok, ++ .discard_nb_failed = s->stats.discard_nb_failed, ++ .discard_bytes_ok = s->stats.discard_bytes_ok, ++ }; ++} ++ ++static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs) ++{ ++ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1); ++ ++ stats->driver = BLOCKDEV_DRIVER_FILE; ++ stats->u.file = get_blockstats_specific_file(bs); ++ ++ return stats; ++} ++ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++static BlockStatsSpecific *hdev_get_specific_stats(BlockDriverState *bs) ++{ ++ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1); ++ ++ stats->driver = BLOCKDEV_DRIVER_HOST_DEVICE; ++ stats->u.host_device = get_blockstats_specific_file(bs); ++ ++ return stats; ++} ++#endif /* HAVE_HOST_BLOCK_DEVICE */ ++ ++static QemuOptsList raw_create_opts = { ++ .name = "raw-create-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), ++ .desc = { ++ { ++ .name = BLOCK_OPT_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Virtual disk size" ++ }, ++ { ++ .name = BLOCK_OPT_NOCOW, ++ .type = QEMU_OPT_BOOL, ++ .help = "Turn off copy-on-write (valid only on btrfs)" ++ }, ++ { ++ .name = BLOCK_OPT_PREALLOC, ++ .type = QEMU_OPT_STRING, ++ .help = "Preallocation mode (allowed values: off" ++#ifdef CONFIG_POSIX_FALLOCATE ++ ", falloc" ++#endif ++ ", full)" ++ }, ++ { ++ .name = BLOCK_OPT_EXTENT_SIZE_HINT, ++ .type = QEMU_OPT_SIZE, ++ .help = "Extent size hint for the image file, 0 to disable" ++ }, ++ { /* end of list */ } ++ } ++}; ++ ++static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int input_flags = s->reopen_state ? s->reopen_state->flags : bs->open_flags; ++ int open_flags; ++ int ret; ++ ++ /* We may need a new fd if auto-read-only switches the mode */ ++ ret = raw_reconfigure_getfd(bs, input_flags, &open_flags, perm, errp); ++ if (ret < 0) { ++ return ret; ++ } else if (ret != s->fd) { ++ Error *local_err = NULL; ++ ++ /* ++ * Fail already check_perm() if we can't get a working O_DIRECT ++ * alignment with the new fd. ++ */ ++ raw_probe_alignment(bs, ret, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return -EINVAL; ++ } ++ ++ s->perm_change_fd = ret; ++ s->perm_change_flags = open_flags; ++ } ++ ++ /* Prepare permissions on old fd to avoid conflicts between old and new, ++ * but keep everything locked that new will need. */ ++ ret = raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Copy locks to the new fd */ ++ if (s->perm_change_fd && s->use_lock) { ++ ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared, ++ false, errp); ++ if (ret < 0) { ++ raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); ++ goto fail; ++ } ++ } ++ return 0; ++ ++fail: ++ if (s->perm_change_fd) { ++ qemu_close(s->perm_change_fd); ++ } ++ s->perm_change_fd = 0; ++ return ret; ++} ++ ++static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ /* For reopen, we have already switched to the new fd (.bdrv_set_perm is ++ * called after .bdrv_reopen_commit) */ ++ if (s->perm_change_fd && s->fd != s->perm_change_fd) { ++ qemu_close(s->fd); ++ s->fd = s->perm_change_fd; ++ s->open_flags = s->perm_change_flags; ++ } ++ s->perm_change_fd = 0; ++ ++ raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL); ++ s->perm = perm; ++ s->shared_perm = shared; ++} ++ ++static void raw_abort_perm_update(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ /* For reopen, .bdrv_reopen_abort is called afterwards and will close ++ * the file descriptor. */ ++ if (s->perm_change_fd) { ++ qemu_close(s->perm_change_fd); ++ } ++ s->perm_change_fd = 0; ++ ++ raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK raw_co_copy_range_from( ++ BlockDriverState *bs, BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, int64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) ++{ ++ return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, ++ read_flags, write_flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ RawPosixAIOData acb; ++ BDRVRawState *s = bs->opaque; ++ BDRVRawState *src_s; ++ ++ assert(dst->bs == bs); ++ if (src->bs->drv->bdrv_co_copy_range_to != raw_co_copy_range_to) { ++ return -ENOTSUP; ++ } ++ ++ src_s = src->bs->opaque; ++ if (fd_open(src->bs) < 0 || fd_open(dst->bs) < 0) { ++ return -EIO; ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_type = QEMU_AIO_COPY_RANGE, ++ .aio_fildes = src_s->fd, ++ .aio_offset = src_offset, ++ .aio_nbytes = bytes, ++ .copy_range = { ++ .aio_fd2 = s->fd, ++ .aio_offset2 = dst_offset, ++ }, ++ }; ++ ++ return raw_thread_pool_submit(handle_aiocb_copy_range, &acb); ++} ++ ++BlockDriver bdrv_file = { ++ .format_name = "file", ++ .protocol_name = "file", ++ .instance_size = sizeof(BDRVRawState), ++ .bdrv_needs_filename = true, ++ .bdrv_probe = NULL, /* no probe for protocols */ ++ .bdrv_parse_filename = raw_parse_filename, ++ .bdrv_open = raw_open, ++ .bdrv_reopen_prepare = raw_reopen_prepare, ++ .bdrv_reopen_commit = raw_reopen_commit, ++ .bdrv_reopen_abort = raw_reopen_abort, ++ .bdrv_close = raw_close, ++ .bdrv_co_create = raw_co_create, ++ .bdrv_co_create_opts = raw_co_create_opts, ++ .bdrv_has_zero_init = bdrv_has_zero_init_1, ++ .bdrv_co_block_status = raw_co_block_status, ++ .bdrv_co_invalidate_cache = raw_co_invalidate_cache, ++ .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, ++ .bdrv_co_delete_file = raw_co_delete_file, ++ ++ .bdrv_co_preadv = raw_co_preadv, ++ .bdrv_co_pwritev = raw_co_pwritev, ++ .bdrv_co_flush_to_disk = raw_co_flush_to_disk, ++ .bdrv_co_pdiscard = raw_co_pdiscard, ++ .bdrv_co_copy_range_from = raw_co_copy_range_from, ++ .bdrv_co_copy_range_to = raw_co_copy_range_to, ++ .bdrv_refresh_limits = raw_refresh_limits, ++ ++ .bdrv_co_truncate = raw_co_truncate, ++ .bdrv_co_getlength = raw_co_getlength, ++ .bdrv_co_get_info = raw_co_get_info, ++ .bdrv_get_specific_info = raw_get_specific_info, ++ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, ++ .bdrv_get_specific_stats = raw_get_specific_stats, ++ .bdrv_check_perm = raw_check_perm, ++ .bdrv_set_perm = raw_set_perm, ++ .bdrv_abort_perm_update = raw_abort_perm_update, ++ .create_opts = &raw_create_opts, ++ .mutable_opts = mutable_opts, ++}; ++ ++/***********************************************/ ++/* host device */ ++ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ ++#if defined(__APPLE__) && defined(__MACH__) ++static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, ++ CFIndex maxPathSize, int flags); ++ ++static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) ++{ ++ kern_return_t kernResult = KERN_FAILURE; ++ mach_port_t mainPort; ++ CFMutableDictionaryRef classesToMatch; ++ const char *matching_array[] = {kIODVDMediaClass, kIOCDMediaClass}; ++ char *mediaType = NULL; ++ ++ kernResult = IOMainPort(MACH_PORT_NULL, &mainPort); ++ if ( KERN_SUCCESS != kernResult ) { ++ printf("IOMainPort returned %d\n", kernResult); ++ } ++ ++ int index; ++ for (index = 0; index < ARRAY_SIZE(matching_array); index++) { ++ classesToMatch = IOServiceMatching(matching_array[index]); ++ if (classesToMatch == NULL) { ++ error_report("IOServiceMatching returned NULL for %s", ++ matching_array[index]); ++ continue; ++ } ++ CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), ++ kCFBooleanTrue); ++ kernResult = IOServiceGetMatchingServices(mainPort, classesToMatch, ++ mediaIterator); ++ if (kernResult != KERN_SUCCESS) { ++ error_report("Note: IOServiceGetMatchingServices returned %d", ++ kernResult); ++ continue; ++ } ++ ++ /* If a match was found, leave the loop */ ++ if (*mediaIterator != 0) { ++ trace_file_FindEjectableOpticalMedia(matching_array[index]); ++ mediaType = g_strdup(matching_array[index]); ++ break; ++ } ++ } ++ return mediaType; ++} ++ ++kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, ++ CFIndex maxPathSize, int flags) ++{ ++ io_object_t nextMedia; ++ kern_return_t kernResult = KERN_FAILURE; ++ *bsdPath = '\0'; ++ nextMedia = IOIteratorNext( mediaIterator ); ++ if ( nextMedia ) ++ { ++ CFTypeRef bsdPathAsCFString; ++ bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 ); ++ if ( bsdPathAsCFString ) { ++ size_t devPathLength; ++ strcpy( bsdPath, _PATH_DEV ); ++ if (flags & BDRV_O_NOCACHE) { ++ strcat(bsdPath, "r"); ++ } ++ devPathLength = strlen( bsdPath ); ++ if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) { ++ kernResult = KERN_SUCCESS; ++ } ++ CFRelease( bsdPathAsCFString ); ++ } ++ IOObjectRelease( nextMedia ); ++ } ++ ++ return kernResult; ++} ++ ++/* Sets up a real cdrom for use in QEMU */ ++static bool setup_cdrom(char *bsd_path, Error **errp) ++{ ++ int index, num_of_test_partitions = 2, fd; ++ char test_partition[MAXPATHLEN]; ++ bool partition_found = false; ++ ++ /* look for a working partition */ ++ for (index = 0; index < num_of_test_partitions; index++) { ++ snprintf(test_partition, sizeof(test_partition), "%ss%d", bsd_path, ++ index); ++ fd = qemu_open(test_partition, O_RDONLY | O_BINARY | O_LARGEFILE, NULL); ++ if (fd >= 0) { ++ partition_found = true; ++ qemu_close(fd); ++ break; ++ } ++ } ++ ++ /* if a working partition on the device was not found */ ++ if (partition_found == false) { ++ error_setg(errp, "Failed to find a working partition on disc"); ++ } else { ++ trace_file_setup_cdrom(test_partition); ++ pstrcpy(bsd_path, MAXPATHLEN, test_partition); ++ } ++ return partition_found; ++} ++ ++/* Prints directions on mounting and unmounting a device */ ++static void print_unmounting_directions(const char *file_name) ++{ ++ error_report("If device %s is mounted on the desktop, unmount" ++ " it first before using it in QEMU", file_name); ++ error_report("Command to unmount device: diskutil unmountDisk %s", ++ file_name); ++ error_report("Command to mount device: diskutil mountDisk %s", file_name); ++} ++ ++#endif /* defined(__APPLE__) && defined(__MACH__) */ ++ ++static int hdev_probe_device(const char *filename) ++{ ++ struct stat st; ++ ++ /* allow a dedicated CD-ROM driver to match with a higher priority */ ++ if (strstart(filename, "/dev/cdrom", NULL)) ++ return 50; ++ ++ if (stat(filename, &st) >= 0 && ++ (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { ++ return 100; ++ } ++ ++ return 0; ++} ++ ++static void hdev_parse_filename(const char *filename, QDict *options, ++ Error **errp) ++{ ++ bdrv_parse_filename_strip_prefix(filename, "host_device:", options); ++} ++ ++static bool hdev_is_sg(BlockDriverState *bs) ++{ ++ ++#if defined(__linux__) ++ ++ BDRVRawState *s = bs->opaque; ++ struct stat st; ++ struct sg_scsi_id scsiid; ++ int sg_version; ++ int ret; ++ ++ if (stat(bs->filename, &st) < 0 || !S_ISCHR(st.st_mode)) { ++ return false; ++ } ++ ++ ret = ioctl(s->fd, SG_GET_VERSION_NUM, &sg_version); ++ if (ret < 0) { ++ return false; ++ } ++ ++ ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); ++ if (ret >= 0) { ++ trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); ++ return true; ++ } ++ ++#endif ++ ++ return false; ++} ++ ++static int hdev_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++#if defined(__APPLE__) && defined(__MACH__) ++ /* ++ * Caution: while qdict_get_str() is fine, getting non-string types ++ * would require more care. When @options come from -blockdev or ++ * blockdev_add, its members are typed according to the QAPI ++ * schema, but when they come from -drive, they're all QString. ++ */ ++ const char *filename = qdict_get_str(options, "filename"); ++ char bsd_path[MAXPATHLEN] = ""; ++ bool error_occurred = false; ++ ++ /* If using a real cdrom */ ++ if (strcmp(filename, "/dev/cdrom") == 0) { ++ char *mediaType = NULL; ++ kern_return_t ret_val; ++ io_iterator_t mediaIterator = 0; ++ ++ mediaType = FindEjectableOpticalMedia(&mediaIterator); ++ if (mediaType == NULL) { ++ error_setg(errp, "Please make sure your CD/DVD is in the optical" ++ " drive"); ++ error_occurred = true; ++ goto hdev_open_Mac_error; ++ } ++ ++ ret_val = GetBSDPath(mediaIterator, bsd_path, sizeof(bsd_path), flags); ++ if (ret_val != KERN_SUCCESS) { ++ error_setg(errp, "Could not get BSD path for optical drive"); ++ error_occurred = true; ++ goto hdev_open_Mac_error; ++ } ++ ++ /* If a real optical drive was not found */ ++ if (bsd_path[0] == '\0') { ++ error_setg(errp, "Failed to obtain bsd path for optical drive"); ++ error_occurred = true; ++ goto hdev_open_Mac_error; ++ } ++ ++ /* If using a cdrom disc and finding a partition on the disc failed */ ++ if (strncmp(mediaType, kIOCDMediaClass, 9) == 0 && ++ setup_cdrom(bsd_path, errp) == false) { ++ print_unmounting_directions(bsd_path); ++ error_occurred = true; ++ goto hdev_open_Mac_error; ++ } ++ ++ qdict_put_str(options, "filename", bsd_path); ++ ++hdev_open_Mac_error: ++ g_free(mediaType); ++ if (mediaIterator) { ++ IOObjectRelease(mediaIterator); ++ } ++ if (error_occurred) { ++ return -ENOENT; ++ } ++ } ++#endif /* defined(__APPLE__) && defined(__MACH__) */ ++ ++ s->type = FTYPE_FILE; ++ ++ ret = raw_open_common(bs, options, flags, 0, true, errp); ++ if (ret < 0) { ++#if defined(__APPLE__) && defined(__MACH__) ++ if (*bsd_path) { ++ filename = bsd_path; ++ } ++ /* if a physical device experienced an error while being opened */ ++ if (strncmp(filename, "/dev/", 5) == 0) { ++ print_unmounting_directions(filename); ++ } ++#endif /* defined(__APPLE__) && defined(__MACH__) */ ++ return ret; ++ } ++ ++ /* Since this does ioctl the device must be already opened */ ++ bs->sg = hdev_is_sg(bs); ++ ++ return ret; ++} ++ ++#if defined(__linux__) ++static int coroutine_fn ++hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) ++{ ++ BDRVRawState *s = bs->opaque; ++ RawPosixAIOData acb; ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (req == SG_IO && s->pr_mgr) { ++ struct sg_io_hdr *io_hdr = buf; ++ if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT || ++ io_hdr->cmdp[0] == PERSISTENT_RESERVE_IN) { ++ return pr_manager_execute(s->pr_mgr, qemu_get_current_aio_context(), ++ s->fd, io_hdr); ++ } ++ } ++ ++ acb = (RawPosixAIOData) { ++ .bs = bs, ++ .aio_type = QEMU_AIO_IOCTL, ++ .aio_fildes = s->fd, ++ .aio_offset = 0, ++ .ioctl = { ++ .buf = buf, ++ .cmd = req, ++ }, ++ }; ++ ++ return raw_thread_pool_submit(handle_aiocb_ioctl, &acb); ++} ++#endif /* linux */ ++ ++static coroutine_fn int ++hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ ret = fd_open(bs); ++ if (ret < 0) { ++ raw_account_discard(s, bytes, ret); ++ return ret; ++ } ++ return raw_do_pdiscard(bs, offset, bytes, true); ++} ++ ++static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, BdrvRequestFlags flags) ++{ ++ int rc; ++ ++ rc = fd_open(bs); ++ if (rc < 0) { ++ return rc; ++ } ++ ++ return raw_do_pwrite_zeroes(bs, offset, bytes, flags, true); ++} ++ ++static BlockDriver bdrv_host_device = { ++ .format_name = "host_device", ++ .protocol_name = "host_device", ++ .instance_size = sizeof(BDRVRawState), ++ .bdrv_needs_filename = true, ++ .bdrv_probe_device = hdev_probe_device, ++ .bdrv_parse_filename = hdev_parse_filename, ++ .bdrv_open = hdev_open, ++ .bdrv_close = raw_close, ++ .bdrv_reopen_prepare = raw_reopen_prepare, ++ .bdrv_reopen_commit = raw_reopen_commit, ++ .bdrv_reopen_abort = raw_reopen_abort, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .mutable_opts = mutable_opts, ++ .bdrv_co_invalidate_cache = raw_co_invalidate_cache, ++ .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, ++ ++ .bdrv_co_preadv = raw_co_preadv, ++ .bdrv_co_pwritev = raw_co_pwritev, ++ .bdrv_co_flush_to_disk = raw_co_flush_to_disk, ++ .bdrv_co_pdiscard = hdev_co_pdiscard, ++ .bdrv_co_copy_range_from = raw_co_copy_range_from, ++ .bdrv_co_copy_range_to = raw_co_copy_range_to, ++ .bdrv_refresh_limits = raw_refresh_limits, ++ ++ .bdrv_co_truncate = raw_co_truncate, ++ .bdrv_co_getlength = raw_co_getlength, ++ .bdrv_co_get_info = raw_co_get_info, ++ .bdrv_get_specific_info = raw_get_specific_info, ++ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, ++ .bdrv_get_specific_stats = hdev_get_specific_stats, ++ .bdrv_check_perm = raw_check_perm, ++ .bdrv_set_perm = raw_set_perm, ++ .bdrv_abort_perm_update = raw_abort_perm_update, ++ .bdrv_probe_blocksizes = hdev_probe_blocksizes, ++ .bdrv_probe_geometry = hdev_probe_geometry, ++ ++ /* generic scsi device */ ++#ifdef __linux__ ++ .bdrv_co_ioctl = hdev_co_ioctl, ++#endif ++ ++ /* zoned device */ ++#if defined(CONFIG_BLKZONED) ++ /* zone management operations */ ++ .bdrv_co_zone_report = raw_co_zone_report, ++ .bdrv_co_zone_mgmt = raw_co_zone_mgmt, ++ .bdrv_co_zone_append = raw_co_zone_append, ++#endif ++}; ++ ++#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ++static void cdrom_parse_filename(const char *filename, QDict *options, ++ Error **errp) ++{ ++ bdrv_parse_filename_strip_prefix(filename, "host_cdrom:", options); ++} ++ ++static void cdrom_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ bs->bl.has_variable_length = true; ++ raw_refresh_limits(bs, errp); ++} ++#endif ++ ++#ifdef __linux__ ++static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ s->type = FTYPE_CD; ++ ++ /* open will not fail even if no CD is inserted, so add O_NONBLOCK */ ++ return raw_open_common(bs, options, flags, O_NONBLOCK, true, errp); ++} ++ ++static int cdrom_probe_device(const char *filename) ++{ ++ int fd, ret; ++ int prio = 0; ++ struct stat st; ++ ++ fd = qemu_open(filename, O_RDONLY | O_NONBLOCK, NULL); ++ if (fd < 0) { ++ goto out; ++ } ++ ret = fstat(fd, &st); ++ if (ret == -1 || !S_ISBLK(st.st_mode)) { ++ goto outc; ++ } ++ ++ /* Attempt to detect via a CDROM specific ioctl */ ++ ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); ++ if (ret >= 0) ++ prio = 100; ++ ++outc: ++ qemu_close(fd); ++out: ++ return prio; ++} ++ ++static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); ++ return ret == CDS_DISC_OK; ++} ++ ++static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (eject_flag) { ++ if (ioctl(s->fd, CDROMEJECT, NULL) < 0) ++ perror("CDROMEJECT"); ++ } else { ++ if (ioctl(s->fd, CDROMCLOSETRAY, NULL) < 0) ++ perror("CDROMEJECT"); ++ } ++} ++ ++static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (ioctl(s->fd, CDROM_LOCKDOOR, locked) < 0) { ++ /* ++ * Note: an error can happen if the distribution automatically ++ * mounts the CD-ROM ++ */ ++ /* perror("CDROM_LOCKDOOR"); */ ++ } ++} ++ ++static BlockDriver bdrv_host_cdrom = { ++ .format_name = "host_cdrom", ++ .protocol_name = "host_cdrom", ++ .instance_size = sizeof(BDRVRawState), ++ .bdrv_needs_filename = true, ++ .bdrv_probe_device = cdrom_probe_device, ++ .bdrv_parse_filename = cdrom_parse_filename, ++ .bdrv_open = cdrom_open, ++ .bdrv_close = raw_close, ++ .bdrv_reopen_prepare = raw_reopen_prepare, ++ .bdrv_reopen_commit = raw_reopen_commit, ++ .bdrv_reopen_abort = raw_reopen_abort, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .mutable_opts = mutable_opts, ++ .bdrv_co_invalidate_cache = raw_co_invalidate_cache, ++ ++ .bdrv_co_preadv = raw_co_preadv, ++ .bdrv_co_pwritev = raw_co_pwritev, ++ .bdrv_co_flush_to_disk = raw_co_flush_to_disk, ++ .bdrv_refresh_limits = cdrom_refresh_limits, ++ ++ .bdrv_co_truncate = raw_co_truncate, ++ .bdrv_co_getlength = raw_co_getlength, ++ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, ++ ++ /* removable device support */ ++ .bdrv_co_is_inserted = cdrom_co_is_inserted, ++ .bdrv_co_eject = cdrom_co_eject, ++ .bdrv_co_lock_medium = cdrom_co_lock_medium, ++ ++ /* generic scsi device */ ++ .bdrv_co_ioctl = hdev_co_ioctl, ++}; ++#endif /* __linux__ */ ++ ++#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) ++static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ s->type = FTYPE_CD; ++ ++ ret = raw_open_common(bs, options, flags, 0, true, errp); ++ if (ret) { ++ return ret; ++ } ++ ++ /* make sure the door isn't locked at this time */ ++ ioctl(s->fd, CDIOCALLOW); ++ return 0; ++} ++ ++static int cdrom_probe_device(const char *filename) ++{ ++ if (strstart(filename, "/dev/cd", NULL) || ++ strstart(filename, "/dev/acd", NULL)) ++ return 100; ++ return 0; ++} ++ ++static int cdrom_reopen(BlockDriverState *bs) ++{ ++ BDRVRawState *s = bs->opaque; ++ int fd; ++ ++ /* ++ * Force reread of possibly changed/newly loaded disc, ++ * FreeBSD seems to not notice sometimes... ++ */ ++ if (s->fd >= 0) ++ qemu_close(s->fd); ++ fd = qemu_open(bs->filename, s->open_flags, NULL); ++ if (fd < 0) { ++ s->fd = -1; ++ return -EIO; ++ } ++ s->fd = fd; ++ ++ /* make sure the door isn't locked at this time */ ++ ioctl(s->fd, CDIOCALLOW); ++ return 0; ++} ++ ++static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs) ++{ ++ return raw_getlength(bs) > 0; ++} ++ ++static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->fd < 0) ++ return; ++ ++ (void) ioctl(s->fd, CDIOCALLOW); ++ ++ if (eject_flag) { ++ if (ioctl(s->fd, CDIOCEJECT) < 0) ++ perror("CDIOCEJECT"); ++ } else { ++ if (ioctl(s->fd, CDIOCCLOSE) < 0) ++ perror("CDIOCCLOSE"); ++ } ++ ++ cdrom_reopen(bs); ++} ++ ++static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->fd < 0) ++ return; ++ if (ioctl(s->fd, (locked ? CDIOCPREVENT : CDIOCALLOW)) < 0) { ++ /* ++ * Note: an error can happen if the distribution automatically ++ * mounts the CD-ROM ++ */ ++ /* perror("CDROM_LOCKDOOR"); */ ++ } ++} ++ ++static BlockDriver bdrv_host_cdrom = { ++ .format_name = "host_cdrom", ++ .protocol_name = "host_cdrom", ++ .instance_size = sizeof(BDRVRawState), ++ .bdrv_needs_filename = true, ++ .bdrv_probe_device = cdrom_probe_device, ++ .bdrv_parse_filename = cdrom_parse_filename, ++ .bdrv_open = cdrom_open, ++ .bdrv_close = raw_close, ++ .bdrv_reopen_prepare = raw_reopen_prepare, ++ .bdrv_reopen_commit = raw_reopen_commit, ++ .bdrv_reopen_abort = raw_reopen_abort, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .mutable_opts = mutable_opts, ++ ++ .bdrv_co_preadv = raw_co_preadv, ++ .bdrv_co_pwritev = raw_co_pwritev, ++ .bdrv_co_flush_to_disk = raw_co_flush_to_disk, ++ .bdrv_refresh_limits = cdrom_refresh_limits, ++ ++ .bdrv_co_truncate = raw_co_truncate, ++ .bdrv_co_getlength = raw_co_getlength, ++ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size, ++ ++ /* removable device support */ ++ .bdrv_co_is_inserted = cdrom_co_is_inserted, ++ .bdrv_co_eject = cdrom_co_eject, ++ .bdrv_co_lock_medium = cdrom_co_lock_medium, ++}; ++#endif /* __FreeBSD__ */ ++ ++#endif /* HAVE_HOST_BLOCK_DEVICE */ ++ ++static void bdrv_file_init(void) ++{ ++ /* ++ * Register all the drivers. Note that order is important, the driver ++ * registered last will get probed first. ++ */ ++ bdrv_register(&bdrv_file); ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ bdrv_register(&bdrv_host_device); ++#ifdef __linux__ ++ bdrv_register(&bdrv_host_cdrom); ++#endif ++#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) ++ bdrv_register(&bdrv_host_cdrom); ++#endif ++#endif /* HAVE_HOST_BLOCK_DEVICE */ ++} ++ ++block_init(bdrv_file_init); +diff --git a/qcow2/lib/block/graph-lock.c b/qcow2/lib/block/graph-lock.c +new file mode 100644 +index 00000000..c81162b1 +--- /dev/null ++++ b/qcow2/lib/block/graph-lock.c +@@ -0,0 +1,281 @@ ++/* ++ * Graph lock: rwlock to protect block layer graph manipulations (add/remove ++ * edges and nodes) ++ * ++ * Copyright (c) 2022 Red Hat ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/main-loop.h" ++#include "block/graph-lock.h" ++#include "block/block.h" ++#include "block/block_int.h" ++ ++/* Dummy lock object to use for Thread Safety Analysis (TSA) */ ++BdrvGraphLock graph_lock; ++ ++/* Protects the list of aiocontext and orphaned_reader_count */ ++static QemuMutex aio_context_list_lock; ++ ++/* Written and read with atomic operations. */ ++static int has_writer; ++ ++/* ++ * A reader coroutine could move from an AioContext to another. ++ * If this happens, there is no problem from the point of view of ++ * counters. The problem is that the total count becomes ++ * unbalanced if one of the two AioContexts gets deleted. ++ * The count of readers must remain correct, so the AioContext's ++ * balance is transferred to this glboal variable. ++ * Protected by aio_context_list_lock. ++ */ ++static uint32_t orphaned_reader_count; ++ ++/* Queue of readers waiting for the writer to finish */ ++static CoQueue reader_queue; ++ ++struct BdrvGraphRWlock { ++ /* How many readers are currently reading the graph. */ ++ uint32_t reader_count; ++ ++ /* ++ * List of BdrvGraphRWlock kept in graph-lock.c ++ * Protected by aio_context_list_lock ++ */ ++ QTAILQ_ENTRY(BdrvGraphRWlock) next_aio; ++}; ++ ++/* ++ * List of BdrvGraphRWlock. This list ensures that each BdrvGraphRWlock ++ * can safely modify only its own counter, avoid reading/writing ++ * others and thus improving performances by avoiding cacheline bounces. ++ */ ++static QTAILQ_HEAD(, BdrvGraphRWlock) aio_context_list = ++ QTAILQ_HEAD_INITIALIZER(aio_context_list); ++ ++static void __attribute__((__constructor__)) bdrv_init_graph_lock(void) ++{ ++ qemu_mutex_init(&aio_context_list_lock); ++ qemu_co_queue_init(&reader_queue); ++} ++ ++void register_aiocontext(AioContext *ctx) ++{ ++ ctx->bdrv_graph = g_new0(BdrvGraphRWlock, 1); ++ QEMU_LOCK_GUARD(&aio_context_list_lock); ++ assert(ctx->bdrv_graph->reader_count == 0); ++ QTAILQ_INSERT_TAIL(&aio_context_list, ctx->bdrv_graph, next_aio); ++} ++ ++void unregister_aiocontext(AioContext *ctx) ++{ ++ QEMU_LOCK_GUARD(&aio_context_list_lock); ++ orphaned_reader_count += ctx->bdrv_graph->reader_count; ++ QTAILQ_REMOVE(&aio_context_list, ctx->bdrv_graph, next_aio); ++ g_free(ctx->bdrv_graph); ++} ++ ++static uint32_t reader_count(void) ++{ ++ BdrvGraphRWlock *brdv_graph; ++ uint32_t rd; ++ ++ QEMU_LOCK_GUARD(&aio_context_list_lock); ++ ++ /* rd can temporarily be negative, but the total will *always* be >= 0 */ ++ rd = orphaned_reader_count; ++ QTAILQ_FOREACH(brdv_graph, &aio_context_list, next_aio) { ++ rd += qatomic_read(&brdv_graph->reader_count); ++ } ++ ++ /* shouldn't overflow unless there are 2^31 readers */ ++ assert((int32_t)rd >= 0); ++ return rd; ++} ++ ++void no_coroutine_fn bdrv_graph_wrlock(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(!qatomic_read(&has_writer)); ++ assert(!qemu_in_coroutine()); ++ ++ /* Make sure that constantly arriving new I/O doesn't cause starvation */ ++ bdrv_drain_all_begin_nopoll(); ++ ++ /* ++ * reader_count == 0: this means writer will read has_reader as 1 ++ * reader_count >= 1: we don't know if writer read has_writer == 0 or 1, ++ * but we need to wait. ++ * Wait by allowing other coroutine (and possible readers) to continue. ++ */ ++ do { ++ /* ++ * has_writer must be 0 while polling, otherwise we get a deadlock if ++ * any callback involved during AIO_WAIT_WHILE() tries to acquire the ++ * reader lock. ++ */ ++ qatomic_set(&has_writer, 0); ++ AIO_WAIT_WHILE_UNLOCKED(NULL, reader_count() >= 1); ++ qatomic_set(&has_writer, 1); ++ ++ /* ++ * We want to only check reader_count() after has_writer = 1 is visible ++ * to other threads. That way no more readers can sneak in after we've ++ * determined reader_count() == 0. ++ */ ++ smp_mb(); ++ } while (reader_count() >= 1); ++ ++ bdrv_drain_all_end(); ++} ++ ++void no_coroutine_fn bdrv_graph_wrunlock(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(qatomic_read(&has_writer)); ++ ++ WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) { ++ /* ++ * No need for memory barriers, this works in pair with ++ * the slow path of rdlock() and both take the lock. ++ */ ++ qatomic_store_release(&has_writer, 0); ++ ++ /* Wake up all coroutines that are waiting to read the graph */ ++ qemu_co_enter_all(&reader_queue, &aio_context_list_lock); ++ } ++ ++ /* ++ * Run any BHs that were scheduled during the wrlock section and that ++ * callers might expect to have finished (in particular, this is important ++ * for bdrv_schedule_unref()). ++ * ++ * Do this only after restarting coroutines so that nested event loops in ++ * BHs don't deadlock if their condition relies on the coroutine making ++ * progress. ++ */ ++ aio_bh_poll(qemu_get_aio_context()); ++} ++ ++void coroutine_fn bdrv_graph_co_rdlock(void) ++{ ++ BdrvGraphRWlock *bdrv_graph; ++ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph; ++ ++ for (;;) { ++ qatomic_set(&bdrv_graph->reader_count, ++ bdrv_graph->reader_count + 1); ++ /* make sure writer sees reader_count before we check has_writer */ ++ smp_mb(); ++ ++ /* ++ * has_writer == 0: this means writer will read reader_count as >= 1 ++ * has_writer == 1: we don't know if writer read reader_count == 0 ++ * or > 0, but we need to wait anyways because ++ * it will write. ++ */ ++ if (!qatomic_read(&has_writer)) { ++ break; ++ } ++ ++ /* ++ * Synchronize access with reader_count() in bdrv_graph_wrlock(). ++ * Case 1: ++ * If this critical section gets executed first, reader_count will ++ * decrease and the reader will go to sleep. ++ * Then the writer will read reader_count that does not take into ++ * account this reader, and if there's no other reader it will ++ * enter the write section. ++ * Case 2: ++ * If reader_count() critical section gets executed first, ++ * then writer will read reader_count >= 1. ++ * It will wait in AIO_WAIT_WHILE(), but once it releases the lock ++ * we will enter this critical section and call aio_wait_kick(). ++ */ ++ WITH_QEMU_LOCK_GUARD(&aio_context_list_lock) { ++ /* ++ * Additional check when we use the above lock to synchronize ++ * with bdrv_graph_wrunlock(). ++ * Case 1: ++ * If this gets executed first, has_writer is still 1, so we reduce ++ * reader_count and go to sleep. ++ * Then the writer will set has_writer to 0 and wake up all readers, ++ * us included. ++ * Case 2: ++ * If bdrv_graph_wrunlock() critical section gets executed first, ++ * then it will set has_writer to 0 and wake up all other readers. ++ * Then we execute this critical section, and therefore must check ++ * again for has_writer, otherwise we sleep without any writer ++ * actually running. ++ */ ++ if (!qatomic_read(&has_writer)) { ++ return; ++ } ++ ++ /* slow path where reader sleeps */ ++ bdrv_graph->reader_count--; ++ aio_wait_kick(); ++ qemu_co_queue_wait(&reader_queue, &aio_context_list_lock); ++ } ++ } ++} ++ ++void coroutine_fn bdrv_graph_co_rdunlock(void) ++{ ++ BdrvGraphRWlock *bdrv_graph; ++ bdrv_graph = qemu_get_current_aio_context()->bdrv_graph; ++ ++ qatomic_store_release(&bdrv_graph->reader_count, ++ bdrv_graph->reader_count - 1); ++ /* make sure writer sees reader_count before we check has_writer */ ++ smp_mb(); ++ ++ /* ++ * has_writer == 0: this means reader will read reader_count decreased ++ * has_writer == 1: we don't know if writer read reader_count old or ++ * new. Therefore, kick again so on next iteration ++ * writer will for sure read the updated value. ++ */ ++ if (qatomic_read(&has_writer)) { ++ aio_wait_kick(); ++ } ++} ++ ++void bdrv_graph_rdlock_main_loop(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(!qemu_in_coroutine()); ++} ++ ++void bdrv_graph_rdunlock_main_loop(void) ++{ ++ GLOBAL_STATE_CODE(); ++ assert(!qemu_in_coroutine()); ++} ++ ++void assert_bdrv_graph_readable(void) ++{ ++ /* reader_count() is slow due to aio_context_list_lock lock contention */ ++#ifdef CONFIG_DEBUG_GRAPH_LOCK ++ assert(qemu_in_main_thread() || reader_count()); ++#endif ++} ++ ++void assert_bdrv_graph_writable(void) ++{ ++ assert(qemu_in_main_thread()); ++ assert(qatomic_read(&has_writer)); ++} +diff --git a/qcow2/lib/block/io.c b/qcow2/lib/block/io.c +new file mode 100644 +index 00000000..301514c8 +--- /dev/null ++++ b/qcow2/lib/block/io.c +@@ -0,0 +1,3755 @@ ++/* ++ * Block layer I/O functions ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "trace.h" ++#include "sysemu/block-backend.h" ++#include "block/aio-wait.h" ++#include "block/blockjob.h" ++#include "block/blockjob_int.h" ++#include "block/block_int.h" ++#include "block/coroutines.h" ++#include "block/dirty-bitmap.h" ++#include "block/write-threshold.h" ++#include "qemu/cutils.h" ++#include "qemu/memalign.h" ++#include "qapi/error.h" ++#include "qemu/error-report.h" ++#include "qemu/main-loop.h" ++#include "sysemu/replay.h" ++ ++/* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ ++#define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) ++ ++static void coroutine_fn GRAPH_RDLOCK ++bdrv_parent_cb_resize(BlockDriverState *bs); ++ ++static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, BdrvRequestFlags flags); ++ ++static void GRAPH_RDLOCK ++bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) ++{ ++ BdrvChild *c, *next; ++ IO_OR_GS_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { ++ if (c == ignore) { ++ continue; ++ } ++ bdrv_parent_drained_begin_single(c); ++ } ++} ++ ++void bdrv_parent_drained_end_single(BdrvChild *c) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ assert(c->quiesced_parent); ++ c->quiesced_parent = false; ++ ++ if (c->klass->drained_end) { ++ c->klass->drained_end(c); ++ } ++} ++ ++static void GRAPH_RDLOCK ++bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) ++{ ++ BdrvChild *c; ++ IO_OR_GS_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c == ignore) { ++ continue; ++ } ++ bdrv_parent_drained_end_single(c); ++ } ++} ++ ++bool bdrv_parent_drained_poll_single(BdrvChild *c) ++{ ++ IO_OR_GS_CODE(); ++ ++ if (c->klass->drained_poll) { ++ return c->klass->drained_poll(c); ++ } ++ return false; ++} ++ ++static bool GRAPH_RDLOCK ++bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, ++ bool ignore_bds_parents) ++{ ++ BdrvChild *c, *next; ++ bool busy = false; ++ IO_OR_GS_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { ++ if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { ++ continue; ++ } ++ busy |= bdrv_parent_drained_poll_single(c); ++ } ++ ++ return busy; ++} ++ ++void bdrv_parent_drained_begin_single(BdrvChild *c) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ assert(!c->quiesced_parent); ++ c->quiesced_parent = true; ++ ++ if (c->klass->drained_begin) { ++ /* called with rdlock taken, but it doesn't really need it. */ ++ c->klass->drained_begin(c); ++ } ++} ++ ++static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) ++{ ++ dst->pdiscard_alignment = MAX(dst->pdiscard_alignment, ++ src->pdiscard_alignment); ++ dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); ++ dst->max_transfer = MIN_NON_ZERO(dst->max_transfer, src->max_transfer); ++ dst->max_hw_transfer = MIN_NON_ZERO(dst->max_hw_transfer, ++ src->max_hw_transfer); ++ dst->opt_mem_alignment = MAX(dst->opt_mem_alignment, ++ src->opt_mem_alignment); ++ dst->min_mem_alignment = MAX(dst->min_mem_alignment, ++ src->min_mem_alignment); ++ dst->max_iov = MIN_NON_ZERO(dst->max_iov, src->max_iov); ++ dst->max_hw_iov = MIN_NON_ZERO(dst->max_hw_iov, src->max_hw_iov); ++} ++ ++typedef struct BdrvRefreshLimitsState { ++ BlockDriverState *bs; ++ BlockLimits old_bl; ++} BdrvRefreshLimitsState; ++ ++static void bdrv_refresh_limits_abort(void *opaque) ++{ ++ BdrvRefreshLimitsState *s = opaque; ++ ++ s->bs->bl = s->old_bl; ++} ++ ++static TransactionActionDrv bdrv_refresh_limits_drv = { ++ .abort = bdrv_refresh_limits_abort, ++ .clean = g_free, ++}; ++ ++/* @tran is allowed to be NULL, in this case no rollback is possible. */ ++void bdrv_refresh_limits(BlockDriverState *bs, Transaction *tran, Error **errp) ++{ ++ ERRP_GUARD(); ++ BlockDriver *drv = bs->drv; ++ BdrvChild *c; ++ bool have_limits; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (tran) { ++ BdrvRefreshLimitsState *s = g_new(BdrvRefreshLimitsState, 1); ++ *s = (BdrvRefreshLimitsState) { ++ .bs = bs, ++ .old_bl = bs->bl, ++ }; ++ tran_add(tran, &bdrv_refresh_limits_drv, s); ++ } ++ ++ memset(&bs->bl, 0, sizeof(bs->bl)); ++ ++ if (!drv) { ++ return; ++ } ++ ++ /* Default alignment based on whether driver has byte interface */ ++ bs->bl.request_alignment = (drv->bdrv_co_preadv || ++ drv->bdrv_aio_preadv || ++ drv->bdrv_co_preadv_part) ? 1 : 512; ++ ++ /* Take some limits from the children as a default */ ++ have_limits = false; ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (c->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED | BDRV_CHILD_COW)) ++ { ++ bdrv_merge_limits(&bs->bl, &c->bs->bl); ++ have_limits = true; ++ } ++ ++ if (c->role & BDRV_CHILD_FILTERED) { ++ bs->bl.has_variable_length |= c->bs->bl.has_variable_length; ++ } ++ } ++ ++ if (!have_limits) { ++ bs->bl.min_mem_alignment = 512; ++ bs->bl.opt_mem_alignment = qemu_real_host_page_size(); ++ ++ /* Safe default since most protocols use readv()/writev()/etc */ ++ bs->bl.max_iov = IOV_MAX; ++ } ++ ++ /* Then let the driver override it */ ++ if (drv->bdrv_refresh_limits) { ++ drv->bdrv_refresh_limits(bs, errp); ++ if (*errp) { ++ return; ++ } ++ } ++ ++ if (bs->bl.request_alignment > BDRV_MAX_ALIGNMENT) { ++ error_setg(errp, "Driver requires too large request alignment"); ++ } ++} ++ ++/** ++ * The copy-on-read flag is actually a reference count so multiple users may ++ * use the feature without worrying about clobbering its previous state. ++ * Copy-on-read stays enabled until all users have called to disable it. ++ */ ++void bdrv_enable_copy_on_read(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ qatomic_inc(&bs->copy_on_read); ++} ++ ++void bdrv_disable_copy_on_read(BlockDriverState *bs) ++{ ++ int old = qatomic_fetch_dec(&bs->copy_on_read); ++ IO_CODE(); ++ assert(old >= 1); ++} ++ ++typedef struct { ++ Coroutine *co; ++ BlockDriverState *bs; ++ bool done; ++ bool begin; ++ bool poll; ++ BdrvChild *parent; ++} BdrvCoDrainData; ++ ++/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ ++bool bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, ++ bool ignore_bds_parents) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) { ++ return true; ++ } ++ ++ if (qatomic_read(&bs->in_flight)) { ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool bdrv_drain_poll_top_level(BlockDriverState *bs, ++ BdrvChild *ignore_parent) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ return bdrv_drain_poll(bs, ignore_parent, false); ++} ++ ++static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, ++ bool poll); ++static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent); ++ ++static void bdrv_co_drain_bh_cb(void *opaque) ++{ ++ BdrvCoDrainData *data = opaque; ++ Coroutine *co = data->co; ++ BlockDriverState *bs = data->bs; ++ ++ if (bs) { ++ bdrv_dec_in_flight(bs); ++ if (data->begin) { ++ bdrv_do_drained_begin(bs, data->parent, data->poll); ++ } else { ++ assert(!data->poll); ++ bdrv_do_drained_end(bs, data->parent); ++ } ++ } else { ++ assert(data->begin); ++ bdrv_drain_all_begin(); ++ } ++ ++ data->done = true; ++ aio_co_wake(co); ++} ++ ++static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, ++ bool begin, ++ BdrvChild *parent, ++ bool poll) ++{ ++ BdrvCoDrainData data; ++ Coroutine *self = qemu_coroutine_self(); ++ ++ /* Calling bdrv_drain() from a BH ensures the current coroutine yields and ++ * other coroutines run if they were queued by aio_co_enter(). */ ++ ++ assert(qemu_in_coroutine()); ++ data = (BdrvCoDrainData) { ++ .co = self, ++ .bs = bs, ++ .done = false, ++ .begin = begin, ++ .parent = parent, ++ .poll = poll, ++ }; ++ ++ if (bs) { ++ bdrv_inc_in_flight(bs); ++ } ++ ++ replay_bh_schedule_oneshot_event(qemu_get_aio_context(), ++ bdrv_co_drain_bh_cb, &data); ++ ++ qemu_coroutine_yield(); ++ /* If we are resumed from some other event (such as an aio completion or a ++ * timer callback), it is a bug in the caller that should be fixed. */ ++ assert(data.done); ++} ++ ++static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, ++ bool poll) ++{ ++ IO_OR_GS_CODE(); ++ ++ if (qemu_in_coroutine()) { ++ bdrv_co_yield_to_drain(bs, true, parent, poll); ++ return; ++ } ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Stop things in parent-to-child order */ ++ if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) { ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ bdrv_parent_drained_begin(bs, parent); ++ if (bs->drv && bs->drv->bdrv_drain_begin) { ++ bs->drv->bdrv_drain_begin(bs); ++ } ++ } ++ ++ /* ++ * Wait for drained requests to finish. ++ * ++ * Calling BDRV_POLL_WHILE() only once for the top-level node is okay: The ++ * call is needed so things in this AioContext can make progress even ++ * though we don't return to the main AioContext loop - this automatically ++ * includes other nodes in the same AioContext and therefore all child ++ * nodes. ++ */ ++ if (poll) { ++ BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, parent)); ++ } ++} ++ ++void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, BdrvChild *parent) ++{ ++ bdrv_do_drained_begin(bs, parent, false); ++} ++ ++void coroutine_mixed_fn ++bdrv_drained_begin(BlockDriverState *bs) ++{ ++ IO_OR_GS_CODE(); ++ bdrv_do_drained_begin(bs, NULL, true); ++} ++ ++/** ++ * This function does not poll, nor must any of its recursively called ++ * functions. ++ */ ++static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) ++{ ++ int old_quiesce_counter; ++ ++ IO_OR_GS_CODE(); ++ ++ if (qemu_in_coroutine()) { ++ bdrv_co_yield_to_drain(bs, false, parent, false); ++ return; ++ } ++ ++ /* At this point, we should be always running in the main loop. */ ++ GLOBAL_STATE_CODE(); ++ assert(bs->quiesce_counter > 0); ++ GLOBAL_STATE_CODE(); ++ ++ /* Re-enable things in child-to-parent order */ ++ old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter); ++ if (old_quiesce_counter == 1) { ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ if (bs->drv && bs->drv->bdrv_drain_end) { ++ bs->drv->bdrv_drain_end(bs); ++ } ++ bdrv_parent_drained_end(bs, parent); ++ } ++} ++ ++void bdrv_drained_end(BlockDriverState *bs) ++{ ++ IO_OR_GS_CODE(); ++ bdrv_do_drained_end(bs, NULL); ++} ++ ++void bdrv_drain(BlockDriverState *bs) ++{ ++ IO_OR_GS_CODE(); ++ bdrv_drained_begin(bs); ++ bdrv_drained_end(bs); ++} ++ ++static void bdrv_drain_assert_idle(BlockDriverState *bs) ++{ ++ BdrvChild *child, *next; ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ assert(qatomic_read(&bs->in_flight) == 0); ++ QLIST_FOREACH_SAFE(child, &bs->children, next, next) { ++ bdrv_drain_assert_idle(child->bs); ++ } ++} ++ ++unsigned int bdrv_drain_all_count = 0; ++ ++static bool bdrv_drain_all_poll(void) ++{ ++ BlockDriverState *bs = NULL; ++ bool result = false; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* ++ * bdrv_drain_poll() can't make changes to the graph and we hold the BQL, ++ * so iterating bdrv_next_all_states() is safe. ++ */ ++ while ((bs = bdrv_next_all_states(bs))) { ++ result |= bdrv_drain_poll(bs, NULL, true); ++ } ++ ++ return result; ++} ++ ++/* ++ * Wait for pending requests to complete across all BlockDriverStates ++ * ++ * This function does not flush data to disk, use bdrv_flush_all() for that ++ * after calling this function. ++ * ++ * This pauses all block jobs and disables external clients. It must ++ * be paired with bdrv_drain_all_end(). ++ * ++ * NOTE: no new block jobs or BlockDriverStates can be created between ++ * the bdrv_drain_all_begin() and bdrv_drain_all_end() calls. ++ */ ++void bdrv_drain_all_begin_nopoll(void) ++{ ++ BlockDriverState *bs = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * bdrv queue is managed by record/replay, ++ * waiting for finishing the I/O requests may ++ * be infinite ++ */ ++ if (replay_events_enabled()) { ++ return; ++ } ++ ++ /* AIO_WAIT_WHILE() with a NULL context can only be called from the main ++ * loop AioContext, so make sure we're in the main context. */ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ assert(bdrv_drain_all_count < INT_MAX); ++ bdrv_drain_all_count++; ++ ++ /* Quiesce all nodes, without polling in-flight requests yet. The graph ++ * cannot change during this loop. */ ++ while ((bs = bdrv_next_all_states(bs))) { ++ bdrv_do_drained_begin(bs, NULL, false); ++ } ++} ++ ++void coroutine_mixed_fn bdrv_drain_all_begin(void) ++{ ++ BlockDriverState *bs = NULL; ++ ++ if (qemu_in_coroutine()) { ++ bdrv_co_yield_to_drain(NULL, true, NULL, true); ++ return; ++ } ++ ++ /* ++ * bdrv queue is managed by record/replay, ++ * waiting for finishing the I/O requests may ++ * be infinite ++ */ ++ if (replay_events_enabled()) { ++ return; ++ } ++ ++ bdrv_drain_all_begin_nopoll(); ++ ++ /* Now poll the in-flight requests */ ++ AIO_WAIT_WHILE_UNLOCKED(NULL, bdrv_drain_all_poll()); ++ ++ while ((bs = bdrv_next_all_states(bs))) { ++ bdrv_drain_assert_idle(bs); ++ } ++} ++ ++void bdrv_drain_all_end_quiesce(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ g_assert(bs->quiesce_counter > 0); ++ g_assert(!bs->refcnt); ++ ++ while (bs->quiesce_counter) { ++ bdrv_do_drained_end(bs, NULL); ++ } ++} ++ ++void bdrv_drain_all_end(void) ++{ ++ BlockDriverState *bs = NULL; ++ GLOBAL_STATE_CODE(); ++ ++ /* ++ * bdrv queue is managed by record/replay, ++ * waiting for finishing the I/O requests may ++ * be endless ++ */ ++ if (replay_events_enabled()) { ++ return; ++ } ++ ++ while ((bs = bdrv_next_all_states(bs))) { ++ bdrv_do_drained_end(bs, NULL); ++ } ++ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ assert(bdrv_drain_all_count > 0); ++ bdrv_drain_all_count--; ++} ++ ++void bdrv_drain_all(void) ++{ ++ GLOBAL_STATE_CODE(); ++ bdrv_drain_all_begin(); ++ bdrv_drain_all_end(); ++} ++ ++/** ++ * Remove an active request from the tracked requests list ++ * ++ * This function should be called when a tracked request is completing. ++ */ ++static void coroutine_fn tracked_request_end(BdrvTrackedRequest *req) ++{ ++ if (req->serialising) { ++ qatomic_dec(&req->bs->serialising_in_flight); ++ } ++ ++ qemu_mutex_lock(&req->bs->reqs_lock); ++ QLIST_REMOVE(req, list); ++ qemu_mutex_unlock(&req->bs->reqs_lock); ++ ++ /* ++ * At this point qemu_co_queue_wait(&req->wait_queue, ...) won't be called ++ * anymore because the request has been removed from the list, so it's safe ++ * to restart the queue outside reqs_lock to minimize the critical section. ++ */ ++ qemu_co_queue_restart_all(&req->wait_queue); ++} ++ ++/** ++ * Add an active request to the tracked requests list ++ */ ++static void coroutine_fn tracked_request_begin(BdrvTrackedRequest *req, ++ BlockDriverState *bs, ++ int64_t offset, ++ int64_t bytes, ++ enum BdrvTrackedRequestType type) ++{ ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ *req = (BdrvTrackedRequest){ ++ .bs = bs, ++ .offset = offset, ++ .bytes = bytes, ++ .type = type, ++ .co = qemu_coroutine_self(), ++ .serialising = false, ++ .overlap_offset = offset, ++ .overlap_bytes = bytes, ++ }; ++ ++ qemu_co_queue_init(&req->wait_queue); ++ ++ qemu_mutex_lock(&bs->reqs_lock); ++ QLIST_INSERT_HEAD(&bs->tracked_requests, req, list); ++ qemu_mutex_unlock(&bs->reqs_lock); ++} ++ ++static bool tracked_request_overlaps(BdrvTrackedRequest *req, ++ int64_t offset, int64_t bytes) ++{ ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ /* aaaa bbbb */ ++ if (offset >= req->overlap_offset + req->overlap_bytes) { ++ return false; ++ } ++ /* bbbb aaaa */ ++ if (req->overlap_offset >= offset + bytes) { ++ return false; ++ } ++ return true; ++} ++ ++/* Called with self->bs->reqs_lock held */ ++static coroutine_fn BdrvTrackedRequest * ++bdrv_find_conflicting_request(BdrvTrackedRequest *self) ++{ ++ BdrvTrackedRequest *req; ++ ++ QLIST_FOREACH(req, &self->bs->tracked_requests, list) { ++ if (req == self || (!req->serialising && !self->serialising)) { ++ continue; ++ } ++ if (tracked_request_overlaps(req, self->overlap_offset, ++ self->overlap_bytes)) ++ { ++ /* ++ * Hitting this means there was a reentrant request, for ++ * example, a block driver issuing nested requests. This must ++ * never happen since it means deadlock. ++ */ ++ assert(qemu_coroutine_self() != req->co); ++ ++ /* ++ * If the request is already (indirectly) waiting for us, or ++ * will wait for us as soon as it wakes up, then just go on ++ * (instead of producing a deadlock in the former case). ++ */ ++ if (!req->waiting_for) { ++ return req; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++/* Called with self->bs->reqs_lock held */ ++static void coroutine_fn ++bdrv_wait_serialising_requests_locked(BdrvTrackedRequest *self) ++{ ++ BdrvTrackedRequest *req; ++ ++ while ((req = bdrv_find_conflicting_request(self))) { ++ self->waiting_for = req; ++ qemu_co_queue_wait(&req->wait_queue, &self->bs->reqs_lock); ++ self->waiting_for = NULL; ++ } ++} ++ ++/* Called with req->bs->reqs_lock held */ ++static void tracked_request_set_serialising(BdrvTrackedRequest *req, ++ uint64_t align) ++{ ++ int64_t overlap_offset = req->offset & ~(align - 1); ++ int64_t overlap_bytes = ++ ROUND_UP(req->offset + req->bytes, align) - overlap_offset; ++ ++ bdrv_check_request(req->offset, req->bytes, &error_abort); ++ ++ if (!req->serialising) { ++ qatomic_inc(&req->bs->serialising_in_flight); ++ req->serialising = true; ++ } ++ ++ req->overlap_offset = MIN(req->overlap_offset, overlap_offset); ++ req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); ++} ++ ++/** ++ * Return the tracked request on @bs for the current coroutine, or ++ * NULL if there is none. ++ */ ++BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs) ++{ ++ BdrvTrackedRequest *req; ++ Coroutine *self = qemu_coroutine_self(); ++ IO_CODE(); ++ ++ QLIST_FOREACH(req, &bs->tracked_requests, list) { ++ if (req->co == self) { ++ return req; ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * Round a region to subcluster (if supported) or cluster boundaries ++ */ ++void coroutine_fn GRAPH_RDLOCK ++bdrv_round_to_subclusters(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ int64_t *align_offset, int64_t *align_bytes) ++{ ++ BlockDriverInfo bdi; ++ IO_CODE(); ++ if (bdrv_co_get_info(bs, &bdi) < 0 || bdi.subcluster_size == 0) { ++ *align_offset = offset; ++ *align_bytes = bytes; ++ } else { ++ int64_t c = bdi.subcluster_size; ++ *align_offset = QEMU_ALIGN_DOWN(offset, c); ++ *align_bytes = QEMU_ALIGN_UP(offset - *align_offset + bytes, c); ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK bdrv_get_cluster_size(BlockDriverState *bs) ++{ ++ BlockDriverInfo bdi; ++ int ret; ++ ++ ret = bdrv_co_get_info(bs, &bdi); ++ if (ret < 0 || bdi.cluster_size == 0) { ++ return bs->bl.request_alignment; ++ } else { ++ return bdi.cluster_size; ++ } ++} ++ ++void bdrv_inc_in_flight(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ qatomic_inc(&bs->in_flight); ++} ++ ++void bdrv_wakeup(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ aio_wait_kick(); ++} ++ ++void bdrv_dec_in_flight(BlockDriverState *bs) ++{ ++ IO_CODE(); ++ qatomic_dec(&bs->in_flight); ++ bdrv_wakeup(bs); ++} ++ ++static void coroutine_fn ++bdrv_wait_serialising_requests(BdrvTrackedRequest *self) ++{ ++ BlockDriverState *bs = self->bs; ++ ++ if (!qatomic_read(&bs->serialising_in_flight)) { ++ return; ++ } ++ ++ qemu_mutex_lock(&bs->reqs_lock); ++ bdrv_wait_serialising_requests_locked(self); ++ qemu_mutex_unlock(&bs->reqs_lock); ++} ++ ++void coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req, ++ uint64_t align) ++{ ++ IO_CODE(); ++ ++ qemu_mutex_lock(&req->bs->reqs_lock); ++ ++ tracked_request_set_serialising(req, align); ++ bdrv_wait_serialising_requests_locked(req); ++ ++ qemu_mutex_unlock(&req->bs->reqs_lock); ++} ++ ++int bdrv_check_qiov_request(int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ Error **errp) ++{ ++ /* ++ * Check generic offset/bytes correctness ++ */ ++ ++ if (offset < 0) { ++ error_setg(errp, "offset is negative: %" PRIi64, offset); ++ return -EIO; ++ } ++ ++ if (bytes < 0) { ++ error_setg(errp, "bytes is negative: %" PRIi64, bytes); ++ return -EIO; ++ } ++ ++ if (bytes > BDRV_MAX_LENGTH) { ++ error_setg(errp, "bytes(%" PRIi64 ") exceeds maximum(%" PRIi64 ")", ++ bytes, BDRV_MAX_LENGTH); ++ return -EIO; ++ } ++ ++ if (offset > BDRV_MAX_LENGTH) { ++ error_setg(errp, "offset(%" PRIi64 ") exceeds maximum(%" PRIi64 ")", ++ offset, BDRV_MAX_LENGTH); ++ return -EIO; ++ } ++ ++ if (offset > BDRV_MAX_LENGTH - bytes) { ++ error_setg(errp, "sum of offset(%" PRIi64 ") and bytes(%" PRIi64 ") " ++ "exceeds maximum(%" PRIi64 ")", offset, bytes, ++ BDRV_MAX_LENGTH); ++ return -EIO; ++ } ++ ++ if (!qiov) { ++ return 0; ++ } ++ ++ /* ++ * Check qiov and qiov_offset ++ */ ++ ++ if (qiov_offset > qiov->size) { ++ error_setg(errp, "qiov_offset(%zu) overflow io vector size(%zu)", ++ qiov_offset, qiov->size); ++ return -EIO; ++ } ++ ++ if (bytes > qiov->size - qiov_offset) { ++ error_setg(errp, "bytes(%" PRIi64 ") + qiov_offset(%zu) overflow io " ++ "vector size(%zu)", bytes, qiov_offset, qiov->size); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++int bdrv_check_request(int64_t offset, int64_t bytes, Error **errp) ++{ ++ return bdrv_check_qiov_request(offset, bytes, NULL, 0, errp); ++} ++ ++static int bdrv_check_request32(int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ int ret = bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (bytes > BDRV_REQUEST_MAX_BYTES) { ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Completely zero out a block device with the help of bdrv_pwrite_zeroes. ++ * The operation is sped up by checking the block status and only writing ++ * zeroes to the device if they currently do not return zeroes. Optional ++ * flags are passed through to bdrv_pwrite_zeroes (e.g. BDRV_REQ_MAY_UNMAP, ++ * BDRV_REQ_FUA). ++ * ++ * Returns < 0 on error, 0 on success. For error codes see bdrv_pwrite(). ++ */ ++int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) ++{ ++ int ret; ++ int64_t target_size, bytes, offset = 0; ++ BlockDriverState *bs = child->bs; ++ IO_CODE(); ++ ++ target_size = bdrv_getlength(bs); ++ if (target_size < 0) { ++ return target_size; ++ } ++ ++ for (;;) { ++ bytes = MIN(target_size - offset, BDRV_REQUEST_MAX_BYTES); ++ if (bytes <= 0) { ++ return 0; ++ } ++ ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ if (ret & BDRV_BLOCK_ZERO) { ++ offset += bytes; ++ continue; ++ } ++ ret = bdrv_pwrite_zeroes(child, offset, bytes, flags); ++ if (ret < 0) { ++ return ret; ++ } ++ offset += bytes; ++ } ++} ++ ++/* ++ * Writes to the file and ensures that no writes are reordered across this ++ * request (acts as a barrier) ++ * ++ * Returns 0 on success, -errno in error cases. ++ */ ++int coroutine_fn bdrv_co_pwrite_sync(BdrvChild *child, int64_t offset, ++ int64_t bytes, const void *buf, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ ret = bdrv_co_pwrite(child, offset, bytes, buf, flags); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = bdrv_co_flush(child->bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++typedef struct CoroutineIOCompletion { ++ Coroutine *coroutine; ++ int ret; ++} CoroutineIOCompletion; ++ ++static void bdrv_co_io_em_complete(void *opaque, int ret) ++{ ++ CoroutineIOCompletion *co = opaque; ++ ++ co->ret = ret; ++ aio_co_wake(co->coroutine); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_driver_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, int flags) ++{ ++ BlockDriver *drv = bs->drv; ++ int64_t sector_num; ++ unsigned int nb_sectors; ++ QEMUIOVector local_qiov; ++ int ret; ++ assert_bdrv_graph_readable(); ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ assert(!(flags & ~bs->supported_read_flags)); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (drv->bdrv_co_preadv_part) { ++ return drv->bdrv_co_preadv_part(bs, offset, bytes, qiov, qiov_offset, ++ flags); ++ } ++ ++ if (qiov_offset > 0 || bytes != qiov->size) { ++ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes); ++ qiov = &local_qiov; ++ } ++ ++ if (drv->bdrv_co_preadv) { ++ ret = drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags); ++ goto out; ++ } ++ ++ if (drv->bdrv_aio_preadv) { ++ BlockAIOCB *acb; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ ++ acb = drv->bdrv_aio_preadv(bs, offset, bytes, qiov, flags, ++ bdrv_co_io_em_complete, &co); ++ if (acb == NULL) { ++ ret = -EIO; ++ goto out; ++ } else { ++ qemu_coroutine_yield(); ++ ret = co.ret; ++ goto out; ++ } ++ } ++ ++ sector_num = offset >> BDRV_SECTOR_BITS; ++ nb_sectors = bytes >> BDRV_SECTOR_BITS; ++ ++ assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)); ++ assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); ++ assert(bytes <= BDRV_REQUEST_MAX_BYTES); ++ assert(drv->bdrv_co_readv); ++ ++ ret = drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); ++ ++out: ++ if (qiov == &local_qiov) { ++ qemu_iovec_destroy(&local_qiov); ++ } ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_driver_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BlockDriver *drv = bs->drv; ++ bool emulate_fua = false; ++ int64_t sector_num; ++ unsigned int nb_sectors; ++ QEMUIOVector local_qiov; ++ int ret; ++ assert_bdrv_graph_readable(); ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if ((flags & BDRV_REQ_FUA) && ++ (~bs->supported_write_flags & BDRV_REQ_FUA)) { ++ flags &= ~BDRV_REQ_FUA; ++ emulate_fua = true; ++ } ++ ++ flags &= bs->supported_write_flags; ++ ++ if (drv->bdrv_co_pwritev_part) { ++ ret = drv->bdrv_co_pwritev_part(bs, offset, bytes, qiov, qiov_offset, ++ flags); ++ goto emulate_flags; ++ } ++ ++ if (qiov_offset > 0 || bytes != qiov->size) { ++ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes); ++ qiov = &local_qiov; ++ } ++ ++ if (drv->bdrv_co_pwritev) { ++ ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov, flags); ++ goto emulate_flags; ++ } ++ ++ if (drv->bdrv_aio_pwritev) { ++ BlockAIOCB *acb; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ ++ acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov, flags, ++ bdrv_co_io_em_complete, &co); ++ if (acb == NULL) { ++ ret = -EIO; ++ } else { ++ qemu_coroutine_yield(); ++ ret = co.ret; ++ } ++ goto emulate_flags; ++ } ++ ++ sector_num = offset >> BDRV_SECTOR_BITS; ++ nb_sectors = bytes >> BDRV_SECTOR_BITS; ++ ++ assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)); ++ assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); ++ assert(bytes <= BDRV_REQUEST_MAX_BYTES); ++ ++ assert(drv->bdrv_co_writev); ++ ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov, flags); ++ ++emulate_flags: ++ if (ret == 0 && emulate_fua) { ++ ret = bdrv_co_flush(bs); ++ } ++ ++ if (qiov == &local_qiov) { ++ qemu_iovec_destroy(&local_qiov); ++ } ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_driver_pwritev_compressed(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, QEMUIOVector *qiov, ++ size_t qiov_offset) ++{ ++ BlockDriver *drv = bs->drv; ++ QEMUIOVector local_qiov; ++ int ret; ++ assert_bdrv_graph_readable(); ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (!block_driver_can_compress(drv)) { ++ return -ENOTSUP; ++ } ++ ++ if (drv->bdrv_co_pwritev_compressed_part) { ++ return drv->bdrv_co_pwritev_compressed_part(bs, offset, bytes, ++ qiov, qiov_offset); ++ } ++ ++ if (qiov_offset == 0) { ++ return drv->bdrv_co_pwritev_compressed(bs, offset, bytes, qiov); ++ } ++ ++ qemu_iovec_init_slice(&local_qiov, qiov, qiov_offset, bytes); ++ ret = drv->bdrv_co_pwritev_compressed(bs, offset, bytes, &local_qiov); ++ qemu_iovec_destroy(&local_qiov); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, int flags) ++{ ++ BlockDriverState *bs = child->bs; ++ ++ /* Perform I/O through a temporary buffer so that users who scribble over ++ * their read buffer while the operation is in progress do not end up ++ * modifying the image file. This is critical for zero-copy guest I/O ++ * where anything might happen inside guest memory. ++ */ ++ void *bounce_buffer = NULL; ++ ++ BlockDriver *drv = bs->drv; ++ int64_t align_offset; ++ int64_t align_bytes; ++ int64_t skip_bytes; ++ int ret; ++ int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, ++ BDRV_REQUEST_MAX_BYTES); ++ int64_t progress = 0; ++ bool skip_write; ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* ++ * Do not write anything when the BDS is inactive. That is not ++ * allowed, and it would not help. ++ */ ++ skip_write = (bs->open_flags & BDRV_O_INACTIVE); ++ ++ /* FIXME We cannot require callers to have write permissions when all they ++ * are doing is a read request. If we did things right, write permissions ++ * would be obtained anyway, but internally by the copy-on-read code. As ++ * long as it is implemented here rather than in a separate filter driver, ++ * the copy-on-read code doesn't have its own BdrvChild, however, for which ++ * it could request permissions. Therefore we have to bypass the permission ++ * system for the moment. */ ++ // assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); ++ ++ /* Cover entire cluster so no additional backing file I/O is required when ++ * allocating cluster in the image file. Note that this value may exceed ++ * BDRV_REQUEST_MAX_BYTES (even when the original read did not), which ++ * is one reason we loop rather than doing it all at once. ++ */ ++ bdrv_round_to_subclusters(bs, offset, bytes, &align_offset, &align_bytes); ++ skip_bytes = offset - align_offset; ++ ++ trace_bdrv_co_do_copy_on_readv(bs, offset, bytes, ++ align_offset, align_bytes); ++ ++ while (align_bytes) { ++ int64_t pnum; ++ ++ if (skip_write) { ++ ret = 1; /* "already allocated", so nothing will be copied */ ++ pnum = MIN(align_bytes, max_transfer); ++ } else { ++ ret = bdrv_co_is_allocated(bs, align_offset, ++ MIN(align_bytes, max_transfer), &pnum); ++ if (ret < 0) { ++ /* ++ * Safe to treat errors in querying allocation as if ++ * unallocated; we'll probably fail again soon on the ++ * read, but at least that will set a decent errno. ++ */ ++ pnum = MIN(align_bytes, max_transfer); ++ } ++ ++ /* Stop at EOF if the image ends in the middle of the cluster */ ++ if (ret == 0 && pnum == 0) { ++ assert(progress >= bytes); ++ break; ++ } ++ ++ assert(skip_bytes < pnum); ++ } ++ ++ if (ret <= 0) { ++ QEMUIOVector local_qiov; ++ ++ /* Must copy-on-read; use the bounce buffer */ ++ pnum = MIN(pnum, MAX_BOUNCE_BUFFER); ++ if (!bounce_buffer) { ++ int64_t max_we_need = MAX(pnum, align_bytes - pnum); ++ int64_t max_allowed = MIN(max_transfer, MAX_BOUNCE_BUFFER); ++ int64_t bounce_buffer_len = MIN(max_we_need, max_allowed); ++ ++ bounce_buffer = qemu_try_blockalign(bs, bounce_buffer_len); ++ if (!bounce_buffer) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ } ++ qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum); ++ ++ ret = bdrv_driver_preadv(bs, align_offset, pnum, ++ &local_qiov, 0, 0); ++ if (ret < 0) { ++ goto err; ++ } ++ ++ bdrv_co_debug_event(bs, BLKDBG_COR_WRITE); ++ if (drv->bdrv_co_pwrite_zeroes && ++ buffer_is_zero(bounce_buffer, pnum)) { ++ /* FIXME: Should we (perhaps conditionally) be setting ++ * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy ++ * that still correctly reads as zero? */ ++ ret = bdrv_co_do_pwrite_zeroes(bs, align_offset, pnum, ++ BDRV_REQ_WRITE_UNCHANGED); ++ } else { ++ /* This does not change the data on the disk, it is not ++ * necessary to flush even in cache=writethrough mode. ++ */ ++ ret = bdrv_driver_pwritev(bs, align_offset, pnum, ++ &local_qiov, 0, ++ BDRV_REQ_WRITE_UNCHANGED); ++ } ++ ++ if (ret < 0) { ++ /* It might be okay to ignore write errors for guest ++ * requests. If this is a deliberate copy-on-read ++ * then we don't want to ignore the error. Simply ++ * report it in all cases. ++ */ ++ goto err; ++ } ++ ++ if (!(flags & BDRV_REQ_PREFETCH)) { ++ qemu_iovec_from_buf(qiov, qiov_offset + progress, ++ bounce_buffer + skip_bytes, ++ MIN(pnum - skip_bytes, bytes - progress)); ++ } ++ } else if (!(flags & BDRV_REQ_PREFETCH)) { ++ /* Read directly into the destination */ ++ ret = bdrv_driver_preadv(bs, offset + progress, ++ MIN(pnum - skip_bytes, bytes - progress), ++ qiov, qiov_offset + progress, 0); ++ if (ret < 0) { ++ goto err; ++ } ++ } ++ ++ align_offset += pnum; ++ align_bytes -= pnum; ++ progress += pnum - skip_bytes; ++ skip_bytes = 0; ++ } ++ ret = 0; ++ ++err: ++ qemu_vfree(bounce_buffer); ++ return ret; ++} ++ ++/* ++ * Forwards an already correctly aligned request to the BlockDriver. This ++ * handles copy on read, zeroing after EOF, and fragmentation of large ++ * reads; any other features must be implemented by the caller. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_aligned_preadv(BdrvChild *child, BdrvTrackedRequest *req, ++ int64_t offset, int64_t bytes, int64_t align, ++ QEMUIOVector *qiov, size_t qiov_offset, int flags) ++{ ++ BlockDriverState *bs = child->bs; ++ int64_t total_bytes, max_bytes; ++ int ret = 0; ++ int64_t bytes_remaining = bytes; ++ int max_transfer; ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ assert(is_power_of_2(align)); ++ assert((offset & (align - 1)) == 0); ++ assert((bytes & (align - 1)) == 0); ++ assert((bs->open_flags & BDRV_O_NO_IO) == 0); ++ max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), ++ align); ++ ++ /* ++ * TODO: We would need a per-BDS .supported_read_flags and ++ * potential fallback support, if we ever implement any read flags ++ * to pass through to drivers. For now, there aren't any ++ * passthrough flags except the BDRV_REQ_REGISTERED_BUF optimization hint. ++ */ ++ assert(!(flags & ~(BDRV_REQ_COPY_ON_READ | BDRV_REQ_PREFETCH | ++ BDRV_REQ_REGISTERED_BUF))); ++ ++ /* Handle Copy on Read and associated serialisation */ ++ if (flags & BDRV_REQ_COPY_ON_READ) { ++ /* If we touch the same cluster it counts as an overlap. This ++ * guarantees that allocating writes will be serialized and not race ++ * with each other for the same cluster. For example, in copy-on-read ++ * it ensures that the CoR read and write operations are atomic and ++ * guest writes cannot interleave between them. */ ++ bdrv_make_request_serialising(req, bdrv_get_cluster_size(bs)); ++ } else { ++ bdrv_wait_serialising_requests(req); ++ } ++ ++ if (flags & BDRV_REQ_COPY_ON_READ) { ++ int64_t pnum; ++ ++ /* The flag BDRV_REQ_COPY_ON_READ has reached its addressee */ ++ flags &= ~BDRV_REQ_COPY_ON_READ; ++ ++ ret = bdrv_co_is_allocated(bs, offset, bytes, &pnum); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ if (!ret || pnum != bytes) { ++ ret = bdrv_co_do_copy_on_readv(child, offset, bytes, ++ qiov, qiov_offset, flags); ++ goto out; ++ } else if (flags & BDRV_REQ_PREFETCH) { ++ goto out; ++ } ++ } ++ ++ /* Forward the request to the BlockDriver, possibly fragmenting it */ ++ total_bytes = bdrv_co_getlength(bs); ++ if (total_bytes < 0) { ++ ret = total_bytes; ++ goto out; ++ } ++ ++ assert(!(flags & ~(bs->supported_read_flags | BDRV_REQ_REGISTERED_BUF))); ++ ++ max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align); ++ if (bytes <= max_bytes && bytes <= max_transfer) { ++ ret = bdrv_driver_preadv(bs, offset, bytes, qiov, qiov_offset, flags); ++ goto out; ++ } ++ ++ while (bytes_remaining) { ++ int64_t num; ++ ++ if (max_bytes) { ++ num = MIN(bytes_remaining, MIN(max_bytes, max_transfer)); ++ assert(num); ++ ++ ret = bdrv_driver_preadv(bs, offset + bytes - bytes_remaining, ++ num, qiov, ++ qiov_offset + bytes - bytes_remaining, ++ flags); ++ max_bytes -= num; ++ } else { ++ num = bytes_remaining; ++ ret = qemu_iovec_memset(qiov, qiov_offset + bytes - bytes_remaining, ++ 0, bytes_remaining); ++ } ++ if (ret < 0) { ++ goto out; ++ } ++ bytes_remaining -= num; ++ } ++ ++out: ++ return ret < 0 ? ret : 0; ++} ++ ++/* ++ * Request padding ++ * ++ * |<---- align ----->| |<----- align ---->| ++ * |<- head ->|<------------- bytes ------------->|<-- tail -->| ++ * | | | | | | ++ * -*----------$-------*-------- ... --------*-----$------------*--- ++ * | | | | | | ++ * | offset | | end | ++ * ALIGN_DOWN(offset) ALIGN_UP(offset) ALIGN_DOWN(end) ALIGN_UP(end) ++ * [buf ... ) [tail_buf ) ++ * ++ * @buf is an aligned allocation needed to store @head and @tail paddings. @head ++ * is placed at the beginning of @buf and @tail at the @end. ++ * ++ * @tail_buf is a pointer to sub-buffer, corresponding to align-sized chunk ++ * around tail, if tail exists. ++ * ++ * @merge_reads is true for small requests, ++ * if @buf_len == @head + bytes + @tail. In this case it is possible that both ++ * head and tail exist but @buf_len == align and @tail_buf == @buf. ++ * ++ * @write is true for write requests, false for read requests. ++ * ++ * If padding makes the vector too long (exceeding IOV_MAX), then we need to ++ * merge existing vector elements into a single one. @collapse_bounce_buf acts ++ * as the bounce buffer in such cases. @pre_collapse_qiov has the pre-collapse ++ * I/O vector elements so for read requests, the data can be copied back after ++ * the read is done. ++ */ ++typedef struct BdrvRequestPadding { ++ uint8_t *buf; ++ size_t buf_len; ++ uint8_t *tail_buf; ++ size_t head; ++ size_t tail; ++ bool merge_reads; ++ bool write; ++ QEMUIOVector local_qiov; ++ ++ uint8_t *collapse_bounce_buf; ++ size_t collapse_len; ++ QEMUIOVector pre_collapse_qiov; ++} BdrvRequestPadding; ++ ++static bool bdrv_init_padding(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, ++ bool write, ++ BdrvRequestPadding *pad) ++{ ++ int64_t align = bs->bl.request_alignment; ++ int64_t sum; ++ ++ bdrv_check_request(offset, bytes, &error_abort); ++ assert(align <= INT_MAX); /* documented in block/block_int.h */ ++ assert(align <= SIZE_MAX / 2); /* so we can allocate the buffer */ ++ ++ memset(pad, 0, sizeof(*pad)); ++ ++ pad->head = offset & (align - 1); ++ pad->tail = ((offset + bytes) & (align - 1)); ++ if (pad->tail) { ++ pad->tail = align - pad->tail; ++ } ++ ++ if (!pad->head && !pad->tail) { ++ return false; ++ } ++ ++ assert(bytes); /* Nothing good in aligning zero-length requests */ ++ ++ sum = pad->head + bytes + pad->tail; ++ pad->buf_len = (sum > align && pad->head && pad->tail) ? 2 * align : align; ++ pad->buf = qemu_blockalign(bs, pad->buf_len); ++ pad->merge_reads = sum == pad->buf_len; ++ if (pad->tail) { ++ pad->tail_buf = pad->buf + pad->buf_len - align; ++ } ++ ++ pad->write = write; ++ ++ return true; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, ++ BdrvRequestPadding *pad, bool zero_middle) ++{ ++ QEMUIOVector local_qiov; ++ BlockDriverState *bs = child->bs; ++ uint64_t align = bs->bl.request_alignment; ++ int ret; ++ ++ assert(req->serialising && pad->buf); ++ ++ if (pad->head || pad->merge_reads) { ++ int64_t bytes = pad->merge_reads ? pad->buf_len : align; ++ ++ qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); ++ ++ if (pad->head) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); ++ } ++ if (pad->merge_reads && pad->tail) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ++ } ++ ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes, ++ align, &local_qiov, 0, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ if (pad->head) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); ++ } ++ if (pad->merge_reads && pad->tail) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); ++ } ++ ++ if (pad->merge_reads) { ++ goto zero_mem; ++ } ++ } ++ ++ if (pad->tail) { ++ qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align); ++ ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ++ ret = bdrv_aligned_preadv( ++ child, req, ++ req->overlap_offset + req->overlap_bytes - align, ++ align, align, &local_qiov, 0, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); ++ } ++ ++zero_mem: ++ if (zero_middle) { ++ memset(pad->buf + pad->head, 0, pad->buf_len - pad->head - pad->tail); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Free *pad's associated buffers, and perform any necessary finalization steps. ++ */ ++static void bdrv_padding_finalize(BdrvRequestPadding *pad) ++{ ++ if (pad->collapse_bounce_buf) { ++ if (!pad->write) { ++ /* ++ * If padding required elements in the vector to be collapsed into a ++ * bounce buffer, copy the bounce buffer content back ++ */ ++ qemu_iovec_from_buf(&pad->pre_collapse_qiov, 0, ++ pad->collapse_bounce_buf, pad->collapse_len); ++ } ++ qemu_vfree(pad->collapse_bounce_buf); ++ qemu_iovec_destroy(&pad->pre_collapse_qiov); ++ } ++ if (pad->buf) { ++ qemu_vfree(pad->buf); ++ qemu_iovec_destroy(&pad->local_qiov); ++ } ++ memset(pad, 0, sizeof(*pad)); ++} ++ ++/* ++ * Create pad->local_qiov by wrapping @iov in the padding head and tail, while ++ * ensuring that the resulting vector will not exceed IOV_MAX elements. ++ * ++ * To ensure this, when necessary, the first two or three elements of @iov are ++ * merged into pad->collapse_bounce_buf and replaced by a reference to that ++ * bounce buffer in pad->local_qiov. ++ * ++ * After performing a read request, the data from the bounce buffer must be ++ * copied back into pad->pre_collapse_qiov (e.g. by bdrv_padding_finalize()). ++ */ ++static int bdrv_create_padded_qiov(BlockDriverState *bs, ++ BdrvRequestPadding *pad, ++ struct iovec *iov, int niov, ++ size_t iov_offset, size_t bytes) ++{ ++ int padded_niov, surplus_count, collapse_count; ++ ++ /* Assert this invariant */ ++ assert(niov <= IOV_MAX); ++ ++ /* ++ * Cannot pad if resulting length would exceed SIZE_MAX. Returning an error ++ * to the guest is not ideal, but there is little else we can do. At least ++ * this will practically never happen on 64-bit systems. ++ */ ++ if (SIZE_MAX - pad->head < bytes || ++ SIZE_MAX - pad->head - bytes < pad->tail) ++ { ++ return -EINVAL; ++ } ++ ++ /* Length of the resulting IOV if we just concatenated everything */ ++ padded_niov = !!pad->head + niov + !!pad->tail; ++ ++ qemu_iovec_init(&pad->local_qiov, MIN(padded_niov, IOV_MAX)); ++ ++ if (pad->head) { ++ qemu_iovec_add(&pad->local_qiov, pad->buf, pad->head); ++ } ++ ++ /* ++ * If padded_niov > IOV_MAX, we cannot just concatenate everything. ++ * Instead, merge the first two or three elements of @iov to reduce the ++ * number of vector elements as necessary. ++ */ ++ if (padded_niov > IOV_MAX) { ++ /* ++ * Only head and tail can have lead to the number of entries exceeding ++ * IOV_MAX, so we can exceed it by the head and tail at most. We need ++ * to reduce the number of elements by `surplus_count`, so we merge that ++ * many elements plus one into one element. ++ */ ++ surplus_count = padded_niov - IOV_MAX; ++ assert(surplus_count <= !!pad->head + !!pad->tail); ++ collapse_count = surplus_count + 1; ++ ++ /* ++ * Move the elements to collapse into `pad->pre_collapse_qiov`, then ++ * advance `iov` (and associated variables) by those elements. ++ */ ++ qemu_iovec_init(&pad->pre_collapse_qiov, collapse_count); ++ qemu_iovec_concat_iov(&pad->pre_collapse_qiov, iov, ++ collapse_count, iov_offset, SIZE_MAX); ++ iov += collapse_count; ++ iov_offset = 0; ++ niov -= collapse_count; ++ bytes -= pad->pre_collapse_qiov.size; ++ ++ /* ++ * Construct the bounce buffer to match the length of the to-collapse ++ * vector elements, and for write requests, initialize it with the data ++ * from those elements. Then add it to `pad->local_qiov`. ++ */ ++ pad->collapse_len = pad->pre_collapse_qiov.size; ++ pad->collapse_bounce_buf = qemu_blockalign(bs, pad->collapse_len); ++ if (pad->write) { ++ qemu_iovec_to_buf(&pad->pre_collapse_qiov, 0, ++ pad->collapse_bounce_buf, pad->collapse_len); ++ } ++ qemu_iovec_add(&pad->local_qiov, ++ pad->collapse_bounce_buf, pad->collapse_len); ++ } ++ ++ qemu_iovec_concat_iov(&pad->local_qiov, iov, niov, iov_offset, bytes); ++ ++ if (pad->tail) { ++ qemu_iovec_add(&pad->local_qiov, ++ pad->buf + pad->buf_len - pad->tail, pad->tail); ++ } ++ ++ assert(pad->local_qiov.niov == MIN(padded_niov, IOV_MAX)); ++ return 0; ++} ++ ++/* ++ * bdrv_pad_request ++ * ++ * Exchange request parameters with padded request if needed. Don't include RMW ++ * read of padding, bdrv_padding_rmw_read() should be called separately if ++ * needed. ++ * ++ * @write is true for write requests, false for read requests. ++ * ++ * Request parameters (@qiov, &qiov_offset, &offset, &bytes) are in-out: ++ * - on function start they represent original request ++ * - on failure or when padding is not needed they are unchanged ++ * - on success when padding is needed they represent padded request ++ */ ++static int bdrv_pad_request(BlockDriverState *bs, ++ QEMUIOVector **qiov, size_t *qiov_offset, ++ int64_t *offset, int64_t *bytes, ++ bool write, ++ BdrvRequestPadding *pad, bool *padded, ++ BdrvRequestFlags *flags) ++{ ++ int ret; ++ struct iovec *sliced_iov; ++ int sliced_niov; ++ size_t sliced_head, sliced_tail; ++ ++ /* Should have been checked by the caller already */ ++ ret = bdrv_check_request32(*offset, *bytes, *qiov, *qiov_offset); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!bdrv_init_padding(bs, *offset, *bytes, write, pad)) { ++ if (padded) { ++ *padded = false; ++ } ++ return 0; ++ } ++ ++ /* ++ * For prefetching in stream_populate(), no qiov is passed along, because ++ * only copy-on-read matters. ++ */ ++ if (*qiov) { ++ sliced_iov = qemu_iovec_slice(*qiov, *qiov_offset, *bytes, ++ &sliced_head, &sliced_tail, ++ &sliced_niov); ++ ++ /* Guaranteed by bdrv_check_request32() */ ++ assert(*bytes <= SIZE_MAX); ++ ret = bdrv_create_padded_qiov(bs, pad, sliced_iov, sliced_niov, ++ sliced_head, *bytes); ++ if (ret < 0) { ++ bdrv_padding_finalize(pad); ++ return ret; ++ } ++ *qiov = &pad->local_qiov; ++ *qiov_offset = 0; ++ } ++ ++ *bytes += pad->head + pad->tail; ++ *offset -= pad->head; ++ if (padded) { ++ *padded = true; ++ } ++ if (flags) { ++ /* Can't use optimization hint with bounce buffer */ ++ *flags &= ~BDRV_REQ_REGISTERED_BUF; ++ } ++ ++ return 0; ++} ++ ++int coroutine_fn bdrv_co_preadv(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ IO_CODE(); ++ return bdrv_co_preadv_part(child, offset, bytes, qiov, 0, flags); ++} ++ ++int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, ++ int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BlockDriverState *bs = child->bs; ++ BdrvTrackedRequest req; ++ BdrvRequestPadding pad; ++ int ret; ++ IO_CODE(); ++ ++ trace_bdrv_co_preadv_part(bs, offset, bytes, flags); ++ ++ if (!bdrv_co_is_inserted(bs)) { ++ return -ENOMEDIUM; ++ } ++ ++ ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (bytes == 0 && !QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)) { ++ /* ++ * Aligning zero request is nonsense. Even if driver has special meaning ++ * of zero-length (like qcow2_co_pwritev_compressed_part), we can't pass ++ * it to driver due to request_alignment. ++ * ++ * Still, no reason to return an error if someone do unaligned ++ * zero-length read occasionally. ++ */ ++ return 0; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ /* Don't do copy-on-read if we read data before write operation */ ++ if (qatomic_read(&bs->copy_on_read)) { ++ flags |= BDRV_REQ_COPY_ON_READ; ++ } ++ ++ ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, false, ++ &pad, NULL, &flags); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_READ); ++ ret = bdrv_aligned_preadv(child, &req, offset, bytes, ++ bs->bl.request_alignment, ++ qiov, qiov_offset, flags); ++ tracked_request_end(&req); ++ bdrv_padding_finalize(&pad); ++ ++fail: ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ BlockDriver *drv = bs->drv; ++ QEMUIOVector qiov; ++ void *buf = NULL; ++ int ret = 0; ++ bool need_flush = false; ++ int head = 0; ++ int tail = 0; ++ ++ int64_t max_write_zeroes = MIN_NON_ZERO(bs->bl.max_pwrite_zeroes, ++ INT64_MAX); ++ int alignment = MAX(bs->bl.pwrite_zeroes_alignment, ++ bs->bl.request_alignment); ++ int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); ++ ++ assert_bdrv_graph_readable(); ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if ((flags & ~bs->supported_zero_flags) & BDRV_REQ_NO_FALLBACK) { ++ return -ENOTSUP; ++ } ++ ++ /* By definition there is no user buffer so this flag doesn't make sense */ ++ if (flags & BDRV_REQ_REGISTERED_BUF) { ++ return -EINVAL; ++ } ++ ++ /* If opened with discard=off we should never unmap. */ ++ if (!(bs->open_flags & BDRV_O_UNMAP)) { ++ flags &= ~BDRV_REQ_MAY_UNMAP; ++ } ++ ++ /* Invalidate the cached block-status data range if this write overlaps */ ++ bdrv_bsc_invalidate_range(bs, offset, bytes); ++ ++ assert(alignment % bs->bl.request_alignment == 0); ++ head = offset % alignment; ++ tail = (offset + bytes) % alignment; ++ max_write_zeroes = QEMU_ALIGN_DOWN(max_write_zeroes, alignment); ++ assert(max_write_zeroes >= bs->bl.request_alignment); ++ ++ while (bytes > 0 && !ret) { ++ int64_t num = bytes; ++ ++ /* Align request. Block drivers can expect the "bulk" of the request ++ * to be aligned, and that unaligned requests do not cross cluster ++ * boundaries. ++ */ ++ if (head) { ++ /* Make a small request up to the first aligned sector. For ++ * convenience, limit this request to max_transfer even if ++ * we don't need to fall back to writes. */ ++ num = MIN(MIN(bytes, max_transfer), alignment - head); ++ head = (head + num) % alignment; ++ assert(num < max_write_zeroes); ++ } else if (tail && num > alignment) { ++ /* Shorten the request to the last aligned sector. */ ++ num -= tail; ++ } ++ ++ /* limit request size */ ++ if (num > max_write_zeroes) { ++ num = max_write_zeroes; ++ } ++ ++ ret = -ENOTSUP; ++ /* First try the efficient write zeroes operation */ ++ if (drv->bdrv_co_pwrite_zeroes) { ++ ret = drv->bdrv_co_pwrite_zeroes(bs, offset, num, ++ flags & bs->supported_zero_flags); ++ if (ret != -ENOTSUP && (flags & BDRV_REQ_FUA) && ++ !(bs->supported_zero_flags & BDRV_REQ_FUA)) { ++ need_flush = true; ++ } ++ } else { ++ assert(!bs->supported_zero_flags); ++ } ++ ++ if (ret == -ENOTSUP && !(flags & BDRV_REQ_NO_FALLBACK)) { ++ /* Fall back to bounce buffer if write zeroes is unsupported */ ++ BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE; ++ ++ if ((flags & BDRV_REQ_FUA) && ++ !(bs->supported_write_flags & BDRV_REQ_FUA)) { ++ /* No need for bdrv_driver_pwrite() to do a fallback ++ * flush on each chunk; use just one at the end */ ++ write_flags &= ~BDRV_REQ_FUA; ++ need_flush = true; ++ } ++ num = MIN(num, max_transfer); ++ if (buf == NULL) { ++ buf = qemu_try_blockalign0(bs, num); ++ if (buf == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ } ++ qemu_iovec_init_buf(&qiov, buf, num); ++ ++ ret = bdrv_driver_pwritev(bs, offset, num, &qiov, 0, write_flags); ++ ++ /* Keep bounce buffer around if it is big enough for all ++ * all future requests. ++ */ ++ if (num < max_transfer) { ++ qemu_vfree(buf); ++ buf = NULL; ++ } ++ } ++ ++ offset += num; ++ bytes -= num; ++ } ++ ++fail: ++ if (ret == 0 && need_flush) { ++ ret = bdrv_co_flush(bs); ++ } ++ qemu_vfree(buf); ++ return ret; ++} ++ ++static inline int coroutine_fn GRAPH_RDLOCK ++bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvTrackedRequest *req, int flags) ++{ ++ BlockDriverState *bs = child->bs; ++ ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ if (bdrv_is_read_only(bs)) { ++ return -EPERM; ++ } ++ ++ assert(!(bs->open_flags & BDRV_O_INACTIVE)); ++ assert((bs->open_flags & BDRV_O_NO_IO) == 0); ++ assert(!(flags & ~BDRV_REQ_MASK)); ++ assert(!((flags & BDRV_REQ_NO_WAIT) && !(flags & BDRV_REQ_SERIALISING))); ++ ++ if (flags & BDRV_REQ_SERIALISING) { ++ QEMU_LOCK_GUARD(&bs->reqs_lock); ++ ++ tracked_request_set_serialising(req, bdrv_get_cluster_size(bs)); ++ ++ if ((flags & BDRV_REQ_NO_WAIT) && bdrv_find_conflicting_request(req)) { ++ return -EBUSY; ++ } ++ ++ bdrv_wait_serialising_requests_locked(req); ++ } else { ++ bdrv_wait_serialising_requests(req); ++ } ++ ++ assert(req->overlap_offset <= offset); ++ assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); ++ assert(offset + bytes <= bs->total_sectors * BDRV_SECTOR_SIZE || ++ child->perm & BLK_PERM_RESIZE); ++ ++ switch (req->type) { ++ case BDRV_TRACKED_WRITE: ++ case BDRV_TRACKED_DISCARD: ++ if (flags & BDRV_REQ_WRITE_UNCHANGED) { ++ assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); ++ } else { ++ assert(child->perm & BLK_PERM_WRITE); ++ } ++ bdrv_write_threshold_check_write(bs, offset, bytes); ++ return 0; ++ case BDRV_TRACKED_TRUNCATE: ++ assert(child->perm & BLK_PERM_RESIZE); ++ return 0; ++ default: ++ abort(); ++ } ++} ++ ++static inline void coroutine_fn GRAPH_RDLOCK ++bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvTrackedRequest *req, int ret) ++{ ++ int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); ++ BlockDriverState *bs = child->bs; ++ ++ bdrv_check_request(offset, bytes, &error_abort); ++ ++ qatomic_inc(&bs->write_gen); ++ ++ /* ++ * Discard cannot extend the image, but in error handling cases, such as ++ * when reverting a qcow2 cluster allocation, the discarded range can pass ++ * the end of image file, so we cannot assert about BDRV_TRACKED_DISCARD ++ * here. Instead, just skip it, since semantically a discard request ++ * beyond EOF cannot expand the image anyway. ++ */ ++ if (ret == 0 && ++ (req->type == BDRV_TRACKED_TRUNCATE || ++ end_sector > bs->total_sectors) && ++ req->type != BDRV_TRACKED_DISCARD) { ++ bs->total_sectors = end_sector; ++ bdrv_parent_cb_resize(bs); ++ bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); ++ } ++ if (req->bytes) { ++ switch (req->type) { ++ case BDRV_TRACKED_WRITE: ++ stat64_max(&bs->wr_highest_offset, offset + bytes); ++ /* fall through, to set dirty bits */ ++ case BDRV_TRACKED_DISCARD: ++ bdrv_set_dirty(bs, offset, bytes); ++ break; ++ default: ++ break; ++ } ++ } ++} ++ ++/* ++ * Forwards an already correctly aligned write request to the BlockDriver, ++ * after possibly fragmenting it. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_aligned_pwritev(BdrvChild *child, BdrvTrackedRequest *req, ++ int64_t offset, int64_t bytes, int64_t align, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BlockDriverState *bs = child->bs; ++ BlockDriver *drv = bs->drv; ++ int ret; ++ ++ int64_t bytes_remaining = bytes; ++ int max_transfer; ++ ++ bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, &error_abort); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (bdrv_has_readonly_bitmaps(bs)) { ++ return -EPERM; ++ } ++ ++ assert(is_power_of_2(align)); ++ assert((offset & (align - 1)) == 0); ++ assert((bytes & (align - 1)) == 0); ++ max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), ++ align); ++ ++ ret = bdrv_co_write_req_prepare(child, offset, bytes, req, flags); ++ ++ if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF && ++ !(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_pwrite_zeroes && ++ qemu_iovec_is_zero(qiov, qiov_offset, bytes)) { ++ flags |= BDRV_REQ_ZERO_WRITE; ++ if (bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP) { ++ flags |= BDRV_REQ_MAY_UNMAP; ++ } ++ ++ /* Can't use optimization hint with bufferless zero write */ ++ flags &= ~BDRV_REQ_REGISTERED_BUF; ++ } ++ ++ if (ret < 0) { ++ /* Do nothing, write notifier decided to fail this request */ ++ } else if (flags & BDRV_REQ_ZERO_WRITE) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); ++ ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags); ++ } else if (flags & BDRV_REQ_WRITE_COMPRESSED) { ++ ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, ++ qiov, qiov_offset); ++ } else if (bytes <= max_transfer) { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ++ ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags); ++ } else { ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ++ while (bytes_remaining) { ++ int num = MIN(bytes_remaining, max_transfer); ++ int local_flags = flags; ++ ++ assert(num); ++ if (num < bytes_remaining && (flags & BDRV_REQ_FUA) && ++ !(bs->supported_write_flags & BDRV_REQ_FUA)) { ++ /* If FUA is going to be emulated by flush, we only ++ * need to flush on the last iteration */ ++ local_flags &= ~BDRV_REQ_FUA; ++ } ++ ++ ret = bdrv_driver_pwritev(bs, offset + bytes - bytes_remaining, ++ num, qiov, ++ qiov_offset + bytes - bytes_remaining, ++ local_flags); ++ if (ret < 0) { ++ break; ++ } ++ bytes_remaining -= num; ++ } ++ } ++ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE); ++ ++ if (ret >= 0) { ++ ret = 0; ++ } ++ bdrv_co_write_req_finish(child, offset, bytes, req, ret); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_co_do_zero_pwritev(BdrvChild *child, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags, BdrvTrackedRequest *req) ++{ ++ BlockDriverState *bs = child->bs; ++ QEMUIOVector local_qiov; ++ uint64_t align = bs->bl.request_alignment; ++ int ret = 0; ++ bool padding; ++ BdrvRequestPadding pad; ++ ++ /* This flag doesn't make sense for padding or zero writes */ ++ flags &= ~BDRV_REQ_REGISTERED_BUF; ++ ++ padding = bdrv_init_padding(bs, offset, bytes, true, &pad); ++ if (padding) { ++ assert(!(flags & BDRV_REQ_NO_WAIT)); ++ bdrv_make_request_serialising(req, align); ++ ++ bdrv_padding_rmw_read(child, req, &pad, true); ++ ++ if (pad.head || pad.merge_reads) { ++ int64_t aligned_offset = offset & ~(align - 1); ++ int64_t write_bytes = pad.merge_reads ? pad.buf_len : align; ++ ++ qemu_iovec_init_buf(&local_qiov, pad.buf, write_bytes); ++ ret = bdrv_aligned_pwritev(child, req, aligned_offset, write_bytes, ++ align, &local_qiov, 0, ++ flags & ~BDRV_REQ_ZERO_WRITE); ++ if (ret < 0 || pad.merge_reads) { ++ /* Error or all work is done */ ++ goto out; ++ } ++ offset += write_bytes - pad.head; ++ bytes -= write_bytes - pad.head; ++ } ++ } ++ ++ assert(!bytes || (offset & (align - 1)) == 0); ++ if (bytes >= align) { ++ /* Write the aligned part in the middle. */ ++ int64_t aligned_bytes = bytes & ~(align - 1); ++ ret = bdrv_aligned_pwritev(child, req, offset, aligned_bytes, align, ++ NULL, 0, flags); ++ if (ret < 0) { ++ goto out; ++ } ++ bytes -= aligned_bytes; ++ offset += aligned_bytes; ++ } ++ ++ assert(!bytes || (offset & (align - 1)) == 0); ++ if (bytes) { ++ assert(align == pad.tail + bytes); ++ ++ qemu_iovec_init_buf(&local_qiov, pad.tail_buf, align); ++ ret = bdrv_aligned_pwritev(child, req, offset, align, align, ++ &local_qiov, 0, ++ flags & ~BDRV_REQ_ZERO_WRITE); ++ } ++ ++out: ++ bdrv_padding_finalize(&pad); ++ ++ return ret; ++} ++ ++/* ++ * Handle a write request in coroutine context ++ */ ++int coroutine_fn bdrv_co_pwritev(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ IO_CODE(); ++ return bdrv_co_pwritev_part(child, offset, bytes, qiov, 0, flags); ++} ++ ++int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, ++ int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BlockDriverState *bs = child->bs; ++ BdrvTrackedRequest req; ++ uint64_t align = bs->bl.request_alignment; ++ BdrvRequestPadding pad; ++ int ret; ++ bool padded = false; ++ IO_CODE(); ++ ++ trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); ++ ++ if (!bdrv_co_is_inserted(bs)) { ++ return -ENOMEDIUM; ++ } ++ ++ if (flags & BDRV_REQ_ZERO_WRITE) { ++ ret = bdrv_check_qiov_request(offset, bytes, qiov, qiov_offset, NULL); ++ } else { ++ ret = bdrv_check_request32(offset, bytes, qiov, qiov_offset); ++ } ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* If the request is misaligned then we can't make it efficient */ ++ if ((flags & BDRV_REQ_NO_FALLBACK) && ++ !QEMU_IS_ALIGNED(offset | bytes, align)) ++ { ++ return -ENOTSUP; ++ } ++ ++ if (bytes == 0 && !QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)) { ++ /* ++ * Aligning zero request is nonsense. Even if driver has special meaning ++ * of zero-length (like qcow2_co_pwritev_compressed_part), we can't pass ++ * it to driver due to request_alignment. ++ * ++ * Still, no reason to return an error if someone do unaligned ++ * zero-length write occasionally. ++ */ ++ return 0; ++ } ++ ++ if (!(flags & BDRV_REQ_ZERO_WRITE)) { ++ /* ++ * Pad request for following read-modify-write cycle. ++ * bdrv_co_do_zero_pwritev() does aligning by itself, so, we do ++ * alignment only if there is no ZERO flag. ++ */ ++ ret = bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, true, ++ &pad, &padded, &flags); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ bdrv_inc_in_flight(bs); ++ tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE); ++ ++ if (flags & BDRV_REQ_ZERO_WRITE) { ++ assert(!padded); ++ ret = bdrv_co_do_zero_pwritev(child, offset, bytes, flags, &req); ++ goto out; ++ } ++ ++ if (padded) { ++ /* ++ * Request was unaligned to request_alignment and therefore ++ * padded. We are going to do read-modify-write, and must ++ * serialize the request to prevent interactions of the ++ * widened region with other transactions. ++ */ ++ assert(!(flags & BDRV_REQ_NO_WAIT)); ++ bdrv_make_request_serialising(&req, align); ++ bdrv_padding_rmw_read(child, &req, &pad, false); ++ } ++ ++ ret = bdrv_aligned_pwritev(child, &req, offset, bytes, align, ++ qiov, qiov_offset, flags); ++ ++ bdrv_padding_finalize(&pad); ++ ++out: ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags) ++{ ++ IO_CODE(); ++ trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); ++ assert_bdrv_graph_readable(); ++ ++ return bdrv_co_pwritev(child, offset, bytes, NULL, ++ BDRV_REQ_ZERO_WRITE | flags); ++} ++ ++/* ++ * Flush ALL BDSes regardless of if they are reachable via a BlkBackend or not. ++ */ ++int bdrv_flush_all(void) ++{ ++ BdrvNextIterator it; ++ BlockDriverState *bs = NULL; ++ int result = 0; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* ++ * bdrv queue is managed by record/replay, ++ * creating new flush request for stopping ++ * the VM may break the determinism ++ */ ++ if (replay_events_enabled()) { ++ return result; ++ } ++ ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ int ret = bdrv_flush(bs); ++ if (ret < 0 && !result) { ++ result = ret; ++ } ++ } ++ ++ return result; ++} ++ ++/* ++ * Returns the allocation status of the specified sectors. ++ * Drivers not implementing the functionality are assumed to not support ++ * backing files, hence all their sectors are reported as allocated. ++ * ++ * If 'want_zero' is true, the caller is querying for mapping ++ * purposes, with a focus on valid BDRV_BLOCK_OFFSET_VALID, _DATA, and ++ * _ZERO where possible; otherwise, the result favors larger 'pnum', ++ * with a focus on accurate BDRV_BLOCK_ALLOCATED. ++ * ++ * If 'offset' is beyond the end of the disk image the return value is ++ * BDRV_BLOCK_EOF and 'pnum' is set to 0. ++ * ++ * 'bytes' is the max value 'pnum' should be set to. If bytes goes ++ * beyond the end of the disk image it will be clamped; if 'pnum' is set to ++ * the end of the image, then the returned value will include BDRV_BLOCK_EOF. ++ * ++ * 'pnum' is set to the number of bytes (including and immediately ++ * following the specified offset) that are easily known to be in the ++ * same allocated/unallocated state. Note that a second call starting ++ * at the original offset plus returned pnum may have the same status. ++ * The returned value is non-zero on success except at end-of-file. ++ * ++ * Returns negative errno on failure. Otherwise, if the ++ * BDRV_BLOCK_OFFSET_VALID bit is set, 'map' and 'file' (if non-NULL) are ++ * set to the host mapping and BDS corresponding to the guest offset. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file) ++{ ++ int64_t total_size; ++ int64_t n; /* bytes */ ++ int ret; ++ int64_t local_map = 0; ++ BlockDriverState *local_file = NULL; ++ int64_t aligned_offset, aligned_bytes; ++ uint32_t align; ++ bool has_filtered_child; ++ ++ assert(pnum); ++ assert_bdrv_graph_readable(); ++ *pnum = 0; ++ total_size = bdrv_co_getlength(bs); ++ if (total_size < 0) { ++ ret = total_size; ++ goto early_out; ++ } ++ ++ if (offset >= total_size) { ++ ret = BDRV_BLOCK_EOF; ++ goto early_out; ++ } ++ if (!bytes) { ++ ret = 0; ++ goto early_out; ++ } ++ ++ n = total_size - offset; ++ if (n < bytes) { ++ bytes = n; ++ } ++ ++ /* Must be non-NULL or bdrv_co_getlength() would have failed */ ++ assert(bs->drv); ++ has_filtered_child = bdrv_filter_child(bs); ++ if (!bs->drv->bdrv_co_block_status && !has_filtered_child) { ++ *pnum = bytes; ++ ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; ++ if (offset + bytes == total_size) { ++ ret |= BDRV_BLOCK_EOF; ++ } ++ if (bs->drv->protocol_name) { ++ ret |= BDRV_BLOCK_OFFSET_VALID; ++ local_map = offset; ++ local_file = bs; ++ } ++ goto early_out; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ /* Round out to request_alignment boundaries */ ++ align = bs->bl.request_alignment; ++ aligned_offset = QEMU_ALIGN_DOWN(offset, align); ++ aligned_bytes = ROUND_UP(offset + bytes, align) - aligned_offset; ++ ++ if (bs->drv->bdrv_co_block_status) { ++ /* ++ * Use the block-status cache only for protocol nodes: Format ++ * drivers are generally quick to inquire the status, but protocol ++ * drivers often need to get information from outside of qemu, so ++ * we do not have control over the actual implementation. There ++ * have been cases where inquiring the status took an unreasonably ++ * long time, and we can do nothing in qemu to fix it. ++ * This is especially problematic for images with large data areas, ++ * because finding the few holes in them and giving them special ++ * treatment does not gain much performance. Therefore, we try to ++ * cache the last-identified data region. ++ * ++ * Second, limiting ourselves to protocol nodes allows us to assume ++ * the block status for data regions to be DATA | OFFSET_VALID, and ++ * that the host offset is the same as the guest offset. ++ * ++ * Note that it is possible that external writers zero parts of ++ * the cached regions without the cache being invalidated, and so ++ * we may report zeroes as data. This is not catastrophic, ++ * however, because reporting zeroes as data is fine. ++ */ ++ if (QLIST_EMPTY(&bs->children) && ++ bdrv_bsc_is_data(bs, aligned_offset, pnum)) ++ { ++ ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; ++ local_file = bs; ++ local_map = aligned_offset; ++ } else { ++ ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset, ++ aligned_bytes, pnum, &local_map, ++ &local_file); ++ ++ /* ++ * Note that checking QLIST_EMPTY(&bs->children) is also done when ++ * the cache is queried above. Technically, we do not need to check ++ * it here; the worst that can happen is that we fill the cache for ++ * non-protocol nodes, and then it is never used. However, filling ++ * the cache requires an RCU update, so double check here to avoid ++ * such an update if possible. ++ * ++ * Check want_zero, because we only want to update the cache when we ++ * have accurate information about what is zero and what is data. ++ */ ++ if (want_zero && ++ ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) && ++ QLIST_EMPTY(&bs->children)) ++ { ++ /* ++ * When a protocol driver reports BLOCK_OFFSET_VALID, the ++ * returned local_map value must be the same as the offset we ++ * have passed (aligned_offset), and local_bs must be the node ++ * itself. ++ * Assert this, because we follow this rule when reading from ++ * the cache (see the `local_file = bs` and ++ * `local_map = aligned_offset` assignments above), and the ++ * result the cache delivers must be the same as the driver ++ * would deliver. ++ */ ++ assert(local_file == bs); ++ assert(local_map == aligned_offset); ++ bdrv_bsc_fill(bs, aligned_offset, *pnum); ++ } ++ } ++ } else { ++ /* Default code for filters */ ++ ++ local_file = bdrv_filter_bs(bs); ++ assert(local_file); ++ ++ *pnum = aligned_bytes; ++ local_map = aligned_offset; ++ ret = BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; ++ } ++ if (ret < 0) { ++ *pnum = 0; ++ goto out; ++ } ++ ++ /* ++ * The driver's result must be a non-zero multiple of request_alignment. ++ * Clamp pnum and adjust map to original request. ++ */ ++ assert(*pnum && QEMU_IS_ALIGNED(*pnum, align) && ++ align > offset - aligned_offset); ++ if (ret & BDRV_BLOCK_RECURSE) { ++ assert(ret & BDRV_BLOCK_DATA); ++ assert(ret & BDRV_BLOCK_OFFSET_VALID); ++ assert(!(ret & BDRV_BLOCK_ZERO)); ++ } ++ ++ *pnum -= offset - aligned_offset; ++ if (*pnum > bytes) { ++ *pnum = bytes; ++ } ++ if (ret & BDRV_BLOCK_OFFSET_VALID) { ++ local_map += offset - aligned_offset; ++ } ++ ++ if (ret & BDRV_BLOCK_RAW) { ++ assert(ret & BDRV_BLOCK_OFFSET_VALID && local_file); ++ ret = bdrv_co_do_block_status(local_file, want_zero, local_map, ++ *pnum, pnum, &local_map, &local_file); ++ goto out; ++ } ++ ++ if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) { ++ ret |= BDRV_BLOCK_ALLOCATED; ++ } else if (bs->drv->supports_backing) { ++ BlockDriverState *cow_bs = bdrv_cow_bs(bs); ++ ++ if (!cow_bs) { ++ ret |= BDRV_BLOCK_ZERO; ++ } else if (want_zero) { ++ int64_t size2 = bdrv_co_getlength(cow_bs); ++ ++ if (size2 >= 0 && offset >= size2) { ++ ret |= BDRV_BLOCK_ZERO; ++ } ++ } ++ } ++ ++ if (want_zero && ret & BDRV_BLOCK_RECURSE && ++ local_file && local_file != bs && ++ (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && ++ (ret & BDRV_BLOCK_OFFSET_VALID)) { ++ int64_t file_pnum; ++ int ret2; ++ ++ ret2 = bdrv_co_do_block_status(local_file, want_zero, local_map, ++ *pnum, &file_pnum, NULL, NULL); ++ if (ret2 >= 0) { ++ /* Ignore errors. This is just providing extra information, it ++ * is useful but not necessary. ++ */ ++ if (ret2 & BDRV_BLOCK_EOF && ++ (!file_pnum || ret2 & BDRV_BLOCK_ZERO)) { ++ /* ++ * It is valid for the format block driver to read ++ * beyond the end of the underlying file's current ++ * size; such areas read as zero. ++ */ ++ ret |= BDRV_BLOCK_ZERO; ++ } else { ++ /* Limit request to the range reported by the protocol driver */ ++ *pnum = file_pnum; ++ ret |= (ret2 & BDRV_BLOCK_ZERO); ++ } ++ } ++ ++ /* ++ * Now that the recursive search was done, clear the flag. Otherwise, ++ * with more complicated block graphs like snapshot-access -> ++ * copy-before-write -> qcow2, where the return value will be propagated ++ * further up to a parent bdrv_co_do_block_status() call, both the ++ * BDRV_BLOCK_RECURSE and BDRV_BLOCK_ZERO flags would be set, which is ++ * not allowed. ++ */ ++ ret &= ~BDRV_BLOCK_RECURSE; ++ } ++ ++out: ++ bdrv_dec_in_flight(bs); ++ if (ret >= 0 && offset + *pnum == total_size) { ++ ret |= BDRV_BLOCK_EOF; ++ } ++early_out: ++ if (file) { ++ *file = local_file; ++ } ++ if (map) { ++ *map = local_map; ++ } ++ return ret; ++} ++ ++int coroutine_fn ++bdrv_co_common_block_status_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ bool include_base, ++ bool want_zero, ++ int64_t offset, ++ int64_t bytes, ++ int64_t *pnum, ++ int64_t *map, ++ BlockDriverState **file, ++ int *depth) ++{ ++ int ret; ++ BlockDriverState *p; ++ int64_t eof = 0; ++ int dummy; ++ IO_CODE(); ++ ++ assert(!include_base || base); /* Can't include NULL base */ ++ assert_bdrv_graph_readable(); ++ ++ if (!depth) { ++ depth = &dummy; ++ } ++ *depth = 0; ++ ++ if (!include_base && bs == base) { ++ *pnum = bytes; ++ return 0; ++ } ++ ++ ret = bdrv_co_do_block_status(bs, want_zero, offset, bytes, pnum, ++ map, file); ++ ++*depth; ++ if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) { ++ return ret; ++ } ++ ++ if (ret & BDRV_BLOCK_EOF) { ++ eof = offset + *pnum; ++ } ++ ++ assert(*pnum <= bytes); ++ bytes = *pnum; ++ ++ for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base; ++ p = bdrv_filter_or_cow_bs(p)) ++ { ++ ret = bdrv_co_do_block_status(p, want_zero, offset, bytes, pnum, ++ map, file); ++ ++*depth; ++ if (ret < 0) { ++ return ret; ++ } ++ if (*pnum == 0) { ++ /* ++ * The top layer deferred to this layer, and because this layer is ++ * short, any zeroes that we synthesize beyond EOF behave as if they ++ * were allocated at this layer. ++ * ++ * We don't include BDRV_BLOCK_EOF into ret, as upper layer may be ++ * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see ++ * below. ++ */ ++ assert(ret & BDRV_BLOCK_EOF); ++ *pnum = bytes; ++ if (file) { ++ *file = p; ++ } ++ ret = BDRV_BLOCK_ZERO | BDRV_BLOCK_ALLOCATED; ++ break; ++ } ++ if (ret & BDRV_BLOCK_ALLOCATED) { ++ /* ++ * We've found the node and the status, we must break. ++ * ++ * Drop BDRV_BLOCK_EOF, as it's not for upper layer, which may be ++ * larger. We'll add BDRV_BLOCK_EOF if needed at function end, see ++ * below. ++ */ ++ ret &= ~BDRV_BLOCK_EOF; ++ break; ++ } ++ ++ if (p == base) { ++ assert(include_base); ++ break; ++ } ++ ++ /* ++ * OK, [offset, offset + *pnum) region is unallocated on this layer, ++ * let's continue the diving. ++ */ ++ assert(*pnum <= bytes); ++ bytes = *pnum; ++ } ++ ++ if (offset + *pnum == eof) { ++ ret |= BDRV_BLOCK_EOF; ++ } ++ ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ IO_CODE(); ++ return bdrv_co_common_block_status_above(bs, base, false, true, offset, ++ bytes, pnum, map, file, NULL); ++} ++ ++int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, int64_t *pnum, ++ int64_t *map, BlockDriverState **file) ++{ ++ IO_CODE(); ++ return bdrv_co_block_status_above(bs, bdrv_filter_or_cow_bs(bs), ++ offset, bytes, pnum, map, file); ++} ++ ++/* ++ * Check @bs (and its backing chain) to see if the range defined ++ * by @offset and @bytes is known to read as zeroes. ++ * Return 1 if that is the case, 0 otherwise and -errno on error. ++ * This test is meant to be fast rather than accurate so returning 0 ++ * does not guarantee non-zero data. ++ */ ++int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, ++ int64_t bytes) ++{ ++ int ret; ++ int64_t pnum = bytes; ++ IO_CODE(); ++ ++ if (!bytes) { ++ return 1; ++ } ++ ++ ret = bdrv_co_common_block_status_above(bs, NULL, false, false, offset, ++ bytes, &pnum, NULL, NULL, NULL); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); ++} ++ ++int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, int64_t *pnum) ++{ ++ int ret; ++ int64_t dummy; ++ IO_CODE(); ++ ++ ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset, ++ bytes, pnum ? pnum : &dummy, NULL, ++ NULL, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ return !!(ret & BDRV_BLOCK_ALLOCATED); ++} ++ ++/* ++ * Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP] ++ * ++ * Return a positive depth if (a prefix of) the given range is allocated ++ * in any image between BASE and TOP (BASE is only included if include_base ++ * is set). Depth 1 is TOP, 2 is the first backing layer, and so forth. ++ * BASE can be NULL to check if the given offset is allocated in any ++ * image of the chain. Return 0 otherwise, or negative errno on ++ * failure. ++ * ++ * 'pnum' is set to the number of bytes (including and immediately ++ * following the specified offset) that are known to be in the same ++ * allocated/unallocated state. Note that a subsequent call starting ++ * at 'offset + *pnum' may return the same allocation status (in other ++ * words, the result is not necessarily the maximum possible range); ++ * but 'pnum' will only be 0 when end of file is reached. ++ */ ++int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *bs, ++ BlockDriverState *base, ++ bool include_base, int64_t offset, ++ int64_t bytes, int64_t *pnum) ++{ ++ int depth; ++ int ret; ++ IO_CODE(); ++ ++ ret = bdrv_co_common_block_status_above(bs, base, include_base, false, ++ offset, bytes, pnum, NULL, NULL, ++ &depth); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (ret & BDRV_BLOCK_ALLOCATED) { ++ return depth; ++ } ++ return 0; ++} ++ ++int coroutine_fn ++bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *child_bs = bdrv_primary_bs(bs); ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ if (drv->bdrv_co_load_vmstate) { ++ ret = drv->bdrv_co_load_vmstate(bs, qiov, pos); ++ } else if (child_bs) { ++ ret = bdrv_co_readv_vmstate(child_bs, qiov, pos); ++ } else { ++ ret = -ENOTSUP; ++ } ++ ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int coroutine_fn ++bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *child_bs = bdrv_primary_bs(bs); ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ++ if (drv->bdrv_co_save_vmstate) { ++ ret = drv->bdrv_co_save_vmstate(bs, qiov, pos); ++ } else if (child_bs) { ++ ret = bdrv_co_writev_vmstate(child_bs, qiov, pos); ++ } else { ++ ret = -ENOTSUP; ++ } ++ ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, ++ int64_t pos, int size) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); ++ int ret = bdrv_writev_vmstate(bs, &qiov, pos); ++ IO_CODE(); ++ ++ return ret < 0 ? ret : size; ++} ++ ++int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, ++ int64_t pos, int size) ++{ ++ QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); ++ int ret = bdrv_readv_vmstate(bs, &qiov, pos); ++ IO_CODE(); ++ ++ return ret < 0 ? ret : size; ++} ++ ++/**************************************************************/ ++/* async I/Os */ ++ ++/** ++ * Synchronously cancels an acb. Must be called with the BQL held and the acb ++ * must be processed with the BQL held too (IOThreads are not allowed). ++ * ++ * Use bdrv_aio_cancel_async() instead when possible. ++ */ ++void bdrv_aio_cancel(BlockAIOCB *acb) ++{ ++ GLOBAL_STATE_CODE(); ++ qemu_aio_ref(acb); ++ bdrv_aio_cancel_async(acb); ++ AIO_WAIT_WHILE_UNLOCKED(NULL, acb->refcnt > 1); ++ qemu_aio_unref(acb); ++} ++ ++/* Async version of aio cancel. The caller is not blocked if the acb implements ++ * cancel_async, otherwise we do nothing and let the request normally complete. ++ * In either case the completion callback must be called. */ ++void bdrv_aio_cancel_async(BlockAIOCB *acb) ++{ ++ IO_CODE(); ++ if (acb->aiocb_info->cancel_async) { ++ acb->aiocb_info->cancel_async(acb); ++ } ++} ++ ++/**************************************************************/ ++/* Coroutine block device emulation */ ++ ++int coroutine_fn bdrv_co_flush(BlockDriverState *bs) ++{ ++ BdrvChild *primary_child = bdrv_primary_child(bs); ++ BdrvChild *child; ++ int current_gen; ++ int ret = 0; ++ IO_CODE(); ++ ++ assert_bdrv_graph_readable(); ++ bdrv_inc_in_flight(bs); ++ ++ if (!bdrv_co_is_inserted(bs) || bdrv_is_read_only(bs) || ++ bdrv_is_sg(bs)) { ++ goto early_exit; ++ } ++ ++ qemu_mutex_lock(&bs->reqs_lock); ++ current_gen = qatomic_read(&bs->write_gen); ++ ++ /* Wait until any previous flushes are completed */ ++ while (bs->active_flush_req) { ++ qemu_co_queue_wait(&bs->flush_queue, &bs->reqs_lock); ++ } ++ ++ /* Flushes reach this point in nondecreasing current_gen order. */ ++ bs->active_flush_req = true; ++ qemu_mutex_unlock(&bs->reqs_lock); ++ ++ /* Write back all layers by calling one driver function */ ++ if (bs->drv->bdrv_co_flush) { ++ ret = bs->drv->bdrv_co_flush(bs); ++ goto out; ++ } ++ ++ /* Write back cached data to the OS even with cache=unsafe */ ++ BLKDBG_CO_EVENT(primary_child, BLKDBG_FLUSH_TO_OS); ++ if (bs->drv->bdrv_co_flush_to_os) { ++ ret = bs->drv->bdrv_co_flush_to_os(bs); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ /* But don't actually force it to the disk with cache=unsafe */ ++ if (bs->open_flags & BDRV_O_NO_FLUSH) { ++ goto flush_children; ++ } ++ ++ /* Check if we really need to flush anything */ ++ if (bs->flushed_gen == current_gen) { ++ goto flush_children; ++ } ++ ++ BLKDBG_CO_EVENT(primary_child, BLKDBG_FLUSH_TO_DISK); ++ if (!bs->drv) { ++ /* bs->drv->bdrv_co_flush() might have ejected the BDS ++ * (even in case of apparent success) */ ++ ret = -ENOMEDIUM; ++ goto out; ++ } ++ if (bs->drv->bdrv_co_flush_to_disk) { ++ ret = bs->drv->bdrv_co_flush_to_disk(bs); ++ } else if (bs->drv->bdrv_aio_flush) { ++ BlockAIOCB *acb; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ ++ acb = bs->drv->bdrv_aio_flush(bs, bdrv_co_io_em_complete, &co); ++ if (acb == NULL) { ++ ret = -EIO; ++ } else { ++ qemu_coroutine_yield(); ++ ret = co.ret; ++ } ++ } else { ++ /* ++ * Some block drivers always operate in either writethrough or unsafe ++ * mode and don't support bdrv_flush therefore. Usually qemu doesn't ++ * know how the server works (because the behaviour is hardcoded or ++ * depends on server-side configuration), so we can't ensure that ++ * everything is safe on disk. Returning an error doesn't work because ++ * that would break guests even if the server operates in writethrough ++ * mode. ++ * ++ * Let's hope the user knows what he's doing. ++ */ ++ ret = 0; ++ } ++ ++ if (ret < 0) { ++ goto out; ++ } ++ ++ /* Now flush the underlying protocol. It will also have BDRV_O_NO_FLUSH ++ * in the case of cache=unsafe, so there are no useless flushes. ++ */ ++flush_children: ++ ret = 0; ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) { ++ int this_child_ret = bdrv_co_flush(child->bs); ++ if (!ret) { ++ ret = this_child_ret; ++ } ++ } ++ } ++ ++out: ++ /* Notify any pending flushes that we have completed */ ++ if (ret == 0) { ++ bs->flushed_gen = current_gen; ++ } ++ ++ qemu_mutex_lock(&bs->reqs_lock); ++ bs->active_flush_req = false; ++ /* Return value is ignored - it's ok if wait queue is empty */ ++ qemu_co_queue_next(&bs->flush_queue); ++ qemu_mutex_unlock(&bs->reqs_lock); ++ ++early_exit: ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, ++ int64_t bytes) ++{ ++ BdrvTrackedRequest req; ++ int ret; ++ int64_t max_pdiscard; ++ int head, tail, align; ++ BlockDriverState *bs = child->bs; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!bs || !bs->drv || !bdrv_co_is_inserted(bs)) { ++ return -ENOMEDIUM; ++ } ++ ++ if (bdrv_has_readonly_bitmaps(bs)) { ++ return -EPERM; ++ } ++ ++ ret = bdrv_check_request(offset, bytes, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Do nothing if disabled. */ ++ if (!(bs->open_flags & BDRV_O_UNMAP)) { ++ return 0; ++ } ++ ++ if (!bs->drv->bdrv_co_pdiscard && !bs->drv->bdrv_aio_pdiscard) { ++ return 0; ++ } ++ ++ /* Invalidate the cached block-status data range if this discard overlaps */ ++ bdrv_bsc_invalidate_range(bs, offset, bytes); ++ ++ /* Discard is advisory, but some devices track and coalesce ++ * unaligned requests, so we must pass everything down rather than ++ * round here. Still, most devices will just silently ignore ++ * unaligned requests (by returning -ENOTSUP), so we must fragment ++ * the request accordingly. */ ++ align = MAX(bs->bl.pdiscard_alignment, bs->bl.request_alignment); ++ assert(align % bs->bl.request_alignment == 0); ++ head = offset % align; ++ tail = (offset + bytes) % align; ++ ++ bdrv_inc_in_flight(bs); ++ tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_DISCARD); ++ ++ ret = bdrv_co_write_req_prepare(child, offset, bytes, &req, 0); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ max_pdiscard = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_pdiscard, INT64_MAX), ++ align); ++ assert(max_pdiscard >= bs->bl.request_alignment); ++ ++ while (bytes > 0) { ++ int64_t num = bytes; ++ ++ if (head) { ++ /* Make small requests to get to alignment boundaries. */ ++ num = MIN(bytes, align - head); ++ if (!QEMU_IS_ALIGNED(num, bs->bl.request_alignment)) { ++ num %= bs->bl.request_alignment; ++ } ++ head = (head + num) % align; ++ assert(num < max_pdiscard); ++ } else if (tail) { ++ if (num > align) { ++ /* Shorten the request to the last aligned cluster. */ ++ num -= tail; ++ } else if (!QEMU_IS_ALIGNED(tail, bs->bl.request_alignment) && ++ tail > bs->bl.request_alignment) { ++ tail %= bs->bl.request_alignment; ++ num -= tail; ++ } ++ } ++ /* limit request size */ ++ if (num > max_pdiscard) { ++ num = max_pdiscard; ++ } ++ ++ if (!bs->drv) { ++ ret = -ENOMEDIUM; ++ goto out; ++ } ++ if (bs->drv->bdrv_co_pdiscard) { ++ ret = bs->drv->bdrv_co_pdiscard(bs, offset, num); ++ } else { ++ BlockAIOCB *acb; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ ++ acb = bs->drv->bdrv_aio_pdiscard(bs, offset, num, ++ bdrv_co_io_em_complete, &co); ++ if (acb == NULL) { ++ ret = -EIO; ++ goto out; ++ } else { ++ qemu_coroutine_yield(); ++ ret = co.ret; ++ } ++ } ++ if (ret && ret != -ENOTSUP) { ++ goto out; ++ } ++ ++ offset += num; ++ bytes -= num; ++ } ++ ret = 0; ++out: ++ bdrv_co_write_req_finish(child, req.offset, req.bytes, &req, ret); ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(bs); ++ return ret; ++} ++ ++int coroutine_fn bdrv_co_ioctl(BlockDriverState *bs, int req, void *buf) ++{ ++ BlockDriver *drv = bs->drv; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ BlockAIOCB *acb; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ bdrv_inc_in_flight(bs); ++ if (!drv || (!drv->bdrv_aio_ioctl && !drv->bdrv_co_ioctl)) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ ++ if (drv->bdrv_co_ioctl) { ++ co.ret = drv->bdrv_co_ioctl(bs, req, buf); ++ } else { ++ acb = drv->bdrv_aio_ioctl(bs, req, buf, bdrv_co_io_em_complete, &co); ++ if (!acb) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ qemu_coroutine_yield(); ++ } ++out: ++ bdrv_dec_in_flight(bs); ++ return co.ret; ++} ++ ++int coroutine_fn bdrv_co_zone_report(BlockDriverState *bs, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones) ++{ ++ BlockDriver *drv = bs->drv; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ IO_CODE(); ++ ++ bdrv_inc_in_flight(bs); ++ if (!drv || !drv->bdrv_co_zone_report || bs->bl.zoned == BLK_Z_NONE) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ co.ret = drv->bdrv_co_zone_report(bs, offset, nr_zones, zones); ++out: ++ bdrv_dec_in_flight(bs); ++ return co.ret; ++} ++ ++int coroutine_fn bdrv_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, ++ int64_t offset, int64_t len) ++{ ++ BlockDriver *drv = bs->drv; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ IO_CODE(); ++ ++ bdrv_inc_in_flight(bs); ++ if (!drv || !drv->bdrv_co_zone_mgmt || bs->bl.zoned == BLK_Z_NONE) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ co.ret = drv->bdrv_co_zone_mgmt(bs, op, offset, len); ++out: ++ bdrv_dec_in_flight(bs); ++ return co.ret; ++} ++ ++int coroutine_fn bdrv_co_zone_append(BlockDriverState *bs, int64_t *offset, ++ QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ BlockDriver *drv = bs->drv; ++ CoroutineIOCompletion co = { ++ .coroutine = qemu_coroutine_self(), ++ }; ++ IO_CODE(); ++ ++ ret = bdrv_check_qiov_request(*offset, qiov->size, qiov, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ if (!drv || !drv->bdrv_co_zone_append || bs->bl.zoned == BLK_Z_NONE) { ++ co.ret = -ENOTSUP; ++ goto out; ++ } ++ co.ret = drv->bdrv_co_zone_append(bs, offset, qiov, flags); ++out: ++ bdrv_dec_in_flight(bs); ++ return co.ret; ++} ++ ++void *qemu_blockalign(BlockDriverState *bs, size_t size) ++{ ++ IO_CODE(); ++ return qemu_memalign(bdrv_opt_mem_align(bs), size); ++} ++ ++void *qemu_blockalign0(BlockDriverState *bs, size_t size) ++{ ++ IO_CODE(); ++ return memset(qemu_blockalign(bs, size), 0, size); ++} ++ ++void *qemu_try_blockalign(BlockDriverState *bs, size_t size) ++{ ++ size_t align = bdrv_opt_mem_align(bs); ++ IO_CODE(); ++ ++ /* Ensure that NULL is never returned on success */ ++ assert(align > 0); ++ if (size == 0) { ++ size = align; ++ } ++ ++ return qemu_try_memalign(align, size); ++} ++ ++void *qemu_try_blockalign0(BlockDriverState *bs, size_t size) ++{ ++ void *mem = qemu_try_blockalign(bs, size); ++ IO_CODE(); ++ ++ if (mem) { ++ memset(mem, 0, size); ++ } ++ ++ return mem; ++} ++ ++/* Helper that undoes bdrv_register_buf() when it fails partway through */ ++static void GRAPH_RDLOCK ++bdrv_register_buf_rollback(BlockDriverState *bs, void *host, size_t size, ++ BdrvChild *final_child) ++{ ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child == final_child) { ++ break; ++ } ++ ++ bdrv_unregister_buf(child->bs, host, size); ++ } ++ ++ if (bs->drv && bs->drv->bdrv_unregister_buf) { ++ bs->drv->bdrv_unregister_buf(bs, host, size); ++ } ++} ++ ++bool bdrv_register_buf(BlockDriverState *bs, void *host, size_t size, ++ Error **errp) ++{ ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bs->drv && bs->drv->bdrv_register_buf) { ++ if (!bs->drv->bdrv_register_buf(bs, host, size, errp)) { ++ return false; ++ } ++ } ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (!bdrv_register_buf(child->bs, host, size, errp)) { ++ bdrv_register_buf_rollback(bs, host, size, child); ++ return false; ++ } ++ } ++ return true; ++} ++ ++void bdrv_unregister_buf(BlockDriverState *bs, void *host, size_t size) ++{ ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bs->drv && bs->drv->bdrv_unregister_buf) { ++ bs->drv->bdrv_unregister_buf(bs, host, size); ++ } ++ QLIST_FOREACH(child, &bs->children, next) { ++ bdrv_unregister_buf(child->bs, host, size); ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK bdrv_co_copy_range_internal( ++ BdrvChild *src, int64_t src_offset, BdrvChild *dst, ++ int64_t dst_offset, int64_t bytes, ++ BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, ++ bool recurse_src) ++{ ++ BdrvTrackedRequest req; ++ int ret; ++ assert_bdrv_graph_readable(); ++ ++ /* TODO We can support BDRV_REQ_NO_FALLBACK here */ ++ assert(!(read_flags & BDRV_REQ_NO_FALLBACK)); ++ assert(!(write_flags & BDRV_REQ_NO_FALLBACK)); ++ assert(!(read_flags & BDRV_REQ_NO_WAIT)); ++ assert(!(write_flags & BDRV_REQ_NO_WAIT)); ++ ++ if (!dst || !dst->bs || !bdrv_co_is_inserted(dst->bs)) { ++ return -ENOMEDIUM; ++ } ++ ret = bdrv_check_request32(dst_offset, bytes, NULL, 0); ++ if (ret) { ++ return ret; ++ } ++ if (write_flags & BDRV_REQ_ZERO_WRITE) { ++ return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); ++ } ++ ++ if (!src || !src->bs || !bdrv_co_is_inserted(src->bs)) { ++ return -ENOMEDIUM; ++ } ++ ret = bdrv_check_request32(src_offset, bytes, NULL, 0); ++ if (ret) { ++ return ret; ++ } ++ ++ if (!src->bs->drv->bdrv_co_copy_range_from ++ || !dst->bs->drv->bdrv_co_copy_range_to ++ || src->bs->encrypted || dst->bs->encrypted) { ++ return -ENOTSUP; ++ } ++ ++ if (recurse_src) { ++ bdrv_inc_in_flight(src->bs); ++ tracked_request_begin(&req, src->bs, src_offset, bytes, ++ BDRV_TRACKED_READ); ++ ++ /* BDRV_REQ_SERIALISING is only for write operation */ ++ assert(!(read_flags & BDRV_REQ_SERIALISING)); ++ bdrv_wait_serialising_requests(&req); ++ ++ ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, ++ src, src_offset, ++ dst, dst_offset, ++ bytes, ++ read_flags, write_flags); ++ ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(src->bs); ++ } else { ++ bdrv_inc_in_flight(dst->bs); ++ tracked_request_begin(&req, dst->bs, dst_offset, bytes, ++ BDRV_TRACKED_WRITE); ++ ret = bdrv_co_write_req_prepare(dst, dst_offset, bytes, &req, ++ write_flags); ++ if (!ret) { ++ ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, ++ src, src_offset, ++ dst, dst_offset, ++ bytes, ++ read_flags, write_flags); ++ } ++ bdrv_co_write_req_finish(dst, dst_offset, bytes, &req, ret); ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(dst->bs); ++ } ++ ++ return ret; ++} ++ ++/* Copy range from @src to @dst. ++ * ++ * See the comment of bdrv_co_copy_range for the parameter and return value ++ * semantics. */ ++int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, ++ read_flags, write_flags); ++ return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, ++ bytes, read_flags, write_flags, true); ++} ++ ++/* Copy range from @src to @dst. ++ * ++ * See the comment of bdrv_co_copy_range for the parameter and return value ++ * semantics. */ ++int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, ++ BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, ++ read_flags, write_flags); ++ return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, ++ bytes, read_flags, write_flags, false); ++} ++ ++int coroutine_fn bdrv_co_copy_range(BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ return bdrv_co_copy_range_from(src, src_offset, ++ dst, dst_offset, ++ bytes, read_flags, write_flags); ++} ++ ++static void coroutine_fn GRAPH_RDLOCK ++bdrv_parent_cb_resize(BlockDriverState *bs) ++{ ++ BdrvChild *c; ++ ++ assert_bdrv_graph_readable(); ++ ++ QLIST_FOREACH(c, &bs->parents, next_parent) { ++ if (c->klass->resize) { ++ c->klass->resize(c); ++ } ++ } ++} ++ ++/** ++ * Truncate file to 'offset' bytes (needed only for file protocols) ++ * ++ * If 'exact' is true, the file must be resized to exactly the given ++ * 'offset'. Otherwise, it is sufficient for the node to be at least ++ * 'offset' bytes in length. ++ */ ++int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, ++ Error **errp) ++{ ++ BlockDriverState *bs = child->bs; ++ BdrvChild *filtered, *backing; ++ BlockDriver *drv = bs->drv; ++ BdrvTrackedRequest req; ++ int64_t old_size, new_bytes; ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ ++ if (!drv) { ++ error_setg(errp, "No medium inserted"); ++ return -ENOMEDIUM; ++ } ++ if (offset < 0) { ++ error_setg(errp, "Image size cannot be negative"); ++ return -EINVAL; ++ } ++ ++ ret = bdrv_check_request(offset, 0, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ old_size = bdrv_co_getlength(bs); ++ if (old_size < 0) { ++ error_setg_errno(errp, -old_size, "Failed to get old image size"); ++ return old_size; ++ } ++ ++ if (bdrv_is_read_only(bs)) { ++ error_setg(errp, "Image is read-only"); ++ return -EACCES; ++ } ++ ++ if (offset > old_size) { ++ new_bytes = offset - old_size; ++ } else { ++ new_bytes = 0; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ tracked_request_begin(&req, bs, offset - new_bytes, new_bytes, ++ BDRV_TRACKED_TRUNCATE); ++ ++ /* If we are growing the image and potentially using preallocation for the ++ * new area, we need to make sure that no write requests are made to it ++ * concurrently or they might be overwritten by preallocation. */ ++ if (new_bytes) { ++ bdrv_make_request_serialising(&req, 1); ++ } ++ ret = bdrv_co_write_req_prepare(child, offset - new_bytes, new_bytes, &req, ++ 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to prepare request for truncation"); ++ goto out; ++ } ++ ++ filtered = bdrv_filter_child(bs); ++ backing = bdrv_cow_child(bs); ++ ++ /* ++ * If the image has a backing file that is large enough that it would ++ * provide data for the new area, we cannot leave it unallocated because ++ * then the backing file content would become visible. Instead, zero-fill ++ * the new area. ++ * ++ * Note that if the image has a backing file, but was opened without the ++ * backing file, taking care of keeping things consistent with that backing ++ * file is the user's responsibility. ++ */ ++ if (new_bytes && backing) { ++ int64_t backing_len; ++ ++ backing_len = bdrv_co_getlength(backing->bs); ++ if (backing_len < 0) { ++ ret = backing_len; ++ error_setg_errno(errp, -ret, "Could not get backing file size"); ++ goto out; ++ } ++ ++ if (backing_len > old_size) { ++ flags |= BDRV_REQ_ZERO_WRITE; ++ } ++ } ++ ++ if (drv->bdrv_co_truncate) { ++ if (flags & ~bs->supported_truncate_flags) { ++ error_setg(errp, "Block driver does not support requested flags"); ++ ret = -ENOTSUP; ++ goto out; ++ } ++ ret = drv->bdrv_co_truncate(bs, offset, exact, prealloc, flags, errp); ++ } else if (filtered) { ++ ret = bdrv_co_truncate(filtered, offset, exact, prealloc, flags, errp); ++ } else { ++ error_setg(errp, "Image format driver does not support resize"); ++ ret = -ENOTSUP; ++ goto out; ++ } ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = bdrv_co_refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not refresh total sector count"); ++ } else { ++ offset = bs->total_sectors * BDRV_SECTOR_SIZE; ++ } ++ /* ++ * It's possible that truncation succeeded but bdrv_refresh_total_sectors ++ * failed, but the latter doesn't affect how we should finish the request. ++ * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. ++ */ ++ bdrv_co_write_req_finish(child, offset - new_bytes, new_bytes, &req, 0); ++ ++out: ++ tracked_request_end(&req); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++void bdrv_cancel_in_flight(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!bs || !bs->drv) { ++ return; ++ } ++ ++ if (bs->drv->bdrv_cancel_in_flight) { ++ bs->drv->bdrv_cancel_in_flight(bs); ++ } ++} ++ ++int coroutine_fn ++bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ BlockDriverState *bs = child->bs; ++ BlockDriver *drv = bs->drv; ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (!drv->bdrv_co_preadv_snapshot) { ++ return -ENOTSUP; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ret = drv->bdrv_co_preadv_snapshot(bs, offset, bytes, qiov, qiov_offset); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int coroutine_fn ++bdrv_co_snapshot_block_status(BlockDriverState *bs, ++ bool want_zero, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ BlockDriver *drv = bs->drv; ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (!drv->bdrv_co_snapshot_block_status) { ++ return -ENOTSUP; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ret = drv->bdrv_co_snapshot_block_status(bs, want_zero, offset, bytes, ++ pnum, map, file); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} ++ ++int coroutine_fn ++bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BlockDriver *drv = bs->drv; ++ int ret; ++ IO_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ ++ if (!drv->bdrv_co_pdiscard_snapshot) { ++ return -ENOTSUP; ++ } ++ ++ bdrv_inc_in_flight(bs); ++ ret = drv->bdrv_co_pdiscard_snapshot(bs, offset, bytes); ++ bdrv_dec_in_flight(bs); ++ ++ return ret; ++} +diff --git a/qcow2/lib/block/linux-aio.c b/qcow2/lib/block/linux-aio.c +new file mode 100644 +index 00000000..e3b5ec9a +--- /dev/null ++++ b/qcow2/lib/block/linux-aio.c +@@ -0,0 +1,507 @@ ++/* ++ * Linux native AIO support. ++ * ++ * Copyright (C) 2009 IBM, Corp. ++ * Copyright (C) 2009 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++#include "qemu/osdep.h" ++#include "block/aio.h" ++#include "qemu/queue.h" ++#include "block/block.h" ++#include "block/raw-aio.h" ++#include "qemu/event_notifier.h" ++#include "qemu/coroutine.h" ++#include "qemu/defer-call.h" ++#include "qapi/error.h" ++#include "sysemu/block-backend.h" ++ ++/* Only used for assertions. */ ++#include "qemu/coroutine_int.h" ++ ++#include ++ ++/* ++ * Queue size (per-device). ++ * ++ * XXX: eventually we need to communicate this to the guest and/or make it ++ * tunable by the guest. If we get more outstanding requests at a time ++ * than this we will get EAGAIN from io_submit which is communicated to ++ * the guest as an I/O error. ++ */ ++#define MAX_EVENTS 1024 ++ ++/* Maximum number of requests in a batch. (default value) */ ++#define DEFAULT_MAX_BATCH 32 ++ ++struct qemu_laiocb { ++ Coroutine *co; ++ LinuxAioState *ctx; ++ struct iocb iocb; ++ ssize_t ret; ++ size_t nbytes; ++ QEMUIOVector *qiov; ++ bool is_read; ++ QSIMPLEQ_ENTRY(qemu_laiocb) next; ++}; ++ ++typedef struct { ++ unsigned int in_queue; ++ unsigned int in_flight; ++ bool blocked; ++ QSIMPLEQ_HEAD(, qemu_laiocb) pending; ++} LaioQueue; ++ ++struct LinuxAioState { ++ AioContext *aio_context; ++ ++ io_context_t ctx; ++ EventNotifier e; ++ ++ /* No locking required, only accessed from AioContext home thread */ ++ LaioQueue io_q; ++ QEMUBH *completion_bh; ++ int event_idx; ++ int event_max; ++}; ++ ++static void ioq_submit(LinuxAioState *s); ++ ++static inline ssize_t io_event_ret(struct io_event *ev) ++{ ++ return (ssize_t)(((uint64_t)ev->res2 << 32) | ev->res); ++} ++ ++/* ++ * Completes an AIO request. ++ */ ++static void qemu_laio_process_completion(struct qemu_laiocb *laiocb) ++{ ++ int ret; ++ ++ ret = laiocb->ret; ++ if (ret != -ECANCELED) { ++ if (ret == laiocb->nbytes) { ++ ret = 0; ++ } else if (ret >= 0) { ++ /* Short reads mean EOF, pad with zeros. */ ++ if (laiocb->is_read) { ++ qemu_iovec_memset(laiocb->qiov, ret, 0, ++ laiocb->qiov->size - ret); ++ } else { ++ ret = -ENOSPC; ++ } ++ } ++ } ++ ++ laiocb->ret = ret; ++ ++ /* ++ * If the coroutine is already entered it must be in ioq_submit() and ++ * will notice laio->ret has been filled in when it eventually runs ++ * later. Coroutines cannot be entered recursively so avoid doing ++ * that! ++ */ ++ assert(laiocb->co->ctx == laiocb->ctx->aio_context); ++ if (!qemu_coroutine_entered(laiocb->co)) { ++ aio_co_wake(laiocb->co); ++ } ++} ++ ++/** ++ * aio_ring buffer which is shared between userspace and kernel. ++ * ++ * This copied from linux/fs/aio.c, common header does not exist ++ * but AIO exists for ages so we assume ABI is stable. ++ */ ++struct aio_ring { ++ unsigned id; /* kernel internal index number */ ++ unsigned nr; /* number of io_events */ ++ unsigned head; /* Written to by userland or by kernel. */ ++ unsigned tail; ++ ++ unsigned magic; ++ unsigned compat_features; ++ unsigned incompat_features; ++ unsigned header_length; /* size of aio_ring */ ++ ++ struct io_event io_events[]; ++}; ++ ++/** ++ * io_getevents_peek: ++ * @ctx: AIO context ++ * @events: pointer on events array, output value ++ ++ * Returns the number of completed events and sets a pointer ++ * on events array. This function does not update the internal ++ * ring buffer, only reads head and tail. When @events has been ++ * processed io_getevents_commit() must be called. ++ */ ++static inline unsigned int io_getevents_peek(io_context_t ctx, ++ struct io_event **events) ++{ ++ struct aio_ring *ring = (struct aio_ring *)ctx; ++ unsigned int head = ring->head, tail = ring->tail; ++ unsigned int nr; ++ ++ nr = tail >= head ? tail - head : ring->nr - head; ++ *events = ring->io_events + head; ++ /* To avoid speculative loads of s->events[i] before observing tail. ++ Paired with smp_wmb() inside linux/fs/aio.c: aio_complete(). */ ++ smp_rmb(); ++ ++ return nr; ++} ++ ++/** ++ * io_getevents_commit: ++ * @ctx: AIO context ++ * @nr: the number of events on which head should be advanced ++ * ++ * Advances head of a ring buffer. ++ */ ++static inline void io_getevents_commit(io_context_t ctx, unsigned int nr) ++{ ++ struct aio_ring *ring = (struct aio_ring *)ctx; ++ ++ if (nr) { ++ ring->head = (ring->head + nr) % ring->nr; ++ } ++} ++ ++/** ++ * io_getevents_advance_and_peek: ++ * @ctx: AIO context ++ * @events: pointer on events array, output value ++ * @nr: the number of events on which head should be advanced ++ * ++ * Advances head of a ring buffer and returns number of elements left. ++ */ ++static inline unsigned int ++io_getevents_advance_and_peek(io_context_t ctx, ++ struct io_event **events, ++ unsigned int nr) ++{ ++ io_getevents_commit(ctx, nr); ++ return io_getevents_peek(ctx, events); ++} ++ ++/** ++ * qemu_laio_process_completions: ++ * @s: AIO state ++ * ++ * Fetches completed I/O requests and invokes their callbacks. ++ * ++ * The function is somewhat tricky because it supports nested event loops, for ++ * example when a request callback invokes aio_poll(). In order to do this, ++ * indices are kept in LinuxAioState. Function schedules BH completion so it ++ * can be called again in a nested event loop. When there are no events left ++ * to complete the BH is being canceled. ++ */ ++static void qemu_laio_process_completions(LinuxAioState *s) ++{ ++ struct io_event *events; ++ ++ defer_call_begin(); ++ ++ /* Reschedule so nested event loops see currently pending completions */ ++ qemu_bh_schedule(s->completion_bh); ++ ++ while ((s->event_max = io_getevents_advance_and_peek(s->ctx, &events, ++ s->event_idx))) { ++ for (s->event_idx = 0; s->event_idx < s->event_max; ) { ++ struct iocb *iocb = events[s->event_idx].obj; ++ struct qemu_laiocb *laiocb = ++ container_of(iocb, struct qemu_laiocb, iocb); ++ ++ laiocb->ret = io_event_ret(&events[s->event_idx]); ++ ++ /* Change counters one-by-one because we can be nested. */ ++ s->io_q.in_flight--; ++ s->event_idx++; ++ qemu_laio_process_completion(laiocb); ++ } ++ } ++ ++ qemu_bh_cancel(s->completion_bh); ++ ++ /* If we are nested we have to notify the level above that we are done ++ * by setting event_max to zero, upper level will then jump out of it's ++ * own `for` loop. If we are the last all counters dropped to zero. */ ++ s->event_max = 0; ++ s->event_idx = 0; ++ ++ defer_call_end(); ++} ++ ++static void qemu_laio_process_completions_and_submit(LinuxAioState *s) ++{ ++ qemu_laio_process_completions(s); ++ ++ if (!QSIMPLEQ_EMPTY(&s->io_q.pending)) { ++ ioq_submit(s); ++ } ++} ++ ++static void qemu_laio_completion_bh(void *opaque) ++{ ++ LinuxAioState *s = opaque; ++ ++ qemu_laio_process_completions_and_submit(s); ++} ++ ++static void qemu_laio_completion_cb(EventNotifier *e) ++{ ++ LinuxAioState *s = container_of(e, LinuxAioState, e); ++ ++ if (event_notifier_test_and_clear(&s->e)) { ++ qemu_laio_process_completions_and_submit(s); ++ } ++} ++ ++static bool qemu_laio_poll_cb(void *opaque) ++{ ++ EventNotifier *e = opaque; ++ LinuxAioState *s = container_of(e, LinuxAioState, e); ++ struct io_event *events; ++ ++ return io_getevents_peek(s->ctx, &events); ++} ++ ++static void qemu_laio_poll_ready(EventNotifier *opaque) ++{ ++ EventNotifier *e = opaque; ++ LinuxAioState *s = container_of(e, LinuxAioState, e); ++ ++ qemu_laio_process_completions_and_submit(s); ++} ++ ++static void ioq_init(LaioQueue *io_q) ++{ ++ QSIMPLEQ_INIT(&io_q->pending); ++ io_q->in_queue = 0; ++ io_q->in_flight = 0; ++ io_q->blocked = false; ++} ++ ++static void ioq_submit(LinuxAioState *s) ++{ ++ int ret, len; ++ struct qemu_laiocb *aiocb; ++ struct iocb *iocbs[MAX_EVENTS]; ++ QSIMPLEQ_HEAD(, qemu_laiocb) completed; ++ ++ do { ++ if (s->io_q.in_flight >= MAX_EVENTS) { ++ break; ++ } ++ len = 0; ++ QSIMPLEQ_FOREACH(aiocb, &s->io_q.pending, next) { ++ iocbs[len++] = &aiocb->iocb; ++ if (s->io_q.in_flight + len >= MAX_EVENTS) { ++ break; ++ } ++ } ++ ++ ret = io_submit(s->ctx, len, iocbs); ++ if (ret == -EAGAIN) { ++ break; ++ } ++ if (ret < 0) { ++ /* Fail the first request, retry the rest */ ++ aiocb = QSIMPLEQ_FIRST(&s->io_q.pending); ++ QSIMPLEQ_REMOVE_HEAD(&s->io_q.pending, next); ++ s->io_q.in_queue--; ++ aiocb->ret = ret; ++ qemu_laio_process_completion(aiocb); ++ continue; ++ } ++ ++ s->io_q.in_flight += ret; ++ s->io_q.in_queue -= ret; ++ aiocb = container_of(iocbs[ret - 1], struct qemu_laiocb, iocb); ++ QSIMPLEQ_SPLIT_AFTER(&s->io_q.pending, aiocb, next, &completed); ++ } while (ret == len && !QSIMPLEQ_EMPTY(&s->io_q.pending)); ++ s->io_q.blocked = (s->io_q.in_queue > 0); ++ ++ if (s->io_q.in_flight) { ++ /* We can try to complete something just right away if there are ++ * still requests in-flight. */ ++ qemu_laio_process_completions(s); ++ /* ++ * Even we have completed everything (in_flight == 0), the queue can ++ * have still pended requests (in_queue > 0). We do not attempt to ++ * repeat submission to avoid IO hang. The reason is simple: s->e is ++ * still set and completion callback will be called shortly and all ++ * pended requests will be submitted from there. ++ */ ++ } ++} ++ ++static uint64_t laio_max_batch(LinuxAioState *s, uint64_t dev_max_batch) ++{ ++ uint64_t max_batch = s->aio_context->aio_max_batch ?: DEFAULT_MAX_BATCH; ++ ++ /* ++ * AIO context can be shared between multiple block devices, so ++ * `dev_max_batch` allows reducing the batch size for latency-sensitive ++ * devices. ++ */ ++ max_batch = MIN_NON_ZERO(dev_max_batch, max_batch); ++ ++ /* limit the batch with the number of available events */ ++ max_batch = MIN_NON_ZERO(MAX_EVENTS - s->io_q.in_flight, max_batch); ++ ++ return max_batch; ++} ++ ++static void laio_deferred_fn(void *opaque) ++{ ++ LinuxAioState *s = opaque; ++ ++ if (!s->io_q.blocked && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { ++ ioq_submit(s); ++ } ++} ++ ++static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, ++ int type, uint64_t dev_max_batch) ++{ ++ LinuxAioState *s = laiocb->ctx; ++ struct iocb *iocbs = &laiocb->iocb; ++ QEMUIOVector *qiov = laiocb->qiov; ++ ++ switch (type) { ++ case QEMU_AIO_WRITE: ++ io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); ++ break; ++ case QEMU_AIO_ZONE_APPEND: ++ io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); ++ break; ++ case QEMU_AIO_READ: ++ io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); ++ break; ++ case QEMU_AIO_FLUSH: ++ io_prep_fdsync(iocbs, fd); ++ break; ++ /* Currently Linux kernel does not support other operations */ ++ default: ++ fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", ++ __func__, type); ++ return -EIO; ++ } ++ io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e)); ++ ++ QSIMPLEQ_INSERT_TAIL(&s->io_q.pending, laiocb, next); ++ s->io_q.in_queue++; ++ if (!s->io_q.blocked) { ++ if (s->io_q.in_queue >= laio_max_batch(s, dev_max_batch)) { ++ ioq_submit(s); ++ } else { ++ defer_call(laio_deferred_fn, s); ++ } ++ } ++ ++ return 0; ++} ++ ++int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov, ++ int type, uint64_t dev_max_batch) ++{ ++ int ret; ++ AioContext *ctx = qemu_get_current_aio_context(); ++ struct qemu_laiocb laiocb = { ++ .co = qemu_coroutine_self(), ++ .nbytes = qiov ? qiov->size : 0, ++ .ctx = aio_get_linux_aio(ctx), ++ .ret = -EINPROGRESS, ++ .is_read = (type == QEMU_AIO_READ), ++ .qiov = qiov, ++ }; ++ ++ ret = laio_do_submit(fd, &laiocb, offset, type, dev_max_batch); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (laiocb.ret == -EINPROGRESS) { ++ qemu_coroutine_yield(); ++ } ++ return laiocb.ret; ++} ++ ++void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context) ++{ ++ aio_set_event_notifier(old_context, &s->e, NULL, NULL, NULL); ++ qemu_bh_delete(s->completion_bh); ++ s->aio_context = NULL; ++} ++ ++void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) ++{ ++ s->aio_context = new_context; ++ s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s); ++ aio_set_event_notifier(new_context, &s->e, ++ qemu_laio_completion_cb, ++ qemu_laio_poll_cb, ++ qemu_laio_poll_ready); ++} ++ ++LinuxAioState *laio_init(Error **errp) ++{ ++ int rc; ++ LinuxAioState *s; ++ ++ s = g_malloc0(sizeof(*s)); ++ rc = event_notifier_init(&s->e, false); ++ if (rc < 0) { ++ error_setg_errno(errp, -rc, "failed to initialize event notifier"); ++ goto out_free_state; ++ } ++ ++ rc = io_setup(MAX_EVENTS, &s->ctx); ++ if (rc < 0) { ++ error_setg_errno(errp, -rc, "failed to create linux AIO context"); ++ goto out_close_efd; ++ } ++ ++ ioq_init(&s->io_q); ++ ++ return s; ++ ++out_close_efd: ++ event_notifier_cleanup(&s->e); ++out_free_state: ++ g_free(s); ++ return NULL; ++} ++ ++void laio_cleanup(LinuxAioState *s) ++{ ++ event_notifier_cleanup(&s->e); ++ ++ if (io_destroy(s->ctx) != 0) { ++ fprintf(stderr, "%s: destroy AIO context %p failed\n", ++ __func__, &s->ctx); ++ } ++ g_free(s); ++} ++ ++bool laio_has_fdsync(int fd) ++{ ++ struct iocb cb; ++ struct iocb *cbs[] = {&cb, NULL}; ++ ++ io_context_t ctx = 0; ++ io_setup(1, &ctx); ++ ++ /* check if host kernel supports IO_CMD_FDSYNC */ ++ io_prep_fdsync(&cb, fd); ++ int ret = io_submit(ctx, 1, cbs); ++ ++ io_destroy(ctx); ++ return (ret == -EINVAL) ? false : true; ++} +diff --git a/qcow2/lib/block/mirror.c b/qcow2/lib/block/mirror.c +new file mode 100644 +index 00000000..61f0a717 +--- /dev/null ++++ b/qcow2/lib/block/mirror.c +@@ -0,0 +1,2082 @@ ++/* ++ * Image mirroring ++ * ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Authors: ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "qemu/coroutine.h" ++#include "qemu/range.h" ++#include "trace.h" ++#include "block/blockjob_int.h" ++#include "block/block_int.h" ++#include "block/dirty-bitmap.h" ++#include "sysemu/block-backend.h" ++#include "qapi/error.h" ++#include "qemu/ratelimit.h" ++#include "qemu/bitmap.h" ++#include "qemu/memalign.h" ++ ++#define MAX_IN_FLIGHT 16 ++#define MAX_IO_BYTES (1 << 20) /* 1 Mb */ ++#define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES) ++ ++/* The mirroring buffer is a list of granularity-sized chunks. ++ * Free chunks are organized in a list. ++ */ ++typedef struct MirrorBuffer { ++ QSIMPLEQ_ENTRY(MirrorBuffer) next; ++} MirrorBuffer; ++ ++typedef struct MirrorOp MirrorOp; ++ ++typedef struct MirrorBlockJob { ++ BlockJob common; ++ BlockBackend *target; ++ BlockDriverState *mirror_top_bs; ++ BlockDriverState *base; ++ BlockDriverState *base_overlay; ++ ++ /* The name of the graph node to replace */ ++ char *replaces; ++ /* The BDS to replace */ ++ BlockDriverState *to_replace; ++ /* Used to block operations on the drive-mirror-replace target */ ++ Error *replace_blocker; ++ bool is_none_mode; ++ BlockMirrorBackingMode backing_mode; ++ /* Whether the target image requires explicit zero-initialization */ ++ bool zero_target; ++ /* ++ * To be accesssed with atomics. Written only under the BQL (required by the ++ * current implementation of mirror_change()). ++ */ ++ MirrorCopyMode copy_mode; ++ BlockdevOnError on_source_error, on_target_error; ++ /* ++ * To be accessed with atomics. ++ * ++ * Set when the target is synced (dirty bitmap is clean, nothing in flight) ++ * and the job is running in active mode. ++ */ ++ bool actively_synced; ++ bool should_complete; ++ int64_t granularity; ++ size_t buf_size; ++ int64_t bdev_length; ++ unsigned long *cow_bitmap; ++ BdrvDirtyBitmap *dirty_bitmap; ++ BdrvDirtyBitmapIter *dbi; ++ uint8_t *buf; ++ QSIMPLEQ_HEAD(, MirrorBuffer) buf_free; ++ int buf_free_count; ++ ++ uint64_t last_pause_ns; ++ unsigned long *in_flight_bitmap; ++ unsigned in_flight; ++ int64_t bytes_in_flight; ++ QTAILQ_HEAD(, MirrorOp) ops_in_flight; ++ int ret; ++ bool unmap; ++ int target_cluster_size; ++ int max_iov; ++ bool initial_zeroing_ongoing; ++ int in_active_write_counter; ++ int64_t active_write_bytes_in_flight; ++ bool prepared; ++ bool in_drain; ++ bool base_ro; ++} MirrorBlockJob; ++ ++typedef struct MirrorBDSOpaque { ++ MirrorBlockJob *job; ++ bool stop; ++ bool is_commit; ++} MirrorBDSOpaque; ++ ++struct MirrorOp { ++ MirrorBlockJob *s; ++ QEMUIOVector qiov; ++ int64_t offset; ++ uint64_t bytes; ++ ++ /* The pointee is set by mirror_co_read(), mirror_co_zero(), and ++ * mirror_co_discard() before yielding for the first time */ ++ int64_t *bytes_handled; ++ ++ bool is_pseudo_op; ++ bool is_active_write; ++ bool is_in_flight; ++ CoQueue waiting_requests; ++ Coroutine *co; ++ MirrorOp *waiting_for_op; ++ ++ QTAILQ_ENTRY(MirrorOp) next; ++}; ++ ++typedef enum MirrorMethod { ++ MIRROR_METHOD_COPY, ++ MIRROR_METHOD_ZERO, ++ MIRROR_METHOD_DISCARD, ++} MirrorMethod; ++ ++static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, ++ int error) ++{ ++ qatomic_set(&s->actively_synced, false); ++ if (read) { ++ return block_job_error_action(&s->common, s->on_source_error, ++ true, error); ++ } else { ++ return block_job_error_action(&s->common, s->on_target_error, ++ false, error); ++ } ++} ++ ++static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self, ++ MirrorBlockJob *s, ++ uint64_t offset, ++ uint64_t bytes) ++{ ++ uint64_t self_start_chunk = offset / s->granularity; ++ uint64_t self_end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity); ++ uint64_t self_nb_chunks = self_end_chunk - self_start_chunk; ++ ++ while (find_next_bit(s->in_flight_bitmap, self_end_chunk, ++ self_start_chunk) < self_end_chunk && ++ s->ret >= 0) ++ { ++ MirrorOp *op; ++ ++ QTAILQ_FOREACH(op, &s->ops_in_flight, next) { ++ uint64_t op_start_chunk = op->offset / s->granularity; ++ uint64_t op_nb_chunks = DIV_ROUND_UP(op->offset + op->bytes, ++ s->granularity) - ++ op_start_chunk; ++ ++ if (op == self) { ++ continue; ++ } ++ ++ if (ranges_overlap(self_start_chunk, self_nb_chunks, ++ op_start_chunk, op_nb_chunks)) ++ { ++ if (self) { ++ /* ++ * If the operation is already (indirectly) waiting for us, ++ * or will wait for us as soon as it wakes up, then just go ++ * on (instead of producing a deadlock in the former case). ++ */ ++ if (op->waiting_for_op) { ++ continue; ++ } ++ ++ self->waiting_for_op = op; ++ } ++ ++ qemu_co_queue_wait(&op->waiting_requests, NULL); ++ ++ if (self) { ++ self->waiting_for_op = NULL; ++ } ++ ++ break; ++ } ++ } ++ } ++} ++ ++static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) ++{ ++ MirrorBlockJob *s = op->s; ++ struct iovec *iov; ++ int64_t chunk_num; ++ int i, nb_chunks; ++ ++ trace_mirror_iteration_done(s, op->offset, op->bytes, ret); ++ ++ s->in_flight--; ++ s->bytes_in_flight -= op->bytes; ++ iov = op->qiov.iov; ++ for (i = 0; i < op->qiov.niov; i++) { ++ MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base; ++ QSIMPLEQ_INSERT_TAIL(&s->buf_free, buf, next); ++ s->buf_free_count++; ++ } ++ ++ chunk_num = op->offset / s->granularity; ++ nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); ++ ++ bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); ++ QTAILQ_REMOVE(&s->ops_in_flight, op, next); ++ if (ret >= 0) { ++ if (s->cow_bitmap) { ++ bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); ++ } ++ if (!s->initial_zeroing_ongoing) { ++ job_progress_update(&s->common.job, op->bytes); ++ } ++ } ++ qemu_iovec_destroy(&op->qiov); ++ ++ qemu_co_queue_restart_all(&op->waiting_requests); ++ g_free(op); ++} ++ ++static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) ++{ ++ MirrorBlockJob *s = op->s; ++ ++ if (ret < 0) { ++ BlockErrorAction action; ++ ++ bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset, op->bytes); ++ action = mirror_error_action(s, false, -ret); ++ if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { ++ s->ret = ret; ++ } ++ } ++ ++ mirror_iteration_done(op, ret); ++} ++ ++static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret) ++{ ++ MirrorBlockJob *s = op->s; ++ ++ if (ret < 0) { ++ BlockErrorAction action; ++ ++ bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset, op->bytes); ++ action = mirror_error_action(s, true, -ret); ++ if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { ++ s->ret = ret; ++ } ++ ++ mirror_iteration_done(op, ret); ++ return; ++ } ++ ++ ret = blk_co_pwritev(s->target, op->offset, op->qiov.size, &op->qiov, 0); ++ mirror_write_complete(op, ret); ++} ++ ++/* Clip bytes relative to offset to not exceed end-of-file */ ++static inline int64_t mirror_clip_bytes(MirrorBlockJob *s, ++ int64_t offset, ++ int64_t bytes) ++{ ++ return MIN(bytes, s->bdev_length - offset); ++} ++ ++/* Round offset and/or bytes to target cluster if COW is needed, and ++ * return the offset of the adjusted tail against original. */ ++static int coroutine_fn mirror_cow_align(MirrorBlockJob *s, int64_t *offset, ++ uint64_t *bytes) ++{ ++ bool need_cow; ++ int ret = 0; ++ int64_t align_offset = *offset; ++ int64_t align_bytes = *bytes; ++ int max_bytes = s->granularity * s->max_iov; ++ ++ need_cow = !test_bit(*offset / s->granularity, s->cow_bitmap); ++ need_cow |= !test_bit((*offset + *bytes - 1) / s->granularity, ++ s->cow_bitmap); ++ if (need_cow) { ++ bdrv_round_to_subclusters(blk_bs(s->target), *offset, *bytes, ++ &align_offset, &align_bytes); ++ } ++ ++ if (align_bytes > max_bytes) { ++ align_bytes = max_bytes; ++ if (need_cow) { ++ align_bytes = QEMU_ALIGN_DOWN(align_bytes, s->target_cluster_size); ++ } ++ } ++ /* Clipping may result in align_bytes unaligned to chunk boundary, but ++ * that doesn't matter because it's already the end of source image. */ ++ align_bytes = mirror_clip_bytes(s, align_offset, align_bytes); ++ ++ ret = align_offset + align_bytes - (*offset + *bytes); ++ *offset = align_offset; ++ *bytes = align_bytes; ++ assert(ret >= 0); ++ return ret; ++} ++ ++static inline void coroutine_fn ++mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) ++{ ++ MirrorOp *op; ++ ++ QTAILQ_FOREACH(op, &s->ops_in_flight, next) { ++ /* ++ * Do not wait on pseudo ops, because it may in turn wait on ++ * some other operation to start, which may in fact be the ++ * caller of this function. Since there is only one pseudo op ++ * at any given time, we will always find some real operation ++ * to wait on. ++ * Also, do not wait on active operations, because they do not ++ * use up in-flight slots. ++ */ ++ if (!op->is_pseudo_op && op->is_in_flight && !op->is_active_write) { ++ qemu_co_queue_wait(&op->waiting_requests, NULL); ++ return; ++ } ++ } ++ abort(); ++} ++ ++/* Perform a mirror copy operation. ++ * ++ * *op->bytes_handled is set to the number of bytes copied after and ++ * including offset, excluding any bytes copied prior to offset due ++ * to alignment. This will be op->bytes if no alignment is necessary, ++ * or (new_end - op->offset) if the tail is rounded up or down due to ++ * alignment or buffer limit. ++ */ ++static void coroutine_fn mirror_co_read(void *opaque) ++{ ++ MirrorOp *op = opaque; ++ MirrorBlockJob *s = op->s; ++ int nb_chunks; ++ uint64_t ret; ++ uint64_t max_bytes; ++ ++ max_bytes = s->granularity * s->max_iov; ++ ++ /* We can only handle as much as buf_size at a time. */ ++ op->bytes = MIN(s->buf_size, MIN(max_bytes, op->bytes)); ++ assert(op->bytes); ++ assert(op->bytes < BDRV_REQUEST_MAX_BYTES); ++ *op->bytes_handled = op->bytes; ++ ++ if (s->cow_bitmap) { ++ *op->bytes_handled += mirror_cow_align(s, &op->offset, &op->bytes); ++ } ++ /* Cannot exceed BDRV_REQUEST_MAX_BYTES + INT_MAX */ ++ assert(*op->bytes_handled <= UINT_MAX); ++ assert(op->bytes <= s->buf_size); ++ /* The offset is granularity-aligned because: ++ * 1) Caller passes in aligned values; ++ * 2) mirror_cow_align is used only when target cluster is larger. */ ++ assert(QEMU_IS_ALIGNED(op->offset, s->granularity)); ++ /* The range is sector-aligned, since bdrv_getlength() rounds up. */ ++ assert(QEMU_IS_ALIGNED(op->bytes, BDRV_SECTOR_SIZE)); ++ nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); ++ ++ while (s->buf_free_count < nb_chunks) { ++ trace_mirror_yield_in_flight(s, op->offset, s->in_flight); ++ mirror_wait_for_free_in_flight_slot(s); ++ } ++ ++ /* Now make a QEMUIOVector taking enough granularity-sized chunks ++ * from s->buf_free. ++ */ ++ qemu_iovec_init(&op->qiov, nb_chunks); ++ while (nb_chunks-- > 0) { ++ MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); ++ size_t remaining = op->bytes - op->qiov.size; ++ ++ QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); ++ s->buf_free_count--; ++ qemu_iovec_add(&op->qiov, buf, MIN(s->granularity, remaining)); ++ } ++ ++ /* Copy the dirty cluster. */ ++ s->in_flight++; ++ s->bytes_in_flight += op->bytes; ++ op->is_in_flight = true; ++ trace_mirror_one_iteration(s, op->offset, op->bytes); ++ ++ WITH_GRAPH_RDLOCK_GUARD() { ++ ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes, ++ &op->qiov, 0); ++ } ++ mirror_read_complete(op, ret); ++} ++ ++static void coroutine_fn mirror_co_zero(void *opaque) ++{ ++ MirrorOp *op = opaque; ++ int ret; ++ ++ op->s->in_flight++; ++ op->s->bytes_in_flight += op->bytes; ++ *op->bytes_handled = op->bytes; ++ op->is_in_flight = true; ++ ++ ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, ++ op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); ++ mirror_write_complete(op, ret); ++} ++ ++static void coroutine_fn mirror_co_discard(void *opaque) ++{ ++ MirrorOp *op = opaque; ++ int ret; ++ ++ op->s->in_flight++; ++ op->s->bytes_in_flight += op->bytes; ++ *op->bytes_handled = op->bytes; ++ op->is_in_flight = true; ++ ++ ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes); ++ mirror_write_complete(op, ret); ++} ++ ++static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, ++ unsigned bytes, MirrorMethod mirror_method) ++{ ++ MirrorOp *op; ++ Coroutine *co; ++ int64_t bytes_handled = -1; ++ ++ op = g_new(MirrorOp, 1); ++ *op = (MirrorOp){ ++ .s = s, ++ .offset = offset, ++ .bytes = bytes, ++ .bytes_handled = &bytes_handled, ++ }; ++ qemu_co_queue_init(&op->waiting_requests); ++ ++ switch (mirror_method) { ++ case MIRROR_METHOD_COPY: ++ co = qemu_coroutine_create(mirror_co_read, op); ++ break; ++ case MIRROR_METHOD_ZERO: ++ co = qemu_coroutine_create(mirror_co_zero, op); ++ break; ++ case MIRROR_METHOD_DISCARD: ++ co = qemu_coroutine_create(mirror_co_discard, op); ++ break; ++ default: ++ abort(); ++ } ++ op->co = co; ++ ++ QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); ++ qemu_coroutine_enter(co); ++ /* At this point, ownership of op has been moved to the coroutine ++ * and the object may already be freed */ ++ ++ /* Assert that this value has been set */ ++ assert(bytes_handled >= 0); ++ ++ /* Same assertion as in mirror_co_read() (and for mirror_co_read() ++ * and mirror_co_discard(), bytes_handled == op->bytes, which ++ * is the @bytes parameter given to this function) */ ++ assert(bytes_handled <= UINT_MAX); ++ return bytes_handled; ++} ++ ++static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) ++{ ++ BlockDriverState *source; ++ MirrorOp *pseudo_op; ++ int64_t offset; ++ /* At least the first dirty chunk is mirrored in one iteration. */ ++ int nb_chunks = 1; ++ bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target)); ++ int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES); ++ ++ bdrv_graph_co_rdlock(); ++ source = s->mirror_top_bs->backing->bs; ++ bdrv_graph_co_rdunlock(); ++ ++ bdrv_dirty_bitmap_lock(s->dirty_bitmap); ++ offset = bdrv_dirty_iter_next(s->dbi); ++ if (offset < 0) { ++ bdrv_set_dirty_iter(s->dbi, 0); ++ offset = bdrv_dirty_iter_next(s->dbi); ++ trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); ++ assert(offset >= 0); ++ } ++ bdrv_dirty_bitmap_unlock(s->dirty_bitmap); ++ ++ /* ++ * Wait for concurrent requests to @offset. The next loop will limit the ++ * copied area based on in_flight_bitmap so we only copy an area that does ++ * not overlap with concurrent in-flight requests. Still, we would like to ++ * copy something, so wait until there are at least no more requests to the ++ * very beginning of the area. ++ */ ++ mirror_wait_on_conflicts(NULL, s, offset, 1); ++ ++ job_pause_point(&s->common.job); ++ ++ /* Find the number of consecutive dirty chunks following the first dirty ++ * one, and wait for in flight requests in them. */ ++ bdrv_dirty_bitmap_lock(s->dirty_bitmap); ++ while (nb_chunks * s->granularity < s->buf_size) { ++ int64_t next_dirty; ++ int64_t next_offset = offset + nb_chunks * s->granularity; ++ int64_t next_chunk = next_offset / s->granularity; ++ if (next_offset >= s->bdev_length || ++ !bdrv_dirty_bitmap_get_locked(s->dirty_bitmap, next_offset)) { ++ break; ++ } ++ if (test_bit(next_chunk, s->in_flight_bitmap)) { ++ break; ++ } ++ ++ next_dirty = bdrv_dirty_iter_next(s->dbi); ++ if (next_dirty > next_offset || next_dirty < 0) { ++ /* The bitmap iterator's cache is stale, refresh it */ ++ bdrv_set_dirty_iter(s->dbi, next_offset); ++ next_dirty = bdrv_dirty_iter_next(s->dbi); ++ } ++ assert(next_dirty == next_offset); ++ nb_chunks++; ++ } ++ ++ /* Clear dirty bits before querying the block status, because ++ * calling bdrv_block_status_above could yield - if some blocks are ++ * marked dirty in this window, we need to know. ++ */ ++ bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, offset, ++ nb_chunks * s->granularity); ++ bdrv_dirty_bitmap_unlock(s->dirty_bitmap); ++ ++ /* Before claiming an area in the in-flight bitmap, we have to ++ * create a MirrorOp for it so that conflicting requests can wait ++ * for it. mirror_perform() will create the real MirrorOps later, ++ * for now we just create a pseudo operation that will wake up all ++ * conflicting requests once all real operations have been ++ * launched. */ ++ pseudo_op = g_new(MirrorOp, 1); ++ *pseudo_op = (MirrorOp){ ++ .offset = offset, ++ .bytes = nb_chunks * s->granularity, ++ .is_pseudo_op = true, ++ }; ++ qemu_co_queue_init(&pseudo_op->waiting_requests); ++ QTAILQ_INSERT_TAIL(&s->ops_in_flight, pseudo_op, next); ++ ++ bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks); ++ while (nb_chunks > 0 && offset < s->bdev_length) { ++ int ret; ++ int64_t io_bytes; ++ int64_t io_bytes_acct; ++ MirrorMethod mirror_method = MIRROR_METHOD_COPY; ++ ++ assert(!(offset % s->granularity)); ++ WITH_GRAPH_RDLOCK_GUARD() { ++ ret = bdrv_co_block_status_above(source, NULL, offset, ++ nb_chunks * s->granularity, ++ &io_bytes, NULL, NULL); ++ } ++ if (ret < 0) { ++ io_bytes = MIN(nb_chunks * s->granularity, max_io_bytes); ++ } else if (ret & BDRV_BLOCK_DATA) { ++ io_bytes = MIN(io_bytes, max_io_bytes); ++ } ++ ++ io_bytes -= io_bytes % s->granularity; ++ if (io_bytes < s->granularity) { ++ io_bytes = s->granularity; ++ } else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) { ++ int64_t target_offset; ++ int64_t target_bytes; ++ WITH_GRAPH_RDLOCK_GUARD() { ++ bdrv_round_to_subclusters(blk_bs(s->target), offset, io_bytes, ++ &target_offset, &target_bytes); ++ } ++ if (target_offset == offset && ++ target_bytes == io_bytes) { ++ mirror_method = ret & BDRV_BLOCK_ZERO ? ++ MIRROR_METHOD_ZERO : ++ MIRROR_METHOD_DISCARD; ++ } ++ } ++ ++ while (s->in_flight >= MAX_IN_FLIGHT) { ++ trace_mirror_yield_in_flight(s, offset, s->in_flight); ++ mirror_wait_for_free_in_flight_slot(s); ++ } ++ ++ if (s->ret < 0) { ++ ret = 0; ++ goto fail; ++ } ++ ++ io_bytes = mirror_clip_bytes(s, offset, io_bytes); ++ io_bytes = mirror_perform(s, offset, io_bytes, mirror_method); ++ if (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok) { ++ io_bytes_acct = 0; ++ } else { ++ io_bytes_acct = io_bytes; ++ } ++ assert(io_bytes); ++ offset += io_bytes; ++ nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); ++ block_job_ratelimit_processed_bytes(&s->common, io_bytes_acct); ++ } ++ ++fail: ++ QTAILQ_REMOVE(&s->ops_in_flight, pseudo_op, next); ++ qemu_co_queue_restart_all(&pseudo_op->waiting_requests); ++ g_free(pseudo_op); ++} ++ ++static void mirror_free_init(MirrorBlockJob *s) ++{ ++ int granularity = s->granularity; ++ size_t buf_size = s->buf_size; ++ uint8_t *buf = s->buf; ++ ++ assert(s->buf_free_count == 0); ++ QSIMPLEQ_INIT(&s->buf_free); ++ while (buf_size != 0) { ++ MirrorBuffer *cur = (MirrorBuffer *)buf; ++ QSIMPLEQ_INSERT_TAIL(&s->buf_free, cur, next); ++ s->buf_free_count++; ++ buf_size -= granularity; ++ buf += granularity; ++ } ++} ++ ++/* This is also used for the .pause callback. There is no matching ++ * mirror_resume() because mirror_run() will begin iterating again ++ * when the job is resumed. ++ */ ++static void coroutine_fn mirror_wait_for_all_io(MirrorBlockJob *s) ++{ ++ while (s->in_flight > 0) { ++ mirror_wait_for_free_in_flight_slot(s); ++ } ++} ++ ++/** ++ * mirror_exit_common: handle both abort() and prepare() cases. ++ * for .prepare, returns 0 on success and -errno on failure. ++ * for .abort cases, denoted by abort = true, MUST return 0. ++ */ ++static int mirror_exit_common(Job *job) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ BlockJob *bjob = &s->common; ++ MirrorBDSOpaque *bs_opaque; ++ BlockDriverState *src; ++ BlockDriverState *target_bs; ++ BlockDriverState *mirror_top_bs; ++ Error *local_err = NULL; ++ bool abort = job->ret < 0; ++ int ret = 0; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (s->prepared) { ++ return 0; ++ } ++ s->prepared = true; ++ ++ bdrv_graph_rdlock_main_loop(); ++ ++ mirror_top_bs = s->mirror_top_bs; ++ bs_opaque = mirror_top_bs->opaque; ++ src = mirror_top_bs->backing->bs; ++ target_bs = blk_bs(s->target); ++ ++ if (bdrv_chain_contains(src, target_bs)) { ++ bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs); ++ } ++ ++ bdrv_release_dirty_bitmap(s->dirty_bitmap); ++ ++ /* Make sure that the source BDS doesn't go away during bdrv_replace_node, ++ * before we can call bdrv_drained_end */ ++ bdrv_ref(src); ++ bdrv_ref(mirror_top_bs); ++ bdrv_ref(target_bs); ++ ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* ++ * Remove target parent that still uses BLK_PERM_WRITE/RESIZE before ++ * inserting target_bs at s->to_replace, where we might not be able to get ++ * these permissions. ++ */ ++ blk_unref(s->target); ++ s->target = NULL; ++ ++ /* We don't access the source any more. Dropping any WRITE/RESIZE is ++ * required before it could become a backing file of target_bs. Not having ++ * these permissions any more means that we can't allow any new requests on ++ * mirror_top_bs from now on, so keep it drained. */ ++ bdrv_drained_begin(mirror_top_bs); ++ bdrv_drained_begin(target_bs); ++ bs_opaque->stop = true; ++ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, ++ &error_abort); ++ ++ if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { ++ BlockDriverState *backing = s->is_none_mode ? src : s->base; ++ BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs); ++ ++ if (bdrv_cow_bs(unfiltered_target) != backing) { ++ bdrv_set_backing_hd(unfiltered_target, backing, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ local_err = NULL; ++ ret = -EPERM; ++ } ++ } ++ } else if (!abort && s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) { ++ assert(!bdrv_backing_chain_next(target_bs)); ++ ret = bdrv_open_backing_file(bdrv_skip_filters(target_bs), NULL, ++ "backing", &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ local_err = NULL; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (s->should_complete && !abort) { ++ BlockDriverState *to_replace = s->to_replace ?: src; ++ bool ro = bdrv_is_read_only(to_replace); ++ ++ if (ro != bdrv_is_read_only(target_bs)) { ++ bdrv_reopen_set_read_only(target_bs, ro, NULL); ++ } ++ ++ /* The mirror job has no requests in flight any more, but we need to ++ * drain potential other users of the BDS before changing the graph. */ ++ assert(s->in_drain); ++ bdrv_drained_begin(to_replace); ++ /* ++ * Cannot use check_to_replace_node() here, because that would ++ * check for an op blocker on @to_replace, and we have our own ++ * there. ++ */ ++ bdrv_graph_wrlock(); ++ if (bdrv_recurse_can_replace(src, to_replace)) { ++ bdrv_replace_node(to_replace, target_bs, &local_err); ++ } else { ++ error_setg(&local_err, "Can no longer replace '%s' by '%s', " ++ "because it can no longer be guaranteed that doing so " ++ "would not lead to an abrupt change of visible data", ++ to_replace->node_name, target_bs->node_name); ++ } ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(to_replace); ++ if (local_err) { ++ error_report_err(local_err); ++ ret = -EPERM; ++ } ++ } ++ if (s->to_replace) { ++ bdrv_op_unblock_all(s->to_replace, s->replace_blocker); ++ error_free(s->replace_blocker); ++ bdrv_unref(s->to_replace); ++ } ++ g_free(s->replaces); ++ ++ /* ++ * Remove the mirror filter driver from the graph. Before this, get rid of ++ * the blockers on the intermediate nodes so that the resulting state is ++ * valid. ++ */ ++ block_job_remove_all_bdrv(bjob); ++ bdrv_graph_wrlock(); ++ bdrv_replace_node(mirror_top_bs, mirror_top_bs->backing->bs, &error_abort); ++ bdrv_graph_wrunlock(); ++ ++ if (abort && s->base_ro && !bdrv_is_read_only(target_bs)) { ++ bdrv_reopen_set_read_only(target_bs, true, NULL); ++ } ++ ++ bdrv_drained_end(target_bs); ++ bdrv_unref(target_bs); ++ ++ bs_opaque->job = NULL; ++ ++ bdrv_drained_end(src); ++ bdrv_drained_end(mirror_top_bs); ++ s->in_drain = false; ++ bdrv_unref(mirror_top_bs); ++ bdrv_unref(src); ++ ++ return ret; ++} ++ ++static int mirror_prepare(Job *job) ++{ ++ return mirror_exit_common(job); ++} ++ ++static void mirror_abort(Job *job) ++{ ++ int ret = mirror_exit_common(job); ++ assert(ret == 0); ++} ++ ++static void coroutine_fn mirror_throttle(MirrorBlockJob *s) ++{ ++ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ ++ if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) { ++ s->last_pause_ns = now; ++ job_sleep_ns(&s->common.job, 0); ++ } else { ++ job_pause_point(&s->common.job); ++ } ++} ++ ++static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) ++{ ++ int64_t offset; ++ BlockDriverState *bs; ++ BlockDriverState *target_bs = blk_bs(s->target); ++ int ret; ++ int64_t count; ++ ++ bdrv_graph_co_rdlock(); ++ bs = s->mirror_top_bs->backing->bs; ++ bdrv_graph_co_rdunlock(); ++ ++ if (s->zero_target) { ++ if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { ++ bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); ++ return 0; ++ } ++ ++ s->initial_zeroing_ongoing = true; ++ for (offset = 0; offset < s->bdev_length; ) { ++ int bytes = MIN(s->bdev_length - offset, ++ QEMU_ALIGN_DOWN(INT_MAX, s->granularity)); ++ ++ mirror_throttle(s); ++ ++ if (job_is_cancelled(&s->common.job)) { ++ s->initial_zeroing_ongoing = false; ++ return 0; ++ } ++ ++ if (s->in_flight >= MAX_IN_FLIGHT) { ++ trace_mirror_yield(s, UINT64_MAX, s->buf_free_count, ++ s->in_flight); ++ mirror_wait_for_free_in_flight_slot(s); ++ continue; ++ } ++ ++ mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO); ++ offset += bytes; ++ } ++ ++ mirror_wait_for_all_io(s); ++ s->initial_zeroing_ongoing = false; ++ } ++ ++ /* First part, loop on the sectors and initialize the dirty bitmap. */ ++ for (offset = 0; offset < s->bdev_length; ) { ++ /* Just to make sure we are not exceeding int limit. */ ++ int bytes = MIN(s->bdev_length - offset, ++ QEMU_ALIGN_DOWN(INT_MAX, s->granularity)); ++ ++ mirror_throttle(s); ++ ++ if (job_is_cancelled(&s->common.job)) { ++ return 0; ++ } ++ ++ WITH_GRAPH_RDLOCK_GUARD() { ++ ret = bdrv_co_is_allocated_above(bs, s->base_overlay, true, offset, ++ bytes, &count); ++ } ++ if (ret < 0) { ++ return ret; ++ } ++ ++ assert(count); ++ if (ret > 0) { ++ bdrv_set_dirty_bitmap(s->dirty_bitmap, offset, count); ++ } ++ offset += count; ++ } ++ return 0; ++} ++ ++/* Called when going out of the streaming phase to flush the bulk of the ++ * data to the medium, or just before completing. ++ */ ++static int coroutine_fn mirror_flush(MirrorBlockJob *s) ++{ ++ int ret = blk_co_flush(s->target); ++ if (ret < 0) { ++ if (mirror_error_action(s, false, -ret) == BLOCK_ERROR_ACTION_REPORT) { ++ s->ret = ret; ++ } ++ } ++ return ret; ++} ++ ++static int coroutine_fn mirror_run(Job *job, Error **errp) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ BlockDriverState *bs; ++ MirrorBDSOpaque *mirror_top_opaque = s->mirror_top_bs->opaque; ++ BlockDriverState *target_bs = blk_bs(s->target); ++ bool need_drain = true; ++ BlockDeviceIoStatus iostatus; ++ int64_t length; ++ int64_t target_length; ++ BlockDriverInfo bdi; ++ char backing_filename[2]; /* we only need 2 characters because we are only ++ checking for a NULL string */ ++ int ret = 0; ++ ++ bdrv_graph_co_rdlock(); ++ bs = bdrv_filter_bs(s->mirror_top_bs); ++ bdrv_graph_co_rdunlock(); ++ ++ if (job_is_cancelled(&s->common.job)) { ++ goto immediate_exit; ++ } ++ ++ bdrv_graph_co_rdlock(); ++ s->bdev_length = bdrv_co_getlength(bs); ++ bdrv_graph_co_rdunlock(); ++ ++ if (s->bdev_length < 0) { ++ ret = s->bdev_length; ++ goto immediate_exit; ++ } ++ ++ target_length = blk_co_getlength(s->target); ++ if (target_length < 0) { ++ ret = target_length; ++ goto immediate_exit; ++ } ++ ++ /* Active commit must resize the base image if its size differs from the ++ * active layer. */ ++ if (s->base == blk_bs(s->target)) { ++ if (s->bdev_length > target_length) { ++ ret = blk_co_truncate(s->target, s->bdev_length, false, ++ PREALLOC_MODE_OFF, 0, NULL); ++ if (ret < 0) { ++ goto immediate_exit; ++ } ++ } ++ } else if (s->bdev_length != target_length) { ++ error_setg(errp, "Source and target image have different sizes"); ++ ret = -EINVAL; ++ goto immediate_exit; ++ } ++ ++ if (s->bdev_length == 0) { ++ /* Transition to the READY state and wait for complete. */ ++ job_transition_to_ready(&s->common.job); ++ qatomic_set(&s->actively_synced, true); ++ while (!job_cancel_requested(&s->common.job) && !s->should_complete) { ++ job_yield(&s->common.job); ++ } ++ goto immediate_exit; ++ } ++ ++ length = DIV_ROUND_UP(s->bdev_length, s->granularity); ++ s->in_flight_bitmap = bitmap_new(length); ++ ++ /* If we have no backing file yet in the destination, we cannot let ++ * the destination do COW. Instead, we copy sectors around the ++ * dirty data if needed. We need a bitmap to do that. ++ */ ++ bdrv_get_backing_filename(target_bs, backing_filename, ++ sizeof(backing_filename)); ++ bdrv_graph_co_rdlock(); ++ if (!bdrv_co_get_info(target_bs, &bdi) && bdi.cluster_size) { ++ s->target_cluster_size = bdi.cluster_size; ++ } else { ++ s->target_cluster_size = BDRV_SECTOR_SIZE; ++ } ++ if (backing_filename[0] && !bdrv_backing_chain_next(target_bs) && ++ s->granularity < s->target_cluster_size) { ++ s->buf_size = MAX(s->buf_size, s->target_cluster_size); ++ s->cow_bitmap = bitmap_new(length); ++ } ++ s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov); ++ bdrv_graph_co_rdunlock(); ++ ++ s->buf = qemu_try_blockalign(bs, s->buf_size); ++ if (s->buf == NULL) { ++ ret = -ENOMEM; ++ goto immediate_exit; ++ } ++ ++ mirror_free_init(s); ++ ++ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ if (!s->is_none_mode) { ++ ret = mirror_dirty_init(s); ++ if (ret < 0 || job_is_cancelled(&s->common.job)) { ++ goto immediate_exit; ++ } ++ } ++ ++ /* ++ * Only now the job is fully initialised and mirror_top_bs should start ++ * accessing it. ++ */ ++ mirror_top_opaque->job = s; ++ ++ assert(!s->dbi); ++ s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap); ++ for (;;) { ++ int64_t cnt, delta; ++ bool should_complete; ++ ++ if (s->ret < 0) { ++ ret = s->ret; ++ goto immediate_exit; ++ } ++ ++ job_pause_point(&s->common.job); ++ ++ if (job_is_cancelled(&s->common.job)) { ++ ret = 0; ++ goto immediate_exit; ++ } ++ ++ cnt = bdrv_get_dirty_count(s->dirty_bitmap); ++ /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is ++ * the number of bytes currently being processed; together those are ++ * the current remaining operation length */ ++ job_progress_set_remaining(&s->common.job, ++ s->bytes_in_flight + cnt + ++ s->active_write_bytes_in_flight); ++ ++ /* Note that even when no rate limit is applied we need to yield ++ * periodically with no pending I/O so that bdrv_drain_all() returns. ++ * We do so every BLKOCK_JOB_SLICE_TIME nanoseconds, or when there is ++ * an error, or when the source is clean, whichever comes first. */ ++ delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns; ++ WITH_JOB_LOCK_GUARD() { ++ iostatus = s->common.iostatus; ++ } ++ if (delta < BLOCK_JOB_SLICE_TIME && ++ iostatus == BLOCK_DEVICE_IO_STATUS_OK) { ++ if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || ++ (cnt == 0 && s->in_flight > 0)) { ++ trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); ++ mirror_wait_for_free_in_flight_slot(s); ++ continue; ++ } else if (cnt != 0) { ++ mirror_iteration(s); ++ } ++ } ++ ++ should_complete = false; ++ if (s->in_flight == 0 && cnt == 0) { ++ trace_mirror_before_flush(s); ++ if (!job_is_ready(&s->common.job)) { ++ if (mirror_flush(s) < 0) { ++ /* Go check s->ret. */ ++ continue; ++ } ++ /* We're out of the streaming phase. From now on, if the job ++ * is cancelled we will actually complete all pending I/O and ++ * report completion. This way, block-job-cancel will leave ++ * the target in a consistent state. ++ */ ++ job_transition_to_ready(&s->common.job); ++ } ++ if (qatomic_read(&s->copy_mode) != MIRROR_COPY_MODE_BACKGROUND) { ++ qatomic_set(&s->actively_synced, true); ++ } ++ ++ should_complete = s->should_complete || ++ job_cancel_requested(&s->common.job); ++ cnt = bdrv_get_dirty_count(s->dirty_bitmap); ++ } ++ ++ if (cnt == 0 && should_complete) { ++ /* The dirty bitmap is not updated while operations are pending. ++ * If we're about to exit, wait for pending operations before ++ * calling bdrv_get_dirty_count(bs), or we may exit while the ++ * source has dirty data to copy! ++ * ++ * Note that I/O can be submitted by the guest while ++ * mirror_populate runs, so pause it now. Before deciding ++ * whether to switch to target check one last time if I/O has ++ * come in the meanwhile, and if not flush the data to disk. ++ */ ++ trace_mirror_before_drain(s, cnt); ++ ++ s->in_drain = true; ++ bdrv_drained_begin(bs); ++ ++ /* Must be zero because we are drained */ ++ assert(s->in_active_write_counter == 0); ++ ++ cnt = bdrv_get_dirty_count(s->dirty_bitmap); ++ if (cnt > 0 || mirror_flush(s) < 0) { ++ bdrv_drained_end(bs); ++ s->in_drain = false; ++ continue; ++ } ++ ++ /* The two disks are in sync. Exit and report successful ++ * completion. ++ */ ++ assert(QLIST_EMPTY(&bs->tracked_requests)); ++ need_drain = false; ++ break; ++ } ++ ++ if (job_is_ready(&s->common.job) && !should_complete) { ++ if (s->in_flight == 0 && cnt == 0) { ++ trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), ++ BLOCK_JOB_SLICE_TIME); ++ job_sleep_ns(&s->common.job, BLOCK_JOB_SLICE_TIME); ++ } ++ } else { ++ block_job_ratelimit_sleep(&s->common); ++ } ++ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ } ++ ++immediate_exit: ++ if (s->in_flight > 0) { ++ /* We get here only if something went wrong. Either the job failed, ++ * or it was cancelled prematurely so that we do not guarantee that ++ * the target is a copy of the source. ++ */ ++ assert(ret < 0 || job_is_cancelled(&s->common.job)); ++ assert(need_drain); ++ mirror_wait_for_all_io(s); ++ } ++ ++ assert(s->in_flight == 0); ++ qemu_vfree(s->buf); ++ g_free(s->cow_bitmap); ++ g_free(s->in_flight_bitmap); ++ bdrv_dirty_iter_free(s->dbi); ++ ++ if (need_drain) { ++ s->in_drain = true; ++ bdrv_drained_begin(bs); ++ } ++ ++ return ret; ++} ++ ++static void mirror_complete(Job *job, Error **errp) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ ++ if (!job_is_ready(job)) { ++ error_setg(errp, "The active block job '%s' cannot be completed", ++ job->id); ++ return; ++ } ++ ++ /* block all operations on to_replace bs */ ++ if (s->replaces) { ++ s->to_replace = bdrv_find_node(s->replaces); ++ if (!s->to_replace) { ++ error_setg(errp, "Node name '%s' not found", s->replaces); ++ return; ++ } ++ ++ /* TODO Translate this into child freeze system. */ ++ error_setg(&s->replace_blocker, ++ "block device is in use by block-job-complete"); ++ bdrv_op_block_all(s->to_replace, s->replace_blocker); ++ bdrv_ref(s->to_replace); ++ } ++ ++ s->should_complete = true; ++ ++ /* If the job is paused, it will be re-entered when it is resumed */ ++ WITH_JOB_LOCK_GUARD() { ++ if (!job->paused) { ++ job_enter_cond_locked(job, NULL); ++ } ++ } ++} ++ ++static void coroutine_fn mirror_pause(Job *job) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ ++ mirror_wait_for_all_io(s); ++} ++ ++static bool mirror_drained_poll(BlockJob *job) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ ++ /* If the job isn't paused nor cancelled, we can't be sure that it won't ++ * issue more requests. We make an exception if we've reached this point ++ * from one of our own drain sections, to avoid a deadlock waiting for ++ * ourselves. ++ */ ++ WITH_JOB_LOCK_GUARD() { ++ if (!s->common.job.paused && !job_is_cancelled_locked(&job->job) ++ && !s->in_drain) { ++ return true; ++ } ++ } ++ ++ return !!s->in_flight; ++} ++ ++static bool mirror_cancel(Job *job, bool force) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); ++ BlockDriverState *target = blk_bs(s->target); ++ ++ /* ++ * Before the job is READY, we treat any cancellation like a ++ * force-cancellation. ++ */ ++ force = force || !job_is_ready(job); ++ ++ if (force) { ++ bdrv_cancel_in_flight(target); ++ } ++ return force; ++} ++ ++static bool commit_active_cancel(Job *job, bool force) ++{ ++ /* Same as above in mirror_cancel() */ ++ return force || !job_is_ready(job); ++} ++ ++static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, ++ Error **errp) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ BlockJobChangeOptionsMirror *change_opts = &opts->u.mirror; ++ MirrorCopyMode current; ++ ++ /* ++ * The implementation relies on the fact that copy_mode is only written ++ * under the BQL. Otherwise, further synchronization would be required. ++ */ ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (qatomic_read(&s->copy_mode) == change_opts->copy_mode) { ++ return; ++ } ++ ++ if (change_opts->copy_mode != MIRROR_COPY_MODE_WRITE_BLOCKING) { ++ error_setg(errp, "Change to copy mode '%s' is not implemented", ++ MirrorCopyMode_str(change_opts->copy_mode)); ++ return; ++ } ++ ++ current = qatomic_cmpxchg(&s->copy_mode, MIRROR_COPY_MODE_BACKGROUND, ++ change_opts->copy_mode); ++ if (current != MIRROR_COPY_MODE_BACKGROUND) { ++ error_setg(errp, "Expected current copy mode '%s', got '%s'", ++ MirrorCopyMode_str(MIRROR_COPY_MODE_BACKGROUND), ++ MirrorCopyMode_str(current)); ++ } ++} ++ ++static void mirror_query(BlockJob *job, BlockJobInfo *info) ++{ ++ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); ++ ++ info->u.mirror = (BlockJobInfoMirror) { ++ .actively_synced = qatomic_read(&s->actively_synced), ++ }; ++} ++ ++static const BlockJobDriver mirror_job_driver = { ++ .job_driver = { ++ .instance_size = sizeof(MirrorBlockJob), ++ .job_type = JOB_TYPE_MIRROR, ++ .free = block_job_free, ++ .user_resume = block_job_user_resume, ++ .run = mirror_run, ++ .prepare = mirror_prepare, ++ .abort = mirror_abort, ++ .pause = mirror_pause, ++ .complete = mirror_complete, ++ .cancel = mirror_cancel, ++ }, ++ .drained_poll = mirror_drained_poll, ++ .change = mirror_change, ++ .query = mirror_query, ++}; ++ ++static const BlockJobDriver commit_active_job_driver = { ++ .job_driver = { ++ .instance_size = sizeof(MirrorBlockJob), ++ .job_type = JOB_TYPE_COMMIT, ++ .free = block_job_free, ++ .user_resume = block_job_user_resume, ++ .run = mirror_run, ++ .prepare = mirror_prepare, ++ .abort = mirror_abort, ++ .pause = mirror_pause, ++ .complete = mirror_complete, ++ .cancel = commit_active_cancel, ++ }, ++ .drained_poll = mirror_drained_poll, ++}; ++ ++static void coroutine_fn ++do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, ++ uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, int flags) ++{ ++ int ret; ++ size_t qiov_offset = 0; ++ int64_t bitmap_offset, bitmap_end; ++ ++ if (!QEMU_IS_ALIGNED(offset, job->granularity) && ++ bdrv_dirty_bitmap_get(job->dirty_bitmap, offset)) ++ { ++ /* ++ * Dirty unaligned padding: ignore it. ++ * ++ * Reasoning: ++ * 1. If we copy it, we can't reset corresponding bit in ++ * dirty_bitmap as there may be some "dirty" bytes still not ++ * copied. ++ * 2. It's already dirty, so skipping it we don't diverge mirror ++ * progress. ++ * ++ * Note, that because of this, guest write may have no contribution ++ * into mirror converge, but that's not bad, as we have background ++ * process of mirroring. If under some bad circumstances (high guest ++ * IO load) background process starve, we will not converge anyway, ++ * even if each write will contribute, as guest is not guaranteed to ++ * rewrite the whole disk. ++ */ ++ qiov_offset = QEMU_ALIGN_UP(offset, job->granularity) - offset; ++ if (bytes <= qiov_offset) { ++ /* nothing to do after shrink */ ++ return; ++ } ++ offset += qiov_offset; ++ bytes -= qiov_offset; ++ } ++ ++ if (!QEMU_IS_ALIGNED(offset + bytes, job->granularity) && ++ bdrv_dirty_bitmap_get(job->dirty_bitmap, offset + bytes - 1)) ++ { ++ uint64_t tail = (offset + bytes) % job->granularity; ++ ++ if (bytes <= tail) { ++ /* nothing to do after shrink */ ++ return; ++ } ++ bytes -= tail; ++ } ++ ++ /* ++ * Tails are either clean or shrunk, so for bitmap resetting ++ * we safely align the range down. ++ */ ++ bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity); ++ bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity); ++ if (bitmap_offset < bitmap_end) { ++ bdrv_reset_dirty_bitmap(job->dirty_bitmap, bitmap_offset, ++ bitmap_end - bitmap_offset); ++ } ++ ++ job_progress_increase_remaining(&job->common.job, bytes); ++ job->active_write_bytes_in_flight += bytes; ++ ++ switch (method) { ++ case MIRROR_METHOD_COPY: ++ ret = blk_co_pwritev_part(job->target, offset, bytes, ++ qiov, qiov_offset, flags); ++ break; ++ ++ case MIRROR_METHOD_ZERO: ++ assert(!qiov); ++ ret = blk_co_pwrite_zeroes(job->target, offset, bytes, flags); ++ break; ++ ++ case MIRROR_METHOD_DISCARD: ++ assert(!qiov); ++ ret = blk_co_pdiscard(job->target, offset, bytes); ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ job->active_write_bytes_in_flight -= bytes; ++ if (ret >= 0) { ++ job_progress_update(&job->common.job, bytes); ++ } else { ++ BlockErrorAction action; ++ ++ /* ++ * We failed, so we should mark dirty the whole area, aligned up. ++ * Note that we don't care about shrunk tails if any: they were dirty ++ * at function start, and they must be still dirty, as we've locked ++ * the region for in-flight op. ++ */ ++ bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity); ++ bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); ++ bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset, ++ bitmap_end - bitmap_offset); ++ qatomic_set(&job->actively_synced, false); ++ ++ action = mirror_error_action(job, false, -ret); ++ if (action == BLOCK_ERROR_ACTION_REPORT) { ++ if (!job->ret) { ++ job->ret = ret; ++ } ++ } ++ } ++} ++ ++static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s, ++ uint64_t offset, ++ uint64_t bytes) ++{ ++ MirrorOp *op; ++ uint64_t start_chunk = offset / s->granularity; ++ uint64_t end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity); ++ ++ op = g_new(MirrorOp, 1); ++ *op = (MirrorOp){ ++ .s = s, ++ .offset = offset, ++ .bytes = bytes, ++ .is_active_write = true, ++ .is_in_flight = true, ++ .co = qemu_coroutine_self(), ++ }; ++ qemu_co_queue_init(&op->waiting_requests); ++ QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); ++ ++ s->in_active_write_counter++; ++ ++ /* ++ * Wait for concurrent requests affecting the area. If there are already ++ * running requests that are copying off now-to-be stale data in the area, ++ * we must wait for them to finish before we begin writing fresh data to the ++ * target so that the write operations appear in the correct order. ++ * Note that background requests (see mirror_iteration()) in contrast only ++ * wait for conflicting requests at the start of the dirty area, and then ++ * (based on the in_flight_bitmap) truncate the area to copy so it will not ++ * conflict with any requests beyond that. For active writes, however, we ++ * cannot truncate that area. The request from our parent must be blocked ++ * until the area is copied in full. Therefore, we must wait for the whole ++ * area to become free of concurrent requests. ++ */ ++ mirror_wait_on_conflicts(op, s, offset, bytes); ++ ++ bitmap_set(s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); ++ ++ return op; ++} ++ ++static void coroutine_fn GRAPH_RDLOCK active_write_settle(MirrorOp *op) ++{ ++ uint64_t start_chunk = op->offset / op->s->granularity; ++ uint64_t end_chunk = DIV_ROUND_UP(op->offset + op->bytes, ++ op->s->granularity); ++ ++ if (!--op->s->in_active_write_counter && ++ qatomic_read(&op->s->actively_synced)) { ++ BdrvChild *source = op->s->mirror_top_bs->backing; ++ ++ if (QLIST_FIRST(&source->bs->parents) == source && ++ QLIST_NEXT(source, next_parent) == NULL) ++ { ++ /* Assert that we are back in sync once all active write ++ * operations are settled. ++ * Note that we can only assert this if the mirror node ++ * is the source node's only parent. */ ++ assert(!bdrv_get_dirty_count(op->s->dirty_bitmap)); ++ } ++ } ++ bitmap_clear(op->s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); ++ QTAILQ_REMOVE(&op->s->ops_in_flight, op, next); ++ qemu_co_queue_restart_all(&op->waiting_requests); ++ g_free(op); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); ++} ++ ++static bool should_copy_to_target(MirrorBDSOpaque *s) ++{ ++ return s->job && s->job->ret >= 0 && ++ !job_is_cancelled(&s->job->common.job) && ++ qatomic_read(&s->job->copy_mode) == MIRROR_COPY_MODE_WRITE_BLOCKING; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_do_write(BlockDriverState *bs, MirrorMethod method, ++ bool copy_to_target, uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, int flags) ++{ ++ MirrorOp *op = NULL; ++ MirrorBDSOpaque *s = bs->opaque; ++ int ret = 0; ++ ++ if (copy_to_target) { ++ op = active_write_prepare(s->job, offset, bytes); ++ } ++ ++ switch (method) { ++ case MIRROR_METHOD_COPY: ++ ret = bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); ++ break; ++ ++ case MIRROR_METHOD_ZERO: ++ ret = bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); ++ break; ++ ++ case MIRROR_METHOD_DISCARD: ++ ret = bdrv_co_pdiscard(bs->backing, offset, bytes); ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ if (!copy_to_target && s->job && s->job->dirty_bitmap) { ++ qatomic_set(&s->job->actively_synced, false); ++ bdrv_set_dirty_bitmap(s->job->dirty_bitmap, offset, bytes); ++ } ++ ++ if (ret < 0) { ++ goto out; ++ } ++ ++ if (copy_to_target) { ++ do_sync_target_write(s->job, method, offset, bytes, qiov, flags); ++ } ++ ++out: ++ if (copy_to_target) { ++ active_write_settle(op); ++ } ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ QEMUIOVector bounce_qiov; ++ void *bounce_buf; ++ int ret = 0; ++ bool copy_to_target = should_copy_to_target(bs->opaque); ++ ++ if (copy_to_target) { ++ /* The guest might concurrently modify the data to write; but ++ * the data on source and destination must match, so we have ++ * to use a bounce buffer if we are going to write to the ++ * target now. */ ++ bounce_buf = qemu_blockalign(bs, bytes); ++ iov_to_buf_full(qiov->iov, qiov->niov, 0, bounce_buf, bytes); ++ ++ qemu_iovec_init(&bounce_qiov, 1); ++ qemu_iovec_add(&bounce_qiov, bounce_buf, bytes); ++ qiov = &bounce_qiov; ++ ++ flags &= ~BDRV_REQ_REGISTERED_BUF; ++ } ++ ++ ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, copy_to_target, ++ offset, bytes, qiov, flags); ++ ++ if (copy_to_target) { ++ qemu_iovec_destroy(&bounce_qiov); ++ qemu_vfree(bounce_buf); ++ } ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK bdrv_mirror_top_flush(BlockDriverState *bs) ++{ ++ if (bs->backing == NULL) { ++ /* we can be here after failed bdrv_append in mirror_start_job */ ++ return 0; ++ } ++ return bdrv_co_flush(bs->backing->bs); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, BdrvRequestFlags flags) ++{ ++ bool copy_to_target = should_copy_to_target(bs->opaque); ++ return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, copy_to_target, ++ offset, bytes, NULL, flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ bool copy_to_target = should_copy_to_target(bs->opaque); ++ return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, copy_to_target, ++ offset, bytes, NULL, 0); ++} ++ ++static void GRAPH_RDLOCK bdrv_mirror_top_refresh_filename(BlockDriverState *bs) ++{ ++ if (bs->backing == NULL) { ++ /* we can be here after failed bdrv_attach_child in ++ * bdrv_set_backing_hd */ ++ return; ++ } ++ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), ++ bs->backing->bs->filename); ++} ++ ++static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t perm, uint64_t shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ MirrorBDSOpaque *s = bs->opaque; ++ ++ if (s->stop) { ++ /* ++ * If the job is to be stopped, we do not need to forward ++ * anything to the real image. ++ */ ++ *nperm = 0; ++ *nshared = BLK_PERM_ALL; ++ return; ++ } ++ ++ bdrv_default_perms(bs, c, role, reopen_queue, ++ perm, shared, nperm, nshared); ++ ++ if (s->is_commit) { ++ /* ++ * For commit jobs, we cannot take CONSISTENT_READ, because ++ * that permission is unshared for everything above the base ++ * node (except for filters on the base node). ++ * We also have to force-share the WRITE permission, or ++ * otherwise we would block ourselves at the base node (if ++ * writes are blocked for a node, they are also blocked for ++ * its backing file). ++ * (We could also share RESIZE, because it may be needed for ++ * the target if its size is less than the top node's; but ++ * bdrv_default_perms_for_cow() automatically shares RESIZE ++ * for backing nodes if WRITE is shared, so there is no need ++ * to do it here.) ++ */ ++ *nperm &= ~BLK_PERM_CONSISTENT_READ; ++ *nshared |= BLK_PERM_WRITE; ++ } ++} ++ ++/* Dummy node that provides consistent read to its users without requiring it ++ * from its backing file and that allows writes on the backing file chain. */ ++static BlockDriver bdrv_mirror_top = { ++ .format_name = "mirror_top", ++ .bdrv_co_preadv = bdrv_mirror_top_preadv, ++ .bdrv_co_pwritev = bdrv_mirror_top_pwritev, ++ .bdrv_co_pwrite_zeroes = bdrv_mirror_top_pwrite_zeroes, ++ .bdrv_co_pdiscard = bdrv_mirror_top_pdiscard, ++ .bdrv_co_flush = bdrv_mirror_top_flush, ++ .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, ++ .bdrv_child_perm = bdrv_mirror_top_child_perm, ++ ++ .is_filter = true, ++ .filtered_child_is_backing = true, ++}; ++ ++static BlockJob *mirror_start_job( ++ const char *job_id, BlockDriverState *bs, ++ int creation_flags, BlockDriverState *target, ++ const char *replaces, int64_t speed, ++ uint32_t granularity, int64_t buf_size, ++ BlockMirrorBackingMode backing_mode, ++ bool zero_target, ++ BlockdevOnError on_source_error, ++ BlockdevOnError on_target_error, ++ bool unmap, ++ BlockCompletionFunc *cb, ++ void *opaque, ++ const BlockJobDriver *driver, ++ bool is_none_mode, BlockDriverState *base, ++ bool auto_complete, const char *filter_node_name, ++ bool is_mirror, MirrorCopyMode copy_mode, ++ bool base_ro, ++ Error **errp) ++{ ++ MirrorBlockJob *s; ++ MirrorBDSOpaque *bs_opaque; ++ BlockDriverState *mirror_top_bs; ++ bool target_is_backing; ++ uint64_t target_perms, target_shared_perms; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (granularity == 0) { ++ granularity = bdrv_get_default_bitmap_granularity(target); ++ } ++ ++ assert(is_power_of_2(granularity)); ++ ++ if (buf_size < 0) { ++ error_setg(errp, "Invalid parameter 'buf-size'"); ++ return NULL; ++ } ++ ++ if (buf_size == 0) { ++ buf_size = DEFAULT_MIRROR_BUF_SIZE; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_skip_filters(bs) == bdrv_skip_filters(target)) { ++ error_setg(errp, "Can't mirror node into itself"); ++ bdrv_graph_rdunlock_main_loop(); ++ return NULL; ++ } ++ ++ target_is_backing = bdrv_chain_contains(bs, target); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* In the case of active commit, add dummy driver to provide consistent ++ * reads on the top, while disabling it in the intermediate nodes, and make ++ * the backing chain writable. */ ++ mirror_top_bs = bdrv_new_open_driver(&bdrv_mirror_top, filter_node_name, ++ BDRV_O_RDWR, errp); ++ if (mirror_top_bs == NULL) { ++ return NULL; ++ } ++ if (!filter_node_name) { ++ mirror_top_bs->implicit = true; ++ } ++ ++ /* So that we can always drop this node */ ++ mirror_top_bs->never_freeze = true; ++ ++ mirror_top_bs->total_sectors = bs->total_sectors; ++ mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; ++ mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | ++ BDRV_REQ_NO_FALLBACK; ++ bs_opaque = g_new0(MirrorBDSOpaque, 1); ++ mirror_top_bs->opaque = bs_opaque; ++ ++ bs_opaque->is_commit = target_is_backing; ++ ++ bdrv_drained_begin(bs); ++ ret = bdrv_append(mirror_top_bs, bs, errp); ++ bdrv_drained_end(bs); ++ ++ if (ret < 0) { ++ bdrv_unref(mirror_top_bs); ++ return NULL; ++ } ++ ++ /* Make sure that the source is not resized while the job is running */ ++ s = block_job_create(job_id, driver, NULL, mirror_top_bs, ++ BLK_PERM_CONSISTENT_READ, ++ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | ++ BLK_PERM_WRITE, speed, ++ creation_flags, cb, opaque, errp); ++ if (!s) { ++ goto fail; ++ } ++ ++ /* The block job now has a reference to this node */ ++ bdrv_unref(mirror_top_bs); ++ ++ s->mirror_top_bs = mirror_top_bs; ++ s->base_ro = base_ro; ++ ++ /* No resize for the target either; while the mirror is still running, a ++ * consistent read isn't necessarily possible. We could possibly allow ++ * writes and graph modifications, though it would likely defeat the ++ * purpose of a mirror, so leave them blocked for now. ++ * ++ * In the case of active commit, things look a bit different, though, ++ * because the target is an already populated backing file in active use. ++ * We can allow anything except resize there.*/ ++ ++ target_perms = BLK_PERM_WRITE; ++ target_shared_perms = BLK_PERM_WRITE_UNCHANGED; ++ ++ if (target_is_backing) { ++ int64_t bs_size, target_size; ++ bs_size = bdrv_getlength(bs); ++ if (bs_size < 0) { ++ error_setg_errno(errp, -bs_size, ++ "Could not inquire top image size"); ++ goto fail; ++ } ++ ++ target_size = bdrv_getlength(target); ++ if (target_size < 0) { ++ error_setg_errno(errp, -target_size, ++ "Could not inquire base image size"); ++ goto fail; ++ } ++ ++ if (target_size < bs_size) { ++ target_perms |= BLK_PERM_RESIZE; ++ } ++ ++ target_shared_perms |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE; ++ } else { ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) { ++ /* ++ * We may want to allow this in the future, but it would ++ * require taking some extra care. ++ */ ++ error_setg(errp, "Cannot mirror to a filter on top of a node in " ++ "the source's backing chain"); ++ bdrv_graph_rdunlock_main_loop(); ++ goto fail; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ } ++ ++ s->target = blk_new(s->common.job.aio_context, ++ target_perms, target_shared_perms); ++ ret = blk_insert_bs(s->target, target, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ if (is_mirror) { ++ /* XXX: Mirror target could be a NBD server of target QEMU in the case ++ * of non-shared block migration. To allow migration completion, we ++ * have to allow "inactivate" of the target BB. When that happens, we ++ * know the job is drained, and the vcpus are stopped, so no write ++ * operation will be performed. Block layer already has assertions to ++ * ensure that. */ ++ blk_set_force_allow_inactivate(s->target); ++ } ++ blk_set_allow_aio_context_change(s->target, true); ++ blk_set_disable_request_queuing(s->target, true); ++ ++ bdrv_graph_rdlock_main_loop(); ++ s->replaces = g_strdup(replaces); ++ s->on_source_error = on_source_error; ++ s->on_target_error = on_target_error; ++ s->is_none_mode = is_none_mode; ++ s->backing_mode = backing_mode; ++ s->zero_target = zero_target; ++ qatomic_set(&s->copy_mode, copy_mode); ++ s->base = base; ++ s->base_overlay = bdrv_find_overlay(bs, base); ++ s->granularity = granularity; ++ s->buf_size = ROUND_UP(buf_size, granularity); ++ s->unmap = unmap; ++ if (auto_complete) { ++ s->should_complete = true; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ s->dirty_bitmap = bdrv_create_dirty_bitmap(s->mirror_top_bs, granularity, ++ NULL, errp); ++ if (!s->dirty_bitmap) { ++ goto fail; ++ } ++ ++ /* ++ * The dirty bitmap is set by bdrv_mirror_top_do_write() when not in active ++ * mode. ++ */ ++ bdrv_disable_dirty_bitmap(s->dirty_bitmap); ++ ++ bdrv_graph_wrlock(); ++ ret = block_job_add_bdrv(&s->common, "source", bs, 0, ++ BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | ++ BLK_PERM_CONSISTENT_READ, ++ errp); ++ if (ret < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ ++ /* Required permissions are already taken with blk_new() */ ++ block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL, ++ &error_abort); ++ ++ /* In commit_active_start() all intermediate nodes disappear, so ++ * any jobs in them must be blocked */ ++ if (target_is_backing) { ++ BlockDriverState *iter, *filtered_target; ++ uint64_t iter_shared_perms; ++ ++ /* ++ * The topmost node with ++ * bdrv_skip_filters(filtered_target) == bdrv_skip_filters(target) ++ */ ++ filtered_target = bdrv_cow_bs(bdrv_find_overlay(bs, target)); ++ ++ assert(bdrv_skip_filters(filtered_target) == ++ bdrv_skip_filters(target)); ++ ++ /* ++ * XXX BLK_PERM_WRITE needs to be allowed so we don't block ++ * ourselves at s->base (if writes are blocked for a node, they are ++ * also blocked for its backing file). The other options would be a ++ * second filter driver above s->base (== target). ++ */ ++ iter_shared_perms = BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE; ++ ++ for (iter = bdrv_filter_or_cow_bs(bs); iter != target; ++ iter = bdrv_filter_or_cow_bs(iter)) ++ { ++ if (iter == filtered_target) { ++ /* ++ * From here on, all nodes are filters on the base. ++ * This allows us to share BLK_PERM_CONSISTENT_READ. ++ */ ++ iter_shared_perms |= BLK_PERM_CONSISTENT_READ; ++ } ++ ++ ret = block_job_add_bdrv(&s->common, "intermediate node", iter, 0, ++ iter_shared_perms, errp); ++ if (ret < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ } ++ ++ if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { ++ bdrv_graph_wrunlock(); ++ goto fail; ++ } ++ } ++ bdrv_graph_wrunlock(); ++ ++ QTAILQ_INIT(&s->ops_in_flight); ++ ++ trace_mirror_start(bs, s, opaque); ++ job_start(&s->common.job); ++ ++ return &s->common; ++ ++fail: ++ if (s) { ++ /* Make sure this BDS does not go away until we have completed the graph ++ * changes below */ ++ bdrv_ref(mirror_top_bs); ++ ++ g_free(s->replaces); ++ blk_unref(s->target); ++ bs_opaque->job = NULL; ++ if (s->dirty_bitmap) { ++ bdrv_release_dirty_bitmap(s->dirty_bitmap); ++ } ++ job_early_fail(&s->common.job); ++ } ++ ++ bs_opaque->stop = true; ++ bdrv_drained_begin(bs); ++ bdrv_graph_wrlock(); ++ assert(mirror_top_bs->backing->bs == bs); ++ bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, ++ &error_abort); ++ bdrv_replace_node(mirror_top_bs, bs, &error_abort); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(bs); ++ ++ bdrv_unref(mirror_top_bs); ++ ++ return NULL; ++} ++ ++void mirror_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *target, const char *replaces, ++ int creation_flags, int64_t speed, ++ uint32_t granularity, int64_t buf_size, ++ MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, ++ bool zero_target, ++ BlockdevOnError on_source_error, ++ BlockdevOnError on_target_error, ++ bool unmap, const char *filter_node_name, ++ MirrorCopyMode copy_mode, Error **errp) ++{ ++ bool is_none_mode; ++ BlockDriverState *base; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if ((mode == MIRROR_SYNC_MODE_INCREMENTAL) || ++ (mode == MIRROR_SYNC_MODE_BITMAP)) { ++ error_setg(errp, "Sync mode '%s' not supported", ++ MirrorSyncMode_str(mode)); ++ return; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ is_none_mode = mode == MIRROR_SYNC_MODE_NONE; ++ base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL; ++ bdrv_graph_rdunlock_main_loop(); ++ ++ mirror_start_job(job_id, bs, creation_flags, target, replaces, ++ speed, granularity, buf_size, backing_mode, zero_target, ++ on_source_error, on_target_error, unmap, NULL, NULL, ++ &mirror_job_driver, is_none_mode, base, false, ++ filter_node_name, true, copy_mode, false, errp); ++} ++ ++BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *base, int creation_flags, ++ int64_t speed, BlockdevOnError on_error, ++ const char *filter_node_name, ++ BlockCompletionFunc *cb, void *opaque, ++ bool auto_complete, Error **errp) ++{ ++ bool base_read_only; ++ BlockJob *job; ++ ++ GLOBAL_STATE_CODE(); ++ ++ base_read_only = bdrv_is_read_only(base); ++ ++ if (base_read_only) { ++ if (bdrv_reopen_set_read_only(base, false, errp) < 0) { ++ return NULL; ++ } ++ } ++ ++ job = mirror_start_job( ++ job_id, bs, creation_flags, base, NULL, speed, 0, 0, ++ MIRROR_LEAVE_BACKING_CHAIN, false, ++ on_error, on_error, true, cb, opaque, ++ &commit_active_job_driver, false, base, auto_complete, ++ filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, ++ base_read_only, errp); ++ if (!job) { ++ goto error_restore_flags; ++ } ++ ++ return job; ++ ++error_restore_flags: ++ /* ignore error and errp for bdrv_reopen, because we want to propagate ++ * the original error */ ++ if (base_read_only) { ++ bdrv_reopen_set_read_only(base, true, NULL); ++ } ++ return NULL; ++} +diff --git a/qcow2/lib/block/monitor/bitmap-qmp-cmds.c b/qcow2/lib/block/monitor/bitmap-qmp-cmds.c +new file mode 100644 +index 00000000..a738e7bb +--- /dev/null ++++ b/qcow2/lib/block/monitor/bitmap-qmp-cmds.c +@@ -0,0 +1,317 @@ ++/* ++ * QEMU block dirty bitmap QMP commands ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ * ++ * This file incorporates work covered by the following copyright and ++ * permission notice: ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "block/block-io.h" ++#include "block/block_int.h" ++#include "block/dirty-bitmap.h" ++#include "qapi/qapi-commands-block.h" ++#include "qapi/error.h" ++ ++/** ++ * block_dirty_bitmap_lookup: ++ * Return a dirty bitmap (if present), after validating ++ * the node reference and bitmap names. ++ * ++ * @node: The name of the BDS node to search for bitmaps ++ * @name: The name of the bitmap to search for ++ * @pbs: Output pointer for BDS lookup, if desired. Can be NULL. ++ * @errp: Output pointer for error information. Can be NULL. ++ * ++ * @return: A bitmap object on success, or NULL on failure. ++ */ ++BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, ++ const char *name, ++ BlockDriverState **pbs, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!node) { ++ error_setg(errp, "Node cannot be NULL"); ++ return NULL; ++ } ++ if (!name) { ++ error_setg(errp, "Bitmap name cannot be NULL"); ++ return NULL; ++ } ++ bs = bdrv_lookup_bs(node, node, NULL); ++ if (!bs) { ++ error_setg(errp, "Node '%s' not found", node); ++ return NULL; ++ } ++ ++ bitmap = bdrv_find_dirty_bitmap(bs, name); ++ if (!bitmap) { ++ error_setg(errp, "Dirty bitmap '%s' not found", name); ++ return NULL; ++ } ++ ++ if (pbs) { ++ *pbs = bs; ++ } ++ ++ return bitmap; ++} ++ ++void qmp_block_dirty_bitmap_add(const char *node, const char *name, ++ bool has_granularity, uint32_t granularity, ++ bool has_persistent, bool persistent, ++ bool has_disabled, bool disabled, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ if (!name || name[0] == '\0') { ++ error_setg(errp, "Bitmap name cannot be empty"); ++ return; ++ } ++ ++ bs = bdrv_lookup_bs(node, node, errp); ++ if (!bs) { ++ return; ++ } ++ ++ if (has_granularity) { ++ if (granularity < 512 || !is_power_of_2(granularity)) { ++ error_setg(errp, "Granularity must be power of 2 " ++ "and at least 512"); ++ return; ++ } ++ } else { ++ /* Default to cluster size, if available: */ ++ granularity = bdrv_get_default_bitmap_granularity(bs); ++ } ++ ++ if (!has_persistent) { ++ persistent = false; ++ } ++ ++ if (!has_disabled) { ++ disabled = false; ++ } ++ ++ if (persistent && ++ !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) ++ { ++ return; ++ } ++ ++ bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); ++ if (bitmap == NULL) { ++ return; ++ } ++ ++ if (disabled) { ++ bdrv_disable_dirty_bitmap(bitmap); ++ } ++ ++ bdrv_dirty_bitmap_set_persistence(bitmap, persistent); ++} ++ ++BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, ++ bool release, ++ BlockDriverState **bitmap_bs, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap || !bs) { ++ return NULL; ++ } ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, ++ errp)) { ++ return NULL; ++ } ++ ++ if (bdrv_dirty_bitmap_get_persistence(bitmap) && ++ bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) ++ { ++ return NULL; ++ } ++ ++ if (release) { ++ bdrv_release_dirty_bitmap(bitmap); ++ } ++ ++ if (bitmap_bs) { ++ *bitmap_bs = bs; ++ } ++ ++ return release ? NULL : bitmap; ++} ++ ++void qmp_block_dirty_bitmap_remove(const char *node, const char *name, ++ Error **errp) ++{ ++ block_dirty_bitmap_remove(node, name, true, NULL, errp); ++} ++ ++/** ++ * Completely clear a bitmap, for the purposes of synchronizing a bitmap ++ * immediately after a full backup operation. ++ */ ++void qmp_block_dirty_bitmap_clear(const char *node, const char *name, ++ Error **errp) ++{ ++ BdrvDirtyBitmap *bitmap; ++ BlockDriverState *bs; ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap || !bs) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { ++ return; ++ } ++ ++ bdrv_clear_dirty_bitmap(bitmap, NULL); ++} ++ ++void qmp_block_dirty_bitmap_enable(const char *node, const char *name, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return; ++ } ++ ++ bdrv_enable_dirty_bitmap(bitmap); ++} ++ ++void qmp_block_dirty_bitmap_disable(const char *node, const char *name, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *bitmap; ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return; ++ } ++ ++ bdrv_disable_dirty_bitmap(bitmap); ++} ++ ++BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *dst_node, ++ const char *dst_bitmap, ++ BlockDirtyBitmapOrStrList *bms, ++ HBitmap **backup, Error **errp) ++{ ++ BlockDriverState *bs; ++ BdrvDirtyBitmap *dst, *src; ++ BlockDirtyBitmapOrStrList *lst; ++ const char *src_node, *src_bitmap; ++ HBitmap *local_backup = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ dst = block_dirty_bitmap_lookup(dst_node, dst_bitmap, &bs, errp); ++ if (!dst) { ++ return NULL; ++ } ++ ++ for (lst = bms; lst; lst = lst->next) { ++ switch (lst->value->type) { ++ case QTYPE_QSTRING: ++ src_bitmap = lst->value->u.local; ++ src = bdrv_find_dirty_bitmap(bs, src_bitmap); ++ if (!src) { ++ error_setg(errp, "Dirty bitmap '%s' not found", src_bitmap); ++ goto fail; ++ } ++ break; ++ case QTYPE_QDICT: ++ src_node = lst->value->u.external.node; ++ src_bitmap = lst->value->u.external.name; ++ src = block_dirty_bitmap_lookup(src_node, src_bitmap, NULL, errp); ++ if (!src) { ++ goto fail; ++ } ++ break; ++ default: ++ abort(); ++ } ++ ++ /* We do backup only for first merge operation */ ++ if (!bdrv_merge_dirty_bitmap(dst, src, ++ local_backup ? NULL : &local_backup, ++ errp)) ++ { ++ goto fail; ++ } ++ } ++ ++ if (backup) { ++ *backup = local_backup; ++ } else { ++ hbitmap_free(local_backup); ++ } ++ ++ return dst; ++ ++fail: ++ if (local_backup) { ++ bdrv_restore_dirty_bitmap(dst, local_backup); ++ } ++ ++ return NULL; ++} ++ ++void qmp_block_dirty_bitmap_merge(const char *node, const char *target, ++ BlockDirtyBitmapOrStrList *bitmaps, ++ Error **errp) ++{ ++ block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); ++} +diff --git a/qcow2/lib/block/nbd.c b/qcow2/lib/block/nbd.c +new file mode 100644 +index 00000000..2c65cdcf +--- /dev/null ++++ b/qcow2/lib/block/nbd.c +@@ -0,0 +1,2235 @@ ++/* ++ * QEMU Block driver for NBD ++ * ++ * Copyright (c) 2019 Virtuozzo International GmbH. ++ * Copyright Red Hat ++ * Copyright (C) 2008 Bull S.A.S. ++ * Author: Laurent Vivier ++ * ++ * Some parts: ++ * Copyright (C) 2007 Anthony Liguori ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "trace.h" ++#include "qemu/uri.h" ++#include "qemu/option.h" ++#include "qemu/cutils.h" ++#include "qemu/main-loop.h" ++ ++#include "qapi/qapi-visit-sockets.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/clone-visitor.h" ++ ++#include "block/qdict.h" ++#include "block/nbd.h" ++#include "block/block_int.h" ++#include "block/coroutines.h" ++ ++#include "qemu/yank.h" ++ ++#define EN_OPTSTR ":exportname=" ++#define MAX_NBD_REQUESTS 16 ++ ++#define COOKIE_TO_INDEX(cookie) ((cookie) - 1) ++#define INDEX_TO_COOKIE(index) ((index) + 1) ++ ++typedef struct { ++ Coroutine *coroutine; ++ uint64_t offset; /* original offset of the request */ ++ bool receiving; /* sleeping in the yield in nbd_receive_replies */ ++} NBDClientRequest; ++ ++typedef enum NBDClientState { ++ NBD_CLIENT_CONNECTING_WAIT, ++ NBD_CLIENT_CONNECTING_NOWAIT, ++ NBD_CLIENT_CONNECTED, ++ NBD_CLIENT_QUIT ++} NBDClientState; ++ ++typedef struct BDRVNBDState { ++ QIOChannel *ioc; /* The current I/O channel */ ++ NBDExportInfo info; ++ ++ /* ++ * Protects state, free_sema, in_flight, requests[].coroutine, ++ * reconnect_delay_timer. ++ */ ++ QemuMutex requests_lock; ++ NBDClientState state; ++ CoQueue free_sema; ++ unsigned in_flight; ++ NBDClientRequest requests[MAX_NBD_REQUESTS]; ++ QEMUTimer *reconnect_delay_timer; ++ ++ /* Protects sending data on the socket. */ ++ CoMutex send_mutex; ++ ++ /* ++ * Protects receiving reply headers from the socket, as well as the ++ * fields reply and requests[].receiving ++ */ ++ CoMutex receive_mutex; ++ NBDReply reply; ++ ++ QEMUTimer *open_timer; ++ ++ BlockDriverState *bs; ++ ++ /* Connection parameters */ ++ uint32_t reconnect_delay; ++ uint32_t open_timeout; ++ SocketAddress *saddr; ++ char *export; ++ char *tlscredsid; ++ QCryptoTLSCreds *tlscreds; ++ char *tlshostname; ++ char *x_dirty_bitmap; ++ bool alloc_depth; ++ ++ NBDClientConnection *conn; ++} BDRVNBDState; ++ ++static void nbd_yank(void *opaque); ++ ++static void nbd_clear_bdrvstate(BlockDriverState *bs) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ ++ nbd_client_connection_release(s->conn); ++ s->conn = NULL; ++ ++ yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name)); ++ ++ /* Must not leave timers behind that would access freed data */ ++ assert(!s->reconnect_delay_timer); ++ assert(!s->open_timer); ++ ++ object_unref(OBJECT(s->tlscreds)); ++ qapi_free_SocketAddress(s->saddr); ++ s->saddr = NULL; ++ g_free(s->export); ++ s->export = NULL; ++ g_free(s->tlscredsid); ++ s->tlscredsid = NULL; ++ g_free(s->tlshostname); ++ s->tlshostname = NULL; ++ g_free(s->x_dirty_bitmap); ++ s->x_dirty_bitmap = NULL; ++} ++ ++/* Called with s->receive_mutex taken. */ ++static bool coroutine_fn nbd_recv_coroutine_wake_one(NBDClientRequest *req) ++{ ++ if (req->receiving) { ++ req->receiving = false; ++ aio_co_wake(req->coroutine); ++ return true; ++ } ++ ++ return false; ++} ++ ++static void coroutine_fn nbd_recv_coroutines_wake(BDRVNBDState *s) ++{ ++ int i; ++ ++ QEMU_LOCK_GUARD(&s->receive_mutex); ++ for (i = 0; i < MAX_NBD_REQUESTS; i++) { ++ if (nbd_recv_coroutine_wake_one(&s->requests[i])) { ++ return; ++ } ++ } ++} ++ ++/* Called with s->requests_lock held. */ ++static void coroutine_fn nbd_channel_error_locked(BDRVNBDState *s, int ret) ++{ ++ if (s->state == NBD_CLIENT_CONNECTED) { ++ qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); ++ } ++ ++ if (ret == -EIO) { ++ if (s->state == NBD_CLIENT_CONNECTED) { ++ s->state = s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT : ++ NBD_CLIENT_CONNECTING_NOWAIT; ++ } ++ } else { ++ s->state = NBD_CLIENT_QUIT; ++ } ++} ++ ++static void coroutine_fn nbd_channel_error(BDRVNBDState *s, int ret) ++{ ++ QEMU_LOCK_GUARD(&s->requests_lock); ++ nbd_channel_error_locked(s, ret); ++} ++ ++static void reconnect_delay_timer_del(BDRVNBDState *s) ++{ ++ if (s->reconnect_delay_timer) { ++ timer_free(s->reconnect_delay_timer); ++ s->reconnect_delay_timer = NULL; ++ } ++} ++ ++static void reconnect_delay_timer_cb(void *opaque) ++{ ++ BDRVNBDState *s = opaque; ++ ++ reconnect_delay_timer_del(s); ++ WITH_QEMU_LOCK_GUARD(&s->requests_lock) { ++ if (s->state != NBD_CLIENT_CONNECTING_WAIT) { ++ return; ++ } ++ s->state = NBD_CLIENT_CONNECTING_NOWAIT; ++ } ++ nbd_co_establish_connection_cancel(s->conn); ++} ++ ++static void reconnect_delay_timer_init(BDRVNBDState *s, uint64_t expire_time_ns) ++{ ++ assert(!s->reconnect_delay_timer); ++ s->reconnect_delay_timer = aio_timer_new(bdrv_get_aio_context(s->bs), ++ QEMU_CLOCK_REALTIME, ++ SCALE_NS, ++ reconnect_delay_timer_cb, s); ++ timer_mod(s->reconnect_delay_timer, expire_time_ns); ++} ++ ++static void nbd_teardown_connection(BlockDriverState *bs) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ ++ assert(!s->in_flight); ++ ++ if (s->ioc) { ++ qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); ++ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), ++ nbd_yank, s->bs); ++ object_unref(OBJECT(s->ioc)); ++ s->ioc = NULL; ++ } ++ ++ WITH_QEMU_LOCK_GUARD(&s->requests_lock) { ++ s->state = NBD_CLIENT_QUIT; ++ } ++} ++ ++static void open_timer_del(BDRVNBDState *s) ++{ ++ if (s->open_timer) { ++ timer_free(s->open_timer); ++ s->open_timer = NULL; ++ } ++} ++ ++static void open_timer_cb(void *opaque) ++{ ++ BDRVNBDState *s = opaque; ++ ++ nbd_co_establish_connection_cancel(s->conn); ++ open_timer_del(s); ++} ++ ++static void open_timer_init(BDRVNBDState *s, uint64_t expire_time_ns) ++{ ++ assert(!s->open_timer); ++ s->open_timer = aio_timer_new(bdrv_get_aio_context(s->bs), ++ QEMU_CLOCK_REALTIME, ++ SCALE_NS, ++ open_timer_cb, s); ++ timer_mod(s->open_timer, expire_time_ns); ++} ++ ++static bool nbd_client_will_reconnect(BDRVNBDState *s) ++{ ++ /* ++ * Called only after a socket error, so this is not performance sensitive. ++ */ ++ QEMU_LOCK_GUARD(&s->requests_lock); ++ return s->state == NBD_CLIENT_CONNECTING_WAIT; ++} ++ ++/* ++ * Update @bs with information learned during a completed negotiation process. ++ * Return failure if the server's advertised options are incompatible with the ++ * client's needs. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++nbd_handle_updated_info(BlockDriverState *bs, Error **errp) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ int ret; ++ ++ if (s->x_dirty_bitmap) { ++ if (!s->info.base_allocation) { ++ error_setg(errp, "requested x-dirty-bitmap %s not found", ++ s->x_dirty_bitmap); ++ return -EINVAL; ++ } ++ if (strcmp(s->x_dirty_bitmap, "qemu:allocation-depth") == 0) { ++ s->alloc_depth = true; ++ } ++ } ++ ++ if (s->info.flags & NBD_FLAG_READ_ONLY) { ++ ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (s->info.flags & NBD_FLAG_SEND_FUA) { ++ bs->supported_write_flags = BDRV_REQ_FUA; ++ bs->supported_zero_flags |= BDRV_REQ_FUA; ++ } ++ ++ if (s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) { ++ bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; ++ if (s->info.flags & NBD_FLAG_SEND_FAST_ZERO) { ++ bs->supported_zero_flags |= BDRV_REQ_NO_FALLBACK; ++ } ++ } ++ ++ trace_nbd_client_handshake_success(s->export); ++ ++ return 0; ++} ++ ++int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, ++ bool blocking, Error **errp) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ int ret; ++ IO_CODE(); ++ ++ assert_bdrv_graph_readable(); ++ assert(!s->ioc); ++ ++ s->ioc = nbd_co_establish_connection(s->conn, &s->info, blocking, errp); ++ if (!s->ioc) { ++ return -ECONNREFUSED; ++ } ++ ++ yank_register_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), nbd_yank, ++ bs); ++ ++ ret = nbd_handle_updated_info(s->bs, NULL); ++ if (ret < 0) { ++ /* ++ * We have connected, but must fail for other reasons. ++ * Send NBD_CMD_DISC as a courtesy to the server. ++ */ ++ NBDRequest request = { .type = NBD_CMD_DISC, .mode = s->info.mode }; ++ ++ nbd_send_request(s->ioc, &request); ++ ++ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), ++ nbd_yank, bs); ++ object_unref(OBJECT(s->ioc)); ++ s->ioc = NULL; ++ ++ return ret; ++ } ++ ++ qio_channel_set_blocking(s->ioc, false, NULL); ++ qio_channel_set_follow_coroutine_ctx(s->ioc, true); ++ ++ /* successfully connected */ ++ WITH_QEMU_LOCK_GUARD(&s->requests_lock) { ++ s->state = NBD_CLIENT_CONNECTED; ++ } ++ ++ return 0; ++} ++ ++/* Called with s->requests_lock held. */ ++static bool nbd_client_connecting(BDRVNBDState *s) ++{ ++ return s->state == NBD_CLIENT_CONNECTING_WAIT || ++ s->state == NBD_CLIENT_CONNECTING_NOWAIT; ++} ++ ++/* Called with s->requests_lock taken. */ ++static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) ++{ ++ int ret; ++ bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT; ++ ++ /* ++ * Now we are sure that nobody is accessing the channel, and no one will ++ * try until we set the state to CONNECTED. ++ */ ++ assert(nbd_client_connecting(s)); ++ assert(s->in_flight == 1); ++ ++ trace_nbd_reconnect_attempt(s->bs->in_flight); ++ ++ if (blocking && !s->reconnect_delay_timer) { ++ /* ++ * It's the first reconnect attempt after switching to ++ * NBD_CLIENT_CONNECTING_WAIT ++ */ ++ g_assert(s->reconnect_delay); ++ reconnect_delay_timer_init(s, ++ qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ++ s->reconnect_delay * NANOSECONDS_PER_SECOND); ++ } ++ ++ /* Finalize previous connection if any */ ++ if (s->ioc) { ++ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), ++ nbd_yank, s->bs); ++ object_unref(OBJECT(s->ioc)); ++ s->ioc = NULL; ++ } ++ ++ qemu_mutex_unlock(&s->requests_lock); ++ ret = nbd_co_do_establish_connection(s->bs, blocking, NULL); ++ trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight); ++ qemu_mutex_lock(&s->requests_lock); ++ ++ /* ++ * The reconnect attempt is done (maybe successfully, maybe not), so ++ * we no longer need this timer. Delete it so it will not outlive ++ * this I/O request (so draining removes all timers). ++ */ ++ reconnect_delay_timer_del(s); ++} ++ ++static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie, ++ Error **errp) ++{ ++ int ret; ++ uint64_t ind = COOKIE_TO_INDEX(cookie), ind2; ++ QEMU_LOCK_GUARD(&s->receive_mutex); ++ ++ while (true) { ++ if (s->reply.cookie == cookie) { ++ /* We are done */ ++ return 0; ++ } ++ ++ if (s->reply.cookie != 0) { ++ /* ++ * Some other request is being handled now. It should already be ++ * woken by whoever set s->reply.cookie (or never wait in this ++ * yield). So, we should not wake it here. ++ */ ++ ind2 = COOKIE_TO_INDEX(s->reply.cookie); ++ assert(!s->requests[ind2].receiving); ++ ++ s->requests[ind].receiving = true; ++ qemu_co_mutex_unlock(&s->receive_mutex); ++ ++ qemu_coroutine_yield(); ++ /* ++ * We may be woken for 2 reasons: ++ * 1. From this function, executing in parallel coroutine, when our ++ * cookie is received. ++ * 2. From nbd_co_receive_one_chunk(), when previous request is ++ * finished and s->reply.cookie set to 0. ++ * Anyway, it's OK to lock the mutex and go to the next iteration. ++ */ ++ ++ qemu_co_mutex_lock(&s->receive_mutex); ++ assert(!s->requests[ind].receiving); ++ continue; ++ } ++ ++ /* We are under mutex and cookie is 0. We have to do the dirty work. */ ++ assert(s->reply.cookie == 0); ++ ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, s->info.mode, errp); ++ if (ret == 0) { ++ ret = -EIO; ++ error_setg(errp, "server dropped connection"); ++ } ++ if (ret < 0) { ++ nbd_channel_error(s, ret); ++ return ret; ++ } ++ if (nbd_reply_is_structured(&s->reply) && ++ s->info.mode < NBD_MODE_STRUCTURED) { ++ nbd_channel_error(s, -EINVAL); ++ error_setg(errp, "unexpected structured reply"); ++ return -EINVAL; ++ } ++ ind2 = COOKIE_TO_INDEX(s->reply.cookie); ++ if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) { ++ nbd_channel_error(s, -EINVAL); ++ error_setg(errp, "unexpected cookie value"); ++ return -EINVAL; ++ } ++ if (s->reply.cookie == cookie) { ++ /* We are done */ ++ return 0; ++ } ++ nbd_recv_coroutine_wake_one(&s->requests[ind2]); ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++nbd_co_send_request(BlockDriverState *bs, NBDRequest *request, ++ QEMUIOVector *qiov) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ int rc, i = -1; ++ ++ qemu_mutex_lock(&s->requests_lock); ++ while (s->in_flight == MAX_NBD_REQUESTS || ++ (s->state != NBD_CLIENT_CONNECTED && s->in_flight > 0)) { ++ qemu_co_queue_wait(&s->free_sema, &s->requests_lock); ++ } ++ ++ s->in_flight++; ++ if (s->state != NBD_CLIENT_CONNECTED) { ++ if (nbd_client_connecting(s)) { ++ nbd_reconnect_attempt(s); ++ qemu_co_queue_restart_all(&s->free_sema); ++ } ++ if (s->state != NBD_CLIENT_CONNECTED) { ++ rc = -EIO; ++ goto err; ++ } ++ } ++ ++ for (i = 0; i < MAX_NBD_REQUESTS; i++) { ++ if (s->requests[i].coroutine == NULL) { ++ break; ++ } ++ } ++ ++ assert(i < MAX_NBD_REQUESTS); ++ s->requests[i].coroutine = qemu_coroutine_self(); ++ s->requests[i].offset = request->from; ++ s->requests[i].receiving = false; ++ qemu_mutex_unlock(&s->requests_lock); ++ ++ qemu_co_mutex_lock(&s->send_mutex); ++ request->cookie = INDEX_TO_COOKIE(i); ++ request->mode = s->info.mode; ++ ++ assert(s->ioc); ++ ++ if (qiov) { ++ qio_channel_set_cork(s->ioc, true); ++ rc = nbd_send_request(s->ioc, request); ++ if (rc >= 0 && qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, ++ NULL) < 0) { ++ rc = -EIO; ++ } ++ qio_channel_set_cork(s->ioc, false); ++ } else { ++ rc = nbd_send_request(s->ioc, request); ++ } ++ qemu_co_mutex_unlock(&s->send_mutex); ++ ++ if (rc < 0) { ++ qemu_mutex_lock(&s->requests_lock); ++err: ++ nbd_channel_error_locked(s, rc); ++ if (i != -1) { ++ s->requests[i].coroutine = NULL; ++ } ++ s->in_flight--; ++ qemu_co_queue_next(&s->free_sema); ++ qemu_mutex_unlock(&s->requests_lock); ++ } ++ return rc; ++} ++ ++static inline uint16_t payload_advance16(uint8_t **payload) ++{ ++ *payload += 2; ++ return lduw_be_p(*payload - 2); ++} ++ ++static inline uint32_t payload_advance32(uint8_t **payload) ++{ ++ *payload += 4; ++ return ldl_be_p(*payload - 4); ++} ++ ++static inline uint64_t payload_advance64(uint8_t **payload) ++{ ++ *payload += 8; ++ return ldq_be_p(*payload - 8); ++} ++ ++static int nbd_parse_offset_hole_payload(BDRVNBDState *s, ++ NBDStructuredReplyChunk *chunk, ++ uint8_t *payload, uint64_t orig_offset, ++ QEMUIOVector *qiov, Error **errp) ++{ ++ uint64_t offset; ++ uint32_t hole_size; ++ ++ if (chunk->length != sizeof(offset) + sizeof(hole_size)) { ++ error_setg(errp, "Protocol error: invalid payload for " ++ "NBD_REPLY_TYPE_OFFSET_HOLE"); ++ return -EINVAL; ++ } ++ ++ offset = payload_advance64(&payload); ++ hole_size = payload_advance32(&payload); ++ ++ if (!hole_size || offset < orig_offset || hole_size > qiov->size || ++ offset > orig_offset + qiov->size - hole_size) { ++ error_setg(errp, "Protocol error: server sent chunk exceeding requested" ++ " region"); ++ return -EINVAL; ++ } ++ if (s->info.min_block && ++ !QEMU_IS_ALIGNED(hole_size, s->info.min_block)) { ++ trace_nbd_structured_read_compliance("hole"); ++ } ++ ++ qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size); ++ ++ return 0; ++} ++ ++/* ++ * nbd_parse_blockstatus_payload ++ * Based on our request, we expect only one extent in reply, for the ++ * base:allocation context. ++ */ ++static int nbd_parse_blockstatus_payload(BDRVNBDState *s, ++ NBDStructuredReplyChunk *chunk, ++ uint8_t *payload, bool wide, ++ uint64_t orig_length, ++ NBDExtent64 *extent, Error **errp) ++{ ++ uint32_t context_id; ++ uint32_t count; ++ size_t ext_len = wide ? sizeof(*extent) : sizeof(NBDExtent32); ++ size_t pay_len = sizeof(context_id) + wide * sizeof(count) + ext_len; ++ ++ /* The server succeeded, so it must have sent [at least] one extent */ ++ if (chunk->length < pay_len) { ++ error_setg(errp, "Protocol error: invalid payload for " ++ "NBD_REPLY_TYPE_BLOCK_STATUS"); ++ return -EINVAL; ++ } ++ ++ context_id = payload_advance32(&payload); ++ if (s->info.context_id != context_id) { ++ error_setg(errp, "Protocol error: unexpected context id %d for " ++ "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context " ++ "id is %d", context_id, ++ s->info.context_id); ++ return -EINVAL; ++ } ++ ++ if (wide) { ++ count = payload_advance32(&payload); ++ extent->length = payload_advance64(&payload); ++ extent->flags = payload_advance64(&payload); ++ } else { ++ count = 0; ++ extent->length = payload_advance32(&payload); ++ extent->flags = payload_advance32(&payload); ++ } ++ ++ if (extent->length == 0) { ++ error_setg(errp, "Protocol error: server sent status chunk with " ++ "zero length"); ++ return -EINVAL; ++ } ++ ++ /* ++ * A server sending unaligned block status is in violation of the ++ * protocol, but as qemu-nbd 3.1 is such a server (at least for ++ * POSIX files that are not a multiple of 512 bytes, since qemu ++ * rounds files up to 512-byte multiples but lseek(SEEK_HOLE) ++ * still sees an implicit hole beyond the real EOF), it's nicer to ++ * work around the misbehaving server. If the request included ++ * more than the final unaligned block, truncate it back to an ++ * aligned result; if the request was only the final block, round ++ * up to the full block and change the status to fully-allocated ++ * (always a safe status, even if it loses information). ++ */ ++ if (s->info.min_block && !QEMU_IS_ALIGNED(extent->length, ++ s->info.min_block)) { ++ trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); ++ if (extent->length > s->info.min_block) { ++ extent->length = QEMU_ALIGN_DOWN(extent->length, ++ s->info.min_block); ++ } else { ++ extent->length = s->info.min_block; ++ extent->flags = 0; ++ } ++ } ++ ++ /* ++ * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have ++ * sent us any more than one extent, nor should it have included ++ * status beyond our request in that extent. Furthermore, a wide ++ * server should have replied with an accurate count (we left ++ * count at 0 for a narrow server). However, it's easy enough to ++ * ignore the server's noncompliance without killing the ++ * connection; just ignore trailing extents, and clamp things to ++ * the length of our request. ++ */ ++ if (count != wide || chunk->length > pay_len) { ++ trace_nbd_parse_blockstatus_compliance("unexpected extent count"); ++ } ++ if (extent->length > orig_length) { ++ extent->length = orig_length; ++ trace_nbd_parse_blockstatus_compliance("extent length too large"); ++ } ++ ++ /* ++ * HACK: if we are using x-dirty-bitmaps to access ++ * qemu:allocation-depth, treat all depths > 2 the same as 2, ++ * since nbd_client_co_block_status is only expecting the low two ++ * bits to be set. ++ */ ++ if (s->alloc_depth && extent->flags > 2) { ++ extent->flags = 2; ++ } ++ ++ return 0; ++} ++ ++/* ++ * nbd_parse_error_payload ++ * on success @errp contains message describing nbd error reply ++ */ ++static int nbd_parse_error_payload(NBDStructuredReplyChunk *chunk, ++ uint8_t *payload, int *request_ret, ++ Error **errp) ++{ ++ uint32_t error; ++ uint16_t message_size; ++ ++ assert(chunk->type & (1 << 15)); ++ ++ if (chunk->length < sizeof(error) + sizeof(message_size)) { ++ error_setg(errp, ++ "Protocol error: invalid payload for structured error"); ++ return -EINVAL; ++ } ++ ++ error = nbd_errno_to_system_errno(payload_advance32(&payload)); ++ if (error == 0) { ++ error_setg(errp, "Protocol error: server sent structured error chunk " ++ "with error = 0"); ++ return -EINVAL; ++ } ++ ++ *request_ret = -error; ++ message_size = payload_advance16(&payload); ++ ++ if (message_size > chunk->length - sizeof(error) - sizeof(message_size)) { ++ error_setg(errp, "Protocol error: server sent structured error chunk " ++ "with incorrect message size"); ++ return -EINVAL; ++ } ++ ++ /* TODO: Add a trace point to mention the server complaint */ ++ ++ /* TODO handle ERROR_OFFSET */ ++ ++ return 0; ++} ++ ++static int coroutine_fn ++nbd_co_receive_offset_data_payload(BDRVNBDState *s, uint64_t orig_offset, ++ QEMUIOVector *qiov, Error **errp) ++{ ++ QEMUIOVector sub_qiov; ++ uint64_t offset; ++ size_t data_size; ++ int ret; ++ NBDStructuredReplyChunk *chunk = &s->reply.structured; ++ ++ assert(nbd_reply_is_structured(&s->reply)); ++ ++ /* The NBD spec requires at least one byte of payload */ ++ if (chunk->length <= sizeof(offset)) { ++ error_setg(errp, "Protocol error: invalid payload for " ++ "NBD_REPLY_TYPE_OFFSET_DATA"); ++ return -EINVAL; ++ } ++ ++ if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) { ++ return -EIO; ++ } ++ ++ data_size = chunk->length - sizeof(offset); ++ assert(data_size); ++ if (offset < orig_offset || data_size > qiov->size || ++ offset > orig_offset + qiov->size - data_size) { ++ error_setg(errp, "Protocol error: server sent chunk exceeding requested" ++ " region"); ++ return -EINVAL; ++ } ++ if (s->info.min_block && !QEMU_IS_ALIGNED(data_size, s->info.min_block)) { ++ trace_nbd_structured_read_compliance("data"); ++ } ++ ++ qemu_iovec_init(&sub_qiov, qiov->niov); ++ qemu_iovec_concat(&sub_qiov, qiov, offset - orig_offset, data_size); ++ ret = qio_channel_readv_all(s->ioc, sub_qiov.iov, sub_qiov.niov, errp); ++ qemu_iovec_destroy(&sub_qiov); ++ ++ return ret < 0 ? -EIO : 0; ++} ++ ++#define NBD_MAX_MALLOC_PAYLOAD 1000 ++static coroutine_fn int nbd_co_receive_structured_payload( ++ BDRVNBDState *s, void **payload, Error **errp) ++{ ++ int ret; ++ uint32_t len; ++ ++ assert(nbd_reply_is_structured(&s->reply)); ++ ++ len = s->reply.structured.length; ++ ++ if (len == 0) { ++ return 0; ++ } ++ ++ if (payload == NULL) { ++ error_setg(errp, "Unexpected structured payload"); ++ return -EINVAL; ++ } ++ ++ if (len > NBD_MAX_MALLOC_PAYLOAD) { ++ error_setg(errp, "Payload too large"); ++ return -EINVAL; ++ } ++ ++ *payload = g_new(char, len); ++ ret = nbd_read(s->ioc, *payload, len, "structured payload", errp); ++ if (ret < 0) { ++ g_free(*payload); ++ *payload = NULL; ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * nbd_co_do_receive_one_chunk ++ * for simple reply: ++ * set request_ret to received reply error ++ * if qiov is not NULL: read payload to @qiov ++ * for structured reply chunk: ++ * if error chunk: read payload, set @request_ret, do not set @payload ++ * else if offset_data chunk: read payload data to @qiov, do not set @payload ++ * else: read payload to @payload ++ * ++ * If function fails, @errp contains corresponding error message, and the ++ * connection with the server is suspect. If it returns 0, then the ++ * transaction succeeded (although @request_ret may be a negative errno ++ * corresponding to the server's error reply), and errp is unchanged. ++ */ ++static coroutine_fn int nbd_co_do_receive_one_chunk( ++ BDRVNBDState *s, uint64_t cookie, bool only_structured, ++ int *request_ret, QEMUIOVector *qiov, void **payload, Error **errp) ++{ ++ ERRP_GUARD(); ++ int ret; ++ int i = COOKIE_TO_INDEX(cookie); ++ void *local_payload = NULL; ++ NBDStructuredReplyChunk *chunk; ++ ++ if (payload) { ++ *payload = NULL; ++ } ++ *request_ret = 0; ++ ++ ret = nbd_receive_replies(s, cookie, errp); ++ if (ret < 0) { ++ error_prepend(errp, "Connection closed: "); ++ return -EIO; ++ } ++ assert(s->ioc); ++ ++ assert(s->reply.cookie == cookie); ++ ++ if (nbd_reply_is_simple(&s->reply)) { ++ if (only_structured) { ++ error_setg(errp, "Protocol error: simple reply when structured " ++ "reply chunk was expected"); ++ return -EINVAL; ++ } ++ ++ *request_ret = -nbd_errno_to_system_errno(s->reply.simple.error); ++ if (*request_ret < 0 || !qiov) { ++ return 0; ++ } ++ ++ return qio_channel_readv_all(s->ioc, qiov->iov, qiov->niov, ++ errp) < 0 ? -EIO : 0; ++ } ++ ++ /* handle structured reply chunk */ ++ assert(s->info.mode >= NBD_MODE_STRUCTURED); ++ chunk = &s->reply.structured; ++ ++ if (chunk->type == NBD_REPLY_TYPE_NONE) { ++ if (!(chunk->flags & NBD_REPLY_FLAG_DONE)) { ++ error_setg(errp, "Protocol error: NBD_REPLY_TYPE_NONE chunk without" ++ " NBD_REPLY_FLAG_DONE flag set"); ++ return -EINVAL; ++ } ++ if (chunk->length) { ++ error_setg(errp, "Protocol error: NBD_REPLY_TYPE_NONE chunk with" ++ " nonzero length"); ++ return -EINVAL; ++ } ++ return 0; ++ } ++ ++ if (chunk->type == NBD_REPLY_TYPE_OFFSET_DATA) { ++ if (!qiov) { ++ error_setg(errp, "Unexpected NBD_REPLY_TYPE_OFFSET_DATA chunk"); ++ return -EINVAL; ++ } ++ ++ return nbd_co_receive_offset_data_payload(s, s->requests[i].offset, ++ qiov, errp); ++ } ++ ++ if (nbd_reply_type_is_error(chunk->type)) { ++ payload = &local_payload; ++ } ++ ++ ret = nbd_co_receive_structured_payload(s, payload, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (nbd_reply_type_is_error(chunk->type)) { ++ ret = nbd_parse_error_payload(chunk, local_payload, request_ret, errp); ++ g_free(local_payload); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * nbd_co_receive_one_chunk ++ * Read reply, wake up connection_co and set s->quit if needed. ++ * Return value is a fatal error code or normal nbd reply error code ++ */ ++static coroutine_fn int nbd_co_receive_one_chunk( ++ BDRVNBDState *s, uint64_t cookie, bool only_structured, ++ int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload, ++ Error **errp) ++{ ++ int ret = nbd_co_do_receive_one_chunk(s, cookie, only_structured, ++ request_ret, qiov, payload, errp); ++ ++ if (ret < 0) { ++ memset(reply, 0, sizeof(*reply)); ++ nbd_channel_error(s, ret); ++ } else { ++ /* For assert at loop start in nbd_connection_entry */ ++ *reply = s->reply; ++ } ++ s->reply.cookie = 0; ++ ++ nbd_recv_coroutines_wake(s); ++ ++ return ret; ++} ++ ++typedef struct NBDReplyChunkIter { ++ int ret; ++ int request_ret; ++ Error *err; ++ bool done, only_structured; ++} NBDReplyChunkIter; ++ ++static void nbd_iter_channel_error(NBDReplyChunkIter *iter, ++ int ret, Error **local_err) ++{ ++ assert(local_err && *local_err); ++ assert(ret < 0); ++ ++ if (!iter->ret) { ++ iter->ret = ret; ++ error_propagate(&iter->err, *local_err); ++ } else { ++ error_free(*local_err); ++ } ++ ++ *local_err = NULL; ++} ++ ++static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret) ++{ ++ assert(ret < 0); ++ ++ if (!iter->request_ret) { ++ iter->request_ret = ret; ++ } ++} ++ ++/* ++ * NBD_FOREACH_REPLY_CHUNK ++ * The pointer stored in @payload requires g_free() to free it. ++ */ ++#define NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, structured, \ ++ qiov, reply, payload) \ ++ for (iter = (NBDReplyChunkIter) { .only_structured = structured }; \ ++ nbd_reply_chunk_iter_receive(s, &iter, cookie, qiov, reply, payload);) ++ ++/* ++ * nbd_reply_chunk_iter_receive ++ * The pointer stored in @payload requires g_free() to free it. ++ */ ++static bool coroutine_fn nbd_reply_chunk_iter_receive(BDRVNBDState *s, ++ NBDReplyChunkIter *iter, ++ uint64_t cookie, ++ QEMUIOVector *qiov, ++ NBDReply *reply, ++ void **payload) ++{ ++ int ret, request_ret; ++ NBDReply local_reply; ++ NBDStructuredReplyChunk *chunk; ++ Error *local_err = NULL; ++ ++ if (iter->done) { ++ /* Previous iteration was last. */ ++ goto break_loop; ++ } ++ ++ if (reply == NULL) { ++ reply = &local_reply; ++ } ++ ++ ret = nbd_co_receive_one_chunk(s, cookie, iter->only_structured, ++ &request_ret, qiov, reply, payload, ++ &local_err); ++ if (ret < 0) { ++ nbd_iter_channel_error(iter, ret, &local_err); ++ } else if (request_ret < 0) { ++ nbd_iter_request_error(iter, request_ret); ++ } ++ ++ /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ ++ if (nbd_reply_is_simple(reply) || iter->ret < 0) { ++ goto break_loop; ++ } ++ ++ chunk = &reply->structured; ++ iter->only_structured = true; ++ ++ if (chunk->type == NBD_REPLY_TYPE_NONE) { ++ /* NBD_REPLY_FLAG_DONE is already checked in nbd_co_receive_one_chunk */ ++ assert(chunk->flags & NBD_REPLY_FLAG_DONE); ++ goto break_loop; ++ } ++ ++ if (chunk->flags & NBD_REPLY_FLAG_DONE) { ++ /* This iteration is last. */ ++ iter->done = true; ++ } ++ ++ /* Execute the loop body */ ++ return true; ++ ++break_loop: ++ qemu_mutex_lock(&s->requests_lock); ++ s->requests[COOKIE_TO_INDEX(cookie)].coroutine = NULL; ++ s->in_flight--; ++ qemu_co_queue_next(&s->free_sema); ++ qemu_mutex_unlock(&s->requests_lock); ++ ++ return false; ++} ++ ++static int coroutine_fn ++nbd_co_receive_return_code(BDRVNBDState *s, uint64_t cookie, ++ int *request_ret, Error **errp) ++{ ++ NBDReplyChunkIter iter; ++ ++ NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, false, NULL, NULL, NULL) { ++ /* nbd_reply_chunk_iter_receive does all the work */ ++ } ++ ++ error_propagate(errp, iter.err); ++ *request_ret = iter.request_ret; ++ return iter.ret; ++} ++ ++static int coroutine_fn ++nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t cookie, ++ uint64_t offset, QEMUIOVector *qiov, ++ int *request_ret, Error **errp) ++{ ++ NBDReplyChunkIter iter; ++ NBDReply reply; ++ void *payload = NULL; ++ Error *local_err = NULL; ++ ++ NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, ++ s->info.mode >= NBD_MODE_STRUCTURED, ++ qiov, &reply, &payload) ++ { ++ int ret; ++ NBDStructuredReplyChunk *chunk = &reply.structured; ++ ++ assert(nbd_reply_is_structured(&reply)); ++ ++ switch (chunk->type) { ++ case NBD_REPLY_TYPE_OFFSET_DATA: ++ /* ++ * special cased in nbd_co_receive_one_chunk, data is already ++ * in qiov ++ */ ++ break; ++ case NBD_REPLY_TYPE_OFFSET_HOLE: ++ ret = nbd_parse_offset_hole_payload(s, &reply.structured, payload, ++ offset, qiov, &local_err); ++ if (ret < 0) { ++ nbd_channel_error(s, ret); ++ nbd_iter_channel_error(&iter, ret, &local_err); ++ } ++ break; ++ default: ++ if (!nbd_reply_type_is_error(chunk->type)) { ++ /* not allowed reply type */ ++ nbd_channel_error(s, -EINVAL); ++ error_setg(&local_err, ++ "Unexpected reply type: %d (%s) for CMD_READ", ++ chunk->type, nbd_reply_type_lookup(chunk->type)); ++ nbd_iter_channel_error(&iter, -EINVAL, &local_err); ++ } ++ } ++ ++ g_free(payload); ++ payload = NULL; ++ } ++ ++ error_propagate(errp, iter.err); ++ *request_ret = iter.request_ret; ++ return iter.ret; ++} ++ ++static int coroutine_fn ++nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, ++ uint64_t length, NBDExtent64 *extent, ++ int *request_ret, Error **errp) ++{ ++ NBDReplyChunkIter iter; ++ NBDReply reply; ++ void *payload = NULL; ++ Error *local_err = NULL; ++ bool received = false; ++ ++ assert(!extent->length); ++ NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, false, NULL, &reply, &payload) { ++ int ret; ++ NBDStructuredReplyChunk *chunk = &reply.structured; ++ bool wide; ++ ++ assert(nbd_reply_is_structured(&reply)); ++ ++ switch (chunk->type) { ++ case NBD_REPLY_TYPE_BLOCK_STATUS_EXT: ++ case NBD_REPLY_TYPE_BLOCK_STATUS: ++ wide = chunk->type == NBD_REPLY_TYPE_BLOCK_STATUS_EXT; ++ if ((s->info.mode >= NBD_MODE_EXTENDED) != wide) { ++ trace_nbd_extended_headers_compliance("block_status"); ++ } ++ if (received) { ++ nbd_channel_error(s, -EINVAL); ++ error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); ++ nbd_iter_channel_error(&iter, -EINVAL, &local_err); ++ } ++ received = true; ++ ++ ret = nbd_parse_blockstatus_payload( ++ s, &reply.structured, payload, wide, ++ length, extent, &local_err); ++ if (ret < 0) { ++ nbd_channel_error(s, ret); ++ nbd_iter_channel_error(&iter, ret, &local_err); ++ } ++ break; ++ default: ++ if (!nbd_reply_type_is_error(chunk->type)) { ++ nbd_channel_error(s, -EINVAL); ++ error_setg(&local_err, ++ "Unexpected reply type: %d (%s) " ++ "for CMD_BLOCK_STATUS", ++ chunk->type, nbd_reply_type_lookup(chunk->type)); ++ nbd_iter_channel_error(&iter, -EINVAL, &local_err); ++ } ++ } ++ ++ g_free(payload); ++ payload = NULL; ++ } ++ ++ if (!extent->length && !iter.request_ret) { ++ error_setg(&local_err, "Server did not reply with any status extents"); ++ nbd_iter_channel_error(&iter, -EIO, &local_err); ++ } ++ ++ error_propagate(errp, iter.err); ++ *request_ret = iter.request_ret; ++ return iter.ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++nbd_co_request(BlockDriverState *bs, NBDRequest *request, ++ QEMUIOVector *write_qiov) ++{ ++ int ret, request_ret; ++ Error *local_err = NULL; ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ ++ assert(request->type != NBD_CMD_READ); ++ if (write_qiov) { ++ assert(request->type == NBD_CMD_WRITE); ++ assert(request->len == iov_size(write_qiov->iov, write_qiov->niov)); ++ } else { ++ assert(request->type != NBD_CMD_WRITE); ++ } ++ ++ do { ++ ret = nbd_co_send_request(bs, request, write_qiov); ++ if (ret < 0) { ++ continue; ++ } ++ ++ ret = nbd_co_receive_return_code(s, request->cookie, ++ &request_ret, &local_err); ++ if (local_err) { ++ trace_nbd_co_request_fail(request->from, request->len, ++ request->cookie, request->flags, ++ request->type, ++ nbd_cmd_lookup(request->type), ++ ret, error_get_pretty(local_err)); ++ error_free(local_err); ++ local_err = NULL; ++ } ++ } while (ret < 0 && nbd_client_will_reconnect(s)); ++ ++ return ret ? ret : request_ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ int ret, request_ret; ++ Error *local_err = NULL; ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ NBDRequest request = { ++ .type = NBD_CMD_READ, ++ .from = offset, ++ .len = bytes, ++ }; ++ ++ assert(bytes <= NBD_MAX_BUFFER_SIZE); ++ ++ if (!bytes) { ++ return 0; ++ } ++ /* ++ * Work around the fact that the block layer doesn't do ++ * byte-accurate sizing yet - if the read exceeds the server's ++ * advertised size because the block layer rounded size up, then ++ * truncate the request to the server and tail-pad with zero. ++ */ ++ if (offset >= s->info.size) { ++ assert(bytes < BDRV_SECTOR_SIZE); ++ qemu_iovec_memset(qiov, 0, 0, bytes); ++ return 0; ++ } ++ if (offset + bytes > s->info.size) { ++ uint64_t slop = offset + bytes - s->info.size; ++ ++ assert(slop < BDRV_SECTOR_SIZE); ++ qemu_iovec_memset(qiov, bytes - slop, 0, slop); ++ request.len -= slop; ++ } ++ ++ do { ++ ret = nbd_co_send_request(bs, &request, NULL); ++ if (ret < 0) { ++ continue; ++ } ++ ++ ret = nbd_co_receive_cmdread_reply(s, request.cookie, offset, qiov, ++ &request_ret, &local_err); ++ if (local_err) { ++ trace_nbd_co_request_fail(request.from, request.len, request.cookie, ++ request.flags, request.type, ++ nbd_cmd_lookup(request.type), ++ ret, error_get_pretty(local_err)); ++ error_free(local_err); ++ local_err = NULL; ++ } ++ } while (ret < 0 && nbd_client_will_reconnect(s)); ++ ++ return ret ? ret : request_ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ NBDRequest request = { ++ .type = NBD_CMD_WRITE, ++ .from = offset, ++ .len = bytes, ++ }; ++ ++ assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); ++ if (flags & BDRV_REQ_FUA) { ++ assert(s->info.flags & NBD_FLAG_SEND_FUA); ++ request.flags |= NBD_CMD_FLAG_FUA; ++ } ++ ++ assert(bytes <= NBD_MAX_BUFFER_SIZE); ++ ++ if (!bytes) { ++ return 0; ++ } ++ return nbd_co_request(bs, &request, qiov); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ NBDRequest request = { ++ .type = NBD_CMD_WRITE_ZEROES, ++ .from = offset, ++ .len = bytes, ++ }; ++ ++ /* rely on max_pwrite_zeroes */ ++ assert(bytes <= UINT32_MAX || s->info.mode >= NBD_MODE_EXTENDED); ++ ++ assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); ++ if (!(s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) { ++ return -ENOTSUP; ++ } ++ ++ if (flags & BDRV_REQ_FUA) { ++ assert(s->info.flags & NBD_FLAG_SEND_FUA); ++ request.flags |= NBD_CMD_FLAG_FUA; ++ } ++ if (!(flags & BDRV_REQ_MAY_UNMAP)) { ++ request.flags |= NBD_CMD_FLAG_NO_HOLE; ++ } ++ if (flags & BDRV_REQ_NO_FALLBACK) { ++ assert(s->info.flags & NBD_FLAG_SEND_FAST_ZERO); ++ request.flags |= NBD_CMD_FLAG_FAST_ZERO; ++ } ++ ++ if (!bytes) { ++ return 0; ++ } ++ return nbd_co_request(bs, &request, NULL); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK nbd_client_co_flush(BlockDriverState *bs) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ NBDRequest request = { .type = NBD_CMD_FLUSH }; ++ ++ if (!(s->info.flags & NBD_FLAG_SEND_FLUSH)) { ++ return 0; ++ } ++ ++ request.from = 0; ++ request.len = 0; ++ ++ return nbd_co_request(bs, &request, NULL); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ NBDRequest request = { ++ .type = NBD_CMD_TRIM, ++ .from = offset, ++ .len = bytes, ++ }; ++ ++ /* rely on max_pdiscard */ ++ assert(bytes <= UINT32_MAX || s->info.mode >= NBD_MODE_EXTENDED); ++ ++ assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); ++ if (!(s->info.flags & NBD_FLAG_SEND_TRIM) || !bytes) { ++ return 0; ++ } ++ ++ return nbd_co_request(bs, &request, NULL); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( ++ BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, ++ int64_t *pnum, int64_t *map, BlockDriverState **file) ++{ ++ int ret, request_ret; ++ NBDExtent64 extent = { 0 }; ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ Error *local_err = NULL; ++ ++ NBDRequest request = { ++ .type = NBD_CMD_BLOCK_STATUS, ++ .from = offset, ++ .len = MIN(bytes, s->info.size - offset), ++ .flags = NBD_CMD_FLAG_REQ_ONE, ++ }; ++ ++ if (!s->info.base_allocation) { ++ *pnum = bytes; ++ *map = offset; ++ *file = bs; ++ return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; ++ } ++ if (s->info.mode < NBD_MODE_EXTENDED) { ++ request.len = MIN(QEMU_ALIGN_DOWN(INT_MAX, bs->bl.request_alignment), ++ request.len); ++ } ++ ++ /* ++ * Work around the fact that the block layer doesn't do ++ * byte-accurate sizing yet - if the status request exceeds the ++ * server's advertised size because the block layer rounded size ++ * up, we truncated the request to the server (above), or are ++ * called on just the hole. ++ */ ++ if (offset >= s->info.size) { ++ *pnum = bytes; ++ assert(bytes < BDRV_SECTOR_SIZE); ++ /* Intentionally don't report offset_valid for the hole */ ++ return BDRV_BLOCK_ZERO; ++ } ++ ++ if (s->info.min_block) { ++ assert(QEMU_IS_ALIGNED(request.len, s->info.min_block)); ++ } ++ do { ++ ret = nbd_co_send_request(bs, &request, NULL); ++ if (ret < 0) { ++ continue; ++ } ++ ++ ret = nbd_co_receive_blockstatus_reply(s, request.cookie, bytes, ++ &extent, &request_ret, ++ &local_err); ++ if (local_err) { ++ trace_nbd_co_request_fail(request.from, request.len, request.cookie, ++ request.flags, request.type, ++ nbd_cmd_lookup(request.type), ++ ret, error_get_pretty(local_err)); ++ error_free(local_err); ++ local_err = NULL; ++ } ++ } while (ret < 0 && nbd_client_will_reconnect(s)); ++ ++ if (ret < 0 || request_ret < 0) { ++ return ret ? ret : request_ret; ++ } ++ ++ assert(extent.length); ++ *pnum = extent.length; ++ *map = offset; ++ *file = bs; ++ return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) | ++ (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0) | ++ BDRV_BLOCK_OFFSET_VALID; ++} ++ ++static int nbd_client_reopen_prepare(BDRVReopenState *state, ++ BlockReopenQueue *queue, Error **errp) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)state->bs->opaque; ++ ++ if ((state->flags & BDRV_O_RDWR) && (s->info.flags & NBD_FLAG_READ_ONLY)) { ++ error_setg(errp, "Can't reopen read-only NBD mount as read/write"); ++ return -EACCES; ++ } ++ return 0; ++} ++ ++static void nbd_yank(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ ++ QEMU_LOCK_GUARD(&s->requests_lock); ++ qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); ++ s->state = NBD_CLIENT_QUIT; ++} ++ ++static void nbd_client_close(BlockDriverState *bs) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ NBDRequest request = { .type = NBD_CMD_DISC, .mode = s->info.mode }; ++ ++ if (s->ioc) { ++ nbd_send_request(s->ioc, &request); ++ } ++ ++ nbd_teardown_connection(bs); ++} ++ ++ ++/* ++ * Parse nbd_open options ++ */ ++ ++static int nbd_parse_uri(const char *filename, QDict *options) ++{ ++ URI *uri; ++ const char *p; ++ QueryParams *qp = NULL; ++ int ret = 0; ++ bool is_unix; ++ ++ uri = uri_parse(filename); ++ if (!uri) { ++ return -EINVAL; ++ } ++ ++ /* transport */ ++ if (!g_strcmp0(uri->scheme, "nbd")) { ++ is_unix = false; ++ } else if (!g_strcmp0(uri->scheme, "nbd+tcp")) { ++ is_unix = false; ++ } else if (!g_strcmp0(uri->scheme, "nbd+unix")) { ++ is_unix = true; ++ } else { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ p = uri->path ? uri->path : ""; ++ if (p[0] == '/') { ++ p++; ++ } ++ if (p[0]) { ++ qdict_put_str(options, "export", p); ++ } ++ ++ qp = query_params_parse(uri->query); ++ if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (is_unix) { ++ /* nbd+unix:///export?socket=path */ ++ if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { ++ ret = -EINVAL; ++ goto out; ++ } ++ qdict_put_str(options, "server.type", "unix"); ++ qdict_put_str(options, "server.path", qp->p[0].value); ++ } else { ++ QString *host; ++ char *port_str; ++ ++ /* nbd[+tcp]://host[:port]/export */ ++ if (!uri->server) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ /* strip braces from literal IPv6 address */ ++ if (uri->server[0] == '[') { ++ host = qstring_from_substr(uri->server, 1, ++ strlen(uri->server) - 1); ++ } else { ++ host = qstring_from_str(uri->server); ++ } ++ ++ qdict_put_str(options, "server.type", "inet"); ++ qdict_put(options, "server.host", host); ++ ++ port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); ++ qdict_put_str(options, "server.port", port_str); ++ g_free(port_str); ++ } ++ ++out: ++ if (qp) { ++ query_params_free(qp); ++ } ++ uri_free(uri); ++ return ret; ++} ++ ++static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) ++{ ++ const QDictEntry *e; ++ ++ for (e = qdict_first(options); e; e = qdict_next(options, e)) { ++ if (!strcmp(e->key, "host") || ++ !strcmp(e->key, "port") || ++ !strcmp(e->key, "path") || ++ !strcmp(e->key, "export") || ++ strstart(e->key, "server.", NULL)) ++ { ++ error_setg(errp, "Option '%s' cannot be used with a file name", ++ e->key); ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static void nbd_parse_filename(const char *filename, QDict *options, ++ Error **errp) ++{ ++ g_autofree char *file = NULL; ++ char *export_name; ++ const char *host_spec; ++ const char *unixpath; ++ ++ if (nbd_has_filename_options_conflict(options, errp)) { ++ return; ++ } ++ ++ if (strstr(filename, "://")) { ++ int ret = nbd_parse_uri(filename, options); ++ if (ret < 0) { ++ error_setg(errp, "No valid URL specified"); ++ } ++ return; ++ } ++ ++ file = g_strdup(filename); ++ ++ export_name = strstr(file, EN_OPTSTR); ++ if (export_name) { ++ if (export_name[strlen(EN_OPTSTR)] == 0) { ++ return; ++ } ++ export_name[0] = 0; /* truncate 'file' */ ++ export_name += strlen(EN_OPTSTR); ++ ++ qdict_put_str(options, "export", export_name); ++ } ++ ++ /* extract the host_spec - fail if it's not nbd:... */ ++ if (!strstart(file, "nbd:", &host_spec)) { ++ error_setg(errp, "File name string for NBD must start with 'nbd:'"); ++ return; ++ } ++ ++ if (!*host_spec) { ++ return; ++ } ++ ++ /* are we a UNIX or TCP socket? */ ++ if (strstart(host_spec, "unix:", &unixpath)) { ++ qdict_put_str(options, "server.type", "unix"); ++ qdict_put_str(options, "server.path", unixpath); ++ } else { ++ InetSocketAddress *addr = g_new(InetSocketAddress, 1); ++ ++ if (inet_parse(addr, host_spec, errp)) { ++ goto out_inet; ++ } ++ ++ qdict_put_str(options, "server.type", "inet"); ++ qdict_put_str(options, "server.host", addr->host); ++ qdict_put_str(options, "server.port", addr->port); ++ out_inet: ++ qapi_free_InetSocketAddress(addr); ++ } ++} ++ ++static bool nbd_process_legacy_socket_options(QDict *output_options, ++ QemuOpts *legacy_opts, ++ Error **errp) ++{ ++ const char *path = qemu_opt_get(legacy_opts, "path"); ++ const char *host = qemu_opt_get(legacy_opts, "host"); ++ const char *port = qemu_opt_get(legacy_opts, "port"); ++ const QDictEntry *e; ++ ++ if (!path && !host && !port) { ++ return true; ++ } ++ ++ for (e = qdict_first(output_options); e; e = qdict_next(output_options, e)) ++ { ++ if (strstart(e->key, "server.", NULL)) { ++ error_setg(errp, "Cannot use 'server' and path/host/port at the " ++ "same time"); ++ return false; ++ } ++ } ++ ++ if (path && host) { ++ error_setg(errp, "path and host may not be used at the same time"); ++ return false; ++ } else if (path) { ++ if (port) { ++ error_setg(errp, "port may not be used without host"); ++ return false; ++ } ++ ++ qdict_put_str(output_options, "server.type", "unix"); ++ qdict_put_str(output_options, "server.path", path); ++ } else if (host) { ++ qdict_put_str(output_options, "server.type", "inet"); ++ qdict_put_str(output_options, "server.host", host); ++ qdict_put_str(output_options, "server.port", ++ port ?: stringify(NBD_DEFAULT_PORT)); ++ } ++ ++ return true; ++} ++ ++static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, ++ Error **errp) ++{ ++ SocketAddress *saddr = NULL; ++ QDict *addr = NULL; ++ Visitor *iv = NULL; ++ ++ qdict_extract_subqdict(options, &addr, "server."); ++ if (!qdict_size(addr)) { ++ error_setg(errp, "NBD server address missing"); ++ goto done; ++ } ++ ++ iv = qobject_input_visitor_new_flat_confused(addr, errp); ++ if (!iv) { ++ goto done; ++ } ++ ++ if (!visit_type_SocketAddress(iv, NULL, &saddr, errp)) { ++ goto done; ++ } ++ ++ if (socket_address_parse_named_fd(saddr, errp) < 0) { ++ qapi_free_SocketAddress(saddr); ++ saddr = NULL; ++ goto done; ++ } ++ ++done: ++ qobject_unref(addr); ++ visit_free(iv); ++ return saddr; ++} ++ ++static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) ++{ ++ Object *obj; ++ QCryptoTLSCreds *creds; ++ ++ obj = object_resolve_path_component( ++ object_get_objects_root(), id); ++ if (!obj) { ++ error_setg(errp, "No TLS credentials with id '%s'", ++ id); ++ return NULL; ++ } ++ creds = (QCryptoTLSCreds *) ++ object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); ++ if (!creds) { ++ error_setg(errp, "Object with id '%s' is not TLS credentials", ++ id); ++ return NULL; ++ } ++ ++ if (!qcrypto_tls_creds_check_endpoint(creds, ++ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, ++ errp)) { ++ return NULL; ++ } ++ object_ref(obj); ++ return creds; ++} ++ ++ ++static QemuOptsList nbd_runtime_opts = { ++ .name = "nbd", ++ .head = QTAILQ_HEAD_INITIALIZER(nbd_runtime_opts.head), ++ .desc = { ++ { ++ .name = "host", ++ .type = QEMU_OPT_STRING, ++ .help = "TCP host to connect to", ++ }, ++ { ++ .name = "port", ++ .type = QEMU_OPT_STRING, ++ .help = "TCP port to connect to", ++ }, ++ { ++ .name = "path", ++ .type = QEMU_OPT_STRING, ++ .help = "Unix socket path to connect to", ++ }, ++ { ++ .name = "export", ++ .type = QEMU_OPT_STRING, ++ .help = "Name of the NBD export to open", ++ }, ++ { ++ .name = "tls-creds", ++ .type = QEMU_OPT_STRING, ++ .help = "ID of the TLS credentials to use", ++ }, ++ { ++ .name = "tls-hostname", ++ .type = QEMU_OPT_STRING, ++ .help = "Override hostname for validating TLS x509 certificate", ++ }, ++ { ++ .name = "x-dirty-bitmap", ++ .type = QEMU_OPT_STRING, ++ .help = "experimental: expose named dirty bitmap in place of " ++ "block status", ++ }, ++ { ++ .name = "reconnect-delay", ++ .type = QEMU_OPT_NUMBER, ++ .help = "On an unexpected disconnect, the nbd client tries to " ++ "connect again until succeeding or encountering a serious " ++ "error. During the first @reconnect-delay seconds, all " ++ "requests are paused and will be rerun on a successful " ++ "reconnect. After that time, any delayed requests and all " ++ "future requests before a successful reconnect will " ++ "immediately fail. Default 0", ++ }, ++ { ++ .name = "open-timeout", ++ .type = QEMU_OPT_NUMBER, ++ .help = "In seconds. If zero, the nbd driver tries the connection " ++ "only once, and fails to open if the connection fails. " ++ "If non-zero, the nbd driver will repeat connection " ++ "attempts until successful or until @open-timeout seconds " ++ "have elapsed. Default 0", ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++static int nbd_process_options(BlockDriverState *bs, QDict *options, ++ Error **errp) ++{ ++ BDRVNBDState *s = bs->opaque; ++ QemuOpts *opts; ++ int ret = -EINVAL; ++ ++ opts = qemu_opts_create(&nbd_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ goto error; ++ } ++ ++ /* Translate @host, @port, and @path to a SocketAddress */ ++ if (!nbd_process_legacy_socket_options(options, opts, errp)) { ++ goto error; ++ } ++ ++ /* Pop the config into our state object. Exit if invalid. */ ++ s->saddr = nbd_config(s, options, errp); ++ if (!s->saddr) { ++ goto error; ++ } ++ ++ s->export = g_strdup(qemu_opt_get(opts, "export")); ++ if (s->export && strlen(s->export) > NBD_MAX_STRING_SIZE) { ++ error_setg(errp, "export name too long to send to server"); ++ goto error; ++ } ++ ++ s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); ++ if (s->tlscredsid) { ++ s->tlscreds = nbd_get_tls_creds(s->tlscredsid, errp); ++ if (!s->tlscreds) { ++ goto error; ++ } ++ ++ s->tlshostname = g_strdup(qemu_opt_get(opts, "tls-hostname")); ++ if (!s->tlshostname && ++ s->saddr->type == SOCKET_ADDRESS_TYPE_INET) { ++ s->tlshostname = g_strdup(s->saddr->u.inet.host); ++ } ++ } ++ ++ s->x_dirty_bitmap = g_strdup(qemu_opt_get(opts, "x-dirty-bitmap")); ++ if (s->x_dirty_bitmap && strlen(s->x_dirty_bitmap) > NBD_MAX_STRING_SIZE) { ++ error_setg(errp, "x-dirty-bitmap query too long to send to server"); ++ goto error; ++ } ++ ++ s->reconnect_delay = qemu_opt_get_number(opts, "reconnect-delay", 0); ++ s->open_timeout = qemu_opt_get_number(opts, "open-timeout", 0); ++ ++ ret = 0; ++ ++ error: ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static int nbd_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ int ret; ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ ++ s->bs = bs; ++ qemu_mutex_init(&s->requests_lock); ++ qemu_co_queue_init(&s->free_sema); ++ qemu_co_mutex_init(&s->send_mutex); ++ qemu_co_mutex_init(&s->receive_mutex); ++ ++ if (!yank_register_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name), errp)) { ++ return -EEXIST; ++ } ++ ++ ret = nbd_process_options(bs, options, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->conn = nbd_client_connection_new(s->saddr, true, s->export, ++ s->x_dirty_bitmap, s->tlscreds, ++ s->tlshostname); ++ ++ if (s->open_timeout) { ++ nbd_client_connection_enable_retry(s->conn); ++ open_timer_init(s, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ++ s->open_timeout * NANOSECONDS_PER_SECOND); ++ } ++ ++ s->state = NBD_CLIENT_CONNECTING_WAIT; ++ ret = nbd_do_establish_connection(bs, true, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* ++ * The connect attempt is done, so we no longer need this timer. ++ * Delete it, because we do not want it to be around when this node ++ * is drained or closed. ++ */ ++ open_timer_del(s); ++ ++ nbd_client_connection_enable_retry(s->conn); ++ ++ return 0; ++ ++fail: ++ open_timer_del(s); ++ nbd_clear_bdrvstate(bs); ++ return ret; ++} ++ ++static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ uint32_t min = s->info.min_block; ++ uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block); ++ ++ /* ++ * If the server did not advertise an alignment: ++ * - a size that is not sector-aligned implies that an alignment ++ * of 1 can be used to access those tail bytes ++ * - advertisement of block status requires an alignment of 1, so ++ * that we don't violate block layer constraints that block ++ * status is always aligned (as we can't control whether the ++ * server will report sub-sector extents, such as a hole at EOF ++ * on an unaligned POSIX file) ++ * - otherwise, assume the server is so old that we are safer avoiding ++ * sub-sector requests ++ */ ++ if (!min) { ++ min = (!QEMU_IS_ALIGNED(s->info.size, BDRV_SECTOR_SIZE) || ++ s->info.base_allocation) ? 1 : BDRV_SECTOR_SIZE; ++ } ++ ++ bs->bl.request_alignment = min; ++ bs->bl.max_pdiscard = QEMU_ALIGN_DOWN(INT_MAX, min); ++ bs->bl.max_pwrite_zeroes = max; ++ bs->bl.max_transfer = max; ++ ++ /* ++ * Assume that if the server supports extended headers, it also ++ * supports unlimited size zero and trim commands. ++ */ ++ if (s->info.mode >= NBD_MODE_EXTENDED) { ++ bs->bl.max_pdiscard = bs->bl.max_pwrite_zeroes = 0; ++ } ++ ++ if (s->info.opt_block && ++ s->info.opt_block > bs->bl.opt_transfer) { ++ bs->bl.opt_transfer = s->info.opt_block; ++ } ++} ++ ++static void nbd_close(BlockDriverState *bs) ++{ ++ nbd_client_close(bs); ++ nbd_clear_bdrvstate(bs); ++} ++ ++/* ++ * NBD cannot truncate, but if the caller asks to truncate to the same size, or ++ * to a smaller size with exact=false, there is no reason to fail the ++ * operation. ++ * ++ * Preallocation mode is ignored since it does not seems useful to fail when ++ * we never change anything. ++ */ ++static int coroutine_fn nbd_co_truncate(BlockDriverState *bs, int64_t offset, ++ bool exact, PreallocMode prealloc, ++ BdrvRequestFlags flags, Error **errp) ++{ ++ BDRVNBDState *s = bs->opaque; ++ ++ if (offset != s->info.size && exact) { ++ error_setg(errp, "Cannot resize NBD nodes"); ++ return -ENOTSUP; ++ } ++ ++ if (offset > s->info.size) { ++ error_setg(errp, "Cannot grow NBD nodes"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int64_t coroutine_fn nbd_co_getlength(BlockDriverState *bs) ++{ ++ BDRVNBDState *s = bs->opaque; ++ ++ return s->info.size; ++} ++ ++static void nbd_refresh_filename(BlockDriverState *bs) ++{ ++ BDRVNBDState *s = bs->opaque; ++ const char *host = NULL, *port = NULL, *path = NULL; ++ size_t len = 0; ++ ++ if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) { ++ const InetSocketAddress *inet = &s->saddr->u.inet; ++ if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) { ++ host = inet->host; ++ port = inet->port; ++ } ++ } else if (s->saddr->type == SOCKET_ADDRESS_TYPE_UNIX) { ++ path = s->saddr->u.q_unix.path; ++ } /* else can't represent as pseudo-filename */ ++ ++ if (path && s->export) { ++ len = snprintf(bs->exact_filename, sizeof(bs->exact_filename), ++ "nbd+unix:///%s?socket=%s", s->export, path); ++ } else if (path && !s->export) { ++ len = snprintf(bs->exact_filename, sizeof(bs->exact_filename), ++ "nbd+unix://?socket=%s", path); ++ } else if (host && s->export) { ++ len = snprintf(bs->exact_filename, sizeof(bs->exact_filename), ++ "nbd://%s:%s/%s", host, port, s->export); ++ } else if (host && !s->export) { ++ len = snprintf(bs->exact_filename, sizeof(bs->exact_filename), ++ "nbd://%s:%s", host, port); ++ } ++ if (len >= sizeof(bs->exact_filename)) { ++ /* Name is too long to represent exactly, so leave it empty. */ ++ bs->exact_filename[0] = '\0'; ++ } ++} ++ ++static char *nbd_dirname(BlockDriverState *bs, Error **errp) ++{ ++ /* The generic bdrv_dirname() implementation is able to work out some ++ * directory name for NBD nodes, but that would be wrong. So far there is no ++ * specification for how "export paths" would work, so NBD does not have ++ * directory names. */ ++ error_setg(errp, "Cannot generate a base directory for NBD nodes"); ++ return NULL; ++} ++ ++static const char *const nbd_strong_runtime_opts[] = { ++ "path", ++ "host", ++ "port", ++ "export", ++ "tls-creds", ++ "tls-hostname", ++ "server.", ++ ++ NULL ++}; ++ ++static void nbd_cancel_in_flight(BlockDriverState *bs) ++{ ++ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; ++ ++ reconnect_delay_timer_del(s); ++ ++ qemu_mutex_lock(&s->requests_lock); ++ if (s->state == NBD_CLIENT_CONNECTING_WAIT) { ++ s->state = NBD_CLIENT_CONNECTING_NOWAIT; ++ } ++ qemu_mutex_unlock(&s->requests_lock); ++ ++ nbd_co_establish_connection_cancel(s->conn); ++} ++ ++static void nbd_attach_aio_context(BlockDriverState *bs, ++ AioContext *new_context) ++{ ++ BDRVNBDState *s = bs->opaque; ++ ++ /* The open_timer is used only during nbd_open() */ ++ assert(!s->open_timer); ++ ++ /* ++ * The reconnect_delay_timer is scheduled in I/O paths when the ++ * connection is lost, to cancel the reconnection attempt after a ++ * given time. Once this attempt is done (successfully or not), ++ * nbd_reconnect_attempt() ensures the timer is deleted before the ++ * respective I/O request is resumed. ++ * Since the AioContext can only be changed when a node is drained, ++ * the reconnect_delay_timer cannot be active here. ++ */ ++ assert(!s->reconnect_delay_timer); ++} ++ ++static void nbd_detach_aio_context(BlockDriverState *bs) ++{ ++ BDRVNBDState *s = bs->opaque; ++ ++ assert(!s->open_timer); ++ assert(!s->reconnect_delay_timer); ++} ++ ++static BlockDriver bdrv_nbd = { ++ .format_name = "nbd", ++ .protocol_name = "nbd", ++ .instance_size = sizeof(BDRVNBDState), ++ .bdrv_parse_filename = nbd_parse_filename, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .bdrv_open = nbd_open, ++ .bdrv_reopen_prepare = nbd_client_reopen_prepare, ++ .bdrv_co_preadv = nbd_client_co_preadv, ++ .bdrv_co_pwritev = nbd_client_co_pwritev, ++ .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, ++ .bdrv_close = nbd_close, ++ .bdrv_co_flush_to_os = nbd_client_co_flush, ++ .bdrv_co_pdiscard = nbd_client_co_pdiscard, ++ .bdrv_refresh_limits = nbd_refresh_limits, ++ .bdrv_co_truncate = nbd_co_truncate, ++ .bdrv_co_getlength = nbd_co_getlength, ++ .bdrv_refresh_filename = nbd_refresh_filename, ++ .bdrv_co_block_status = nbd_client_co_block_status, ++ .bdrv_dirname = nbd_dirname, ++ .strong_runtime_opts = nbd_strong_runtime_opts, ++ .bdrv_cancel_in_flight = nbd_cancel_in_flight, ++ ++ .bdrv_attach_aio_context = nbd_attach_aio_context, ++ .bdrv_detach_aio_context = nbd_detach_aio_context, ++}; ++ ++static BlockDriver bdrv_nbd_tcp = { ++ .format_name = "nbd", ++ .protocol_name = "nbd+tcp", ++ .instance_size = sizeof(BDRVNBDState), ++ .bdrv_parse_filename = nbd_parse_filename, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .bdrv_open = nbd_open, ++ .bdrv_reopen_prepare = nbd_client_reopen_prepare, ++ .bdrv_co_preadv = nbd_client_co_preadv, ++ .bdrv_co_pwritev = nbd_client_co_pwritev, ++ .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, ++ .bdrv_close = nbd_close, ++ .bdrv_co_flush_to_os = nbd_client_co_flush, ++ .bdrv_co_pdiscard = nbd_client_co_pdiscard, ++ .bdrv_refresh_limits = nbd_refresh_limits, ++ .bdrv_co_truncate = nbd_co_truncate, ++ .bdrv_co_getlength = nbd_co_getlength, ++ .bdrv_refresh_filename = nbd_refresh_filename, ++ .bdrv_co_block_status = nbd_client_co_block_status, ++ .bdrv_dirname = nbd_dirname, ++ .strong_runtime_opts = nbd_strong_runtime_opts, ++ .bdrv_cancel_in_flight = nbd_cancel_in_flight, ++ ++ .bdrv_attach_aio_context = nbd_attach_aio_context, ++ .bdrv_detach_aio_context = nbd_detach_aio_context, ++}; ++ ++static BlockDriver bdrv_nbd_unix = { ++ .format_name = "nbd", ++ .protocol_name = "nbd+unix", ++ .instance_size = sizeof(BDRVNBDState), ++ .bdrv_parse_filename = nbd_parse_filename, ++ .bdrv_co_create_opts = bdrv_co_create_opts_simple, ++ .create_opts = &bdrv_create_opts_simple, ++ .bdrv_open = nbd_open, ++ .bdrv_reopen_prepare = nbd_client_reopen_prepare, ++ .bdrv_co_preadv = nbd_client_co_preadv, ++ .bdrv_co_pwritev = nbd_client_co_pwritev, ++ .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, ++ .bdrv_close = nbd_close, ++ .bdrv_co_flush_to_os = nbd_client_co_flush, ++ .bdrv_co_pdiscard = nbd_client_co_pdiscard, ++ .bdrv_refresh_limits = nbd_refresh_limits, ++ .bdrv_co_truncate = nbd_co_truncate, ++ .bdrv_co_getlength = nbd_co_getlength, ++ .bdrv_refresh_filename = nbd_refresh_filename, ++ .bdrv_co_block_status = nbd_client_co_block_status, ++ .bdrv_dirname = nbd_dirname, ++ .strong_runtime_opts = nbd_strong_runtime_opts, ++ .bdrv_cancel_in_flight = nbd_cancel_in_flight, ++ ++ .bdrv_attach_aio_context = nbd_attach_aio_context, ++ .bdrv_detach_aio_context = nbd_detach_aio_context, ++}; ++ ++static void bdrv_nbd_init(void) ++{ ++ bdrv_register(&bdrv_nbd); ++ bdrv_register(&bdrv_nbd_tcp); ++ bdrv_register(&bdrv_nbd_unix); ++} ++ ++block_init(bdrv_nbd_init); +diff --git a/qcow2/lib/block/progress_meter.c b/qcow2/lib/block/progress_meter.c +new file mode 100644 +index 00000000..31a170a2 +--- /dev/null ++++ b/qcow2/lib/block/progress_meter.c +@@ -0,0 +1,66 @@ ++/* ++ * Helper functionality for some process progress tracking. ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * Copyright (c) 2020 Virtuozzo International GmbH ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/coroutine.h" ++#include "qemu/progress_meter.h" ++ ++void progress_init(ProgressMeter *pm) ++{ ++ qemu_mutex_init(&pm->lock); ++} ++ ++void progress_destroy(ProgressMeter *pm) ++{ ++ qemu_mutex_destroy(&pm->lock); ++} ++ ++void progress_get_snapshot(ProgressMeter *pm, uint64_t *current, ++ uint64_t *total) ++{ ++ QEMU_LOCK_GUARD(&pm->lock); ++ ++ *current = pm->current; ++ *total = pm->total; ++} ++ ++void progress_work_done(ProgressMeter *pm, uint64_t done) ++{ ++ QEMU_LOCK_GUARD(&pm->lock); ++ pm->current += done; ++} ++ ++void progress_set_remaining(ProgressMeter *pm, uint64_t remaining) ++{ ++ QEMU_LOCK_GUARD(&pm->lock); ++ pm->total = pm->current + remaining; ++} ++ ++void progress_increase_remaining(ProgressMeter *pm, uint64_t delta) ++{ ++ QEMU_LOCK_GUARD(&pm->lock); ++ pm->total += delta; ++} +diff --git a/qcow2/lib/block/qapi.c b/qcow2/lib/block/qapi.c +new file mode 100644 +index 00000000..2b5793f1 +--- /dev/null ++++ b/qcow2/lib/block/qapi.c +@@ -0,0 +1,1012 @@ ++/* ++ * Block layer qmp and info dump related functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "block/qapi.h" ++#include "block/block_int.h" ++#include "block/dirty-bitmap.h" ++#include "block/throttle-groups.h" ++#include "block/write-threshold.h" ++#include "qapi/error.h" ++#include "qapi/qapi-commands-block-core.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/qemu-print.h" ++#include "sysemu/block-backend.h" ++ ++BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, ++ BlockDriverState *bs, ++ bool flat, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ ImageInfo **p_image_info; ++ ImageInfo *backing_info; ++ BlockDriverState *backing; ++ BlockDeviceInfo *info; ++ ++ if (!bs->drv) { ++ error_setg(errp, "Block device %s is ejected", bs->node_name); ++ return NULL; ++ } ++ ++ bdrv_refresh_filename(bs); ++ ++ info = g_malloc0(sizeof(*info)); ++ info->file = g_strdup(bs->filename); ++ info->ro = bdrv_is_read_only(bs); ++ info->drv = g_strdup(bs->drv->format_name); ++ info->encrypted = bs->encrypted; ++ ++ info->cache = g_new(BlockdevCacheInfo, 1); ++ *info->cache = (BlockdevCacheInfo) { ++ .writeback = blk ? blk_enable_write_cache(blk) : true, ++ .direct = !!(bs->open_flags & BDRV_O_NOCACHE), ++ .no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH), ++ }; ++ ++ if (bs->node_name[0]) { ++ info->node_name = g_strdup(bs->node_name); ++ } ++ ++ backing = bdrv_cow_bs(bs); ++ if (backing) { ++ info->backing_file = g_strdup(backing->filename); ++ } ++ ++ if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { ++ info->has_dirty_bitmaps = true; ++ info->dirty_bitmaps = bdrv_query_dirty_bitmaps(bs); ++ } ++ ++ info->detect_zeroes = bs->detect_zeroes; ++ ++ if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) { ++ ThrottleConfig cfg; ++ BlockBackendPublic *blkp = blk_get_public(blk); ++ ++ throttle_group_get_config(&blkp->throttle_group_member, &cfg); ++ ++ info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; ++ info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; ++ info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; ++ ++ info->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg; ++ info->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg; ++ info->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg; ++ ++ info->has_bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; ++ info->bps_max = cfg.buckets[THROTTLE_BPS_TOTAL].max; ++ info->has_bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; ++ info->bps_rd_max = cfg.buckets[THROTTLE_BPS_READ].max; ++ info->has_bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; ++ info->bps_wr_max = cfg.buckets[THROTTLE_BPS_WRITE].max; ++ ++ info->has_iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; ++ info->iops_max = cfg.buckets[THROTTLE_OPS_TOTAL].max; ++ info->has_iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; ++ info->iops_rd_max = cfg.buckets[THROTTLE_OPS_READ].max; ++ info->has_iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; ++ info->iops_wr_max = cfg.buckets[THROTTLE_OPS_WRITE].max; ++ ++ info->has_bps_max_length = info->has_bps_max; ++ info->bps_max_length = ++ cfg.buckets[THROTTLE_BPS_TOTAL].burst_length; ++ info->has_bps_rd_max_length = info->has_bps_rd_max; ++ info->bps_rd_max_length = ++ cfg.buckets[THROTTLE_BPS_READ].burst_length; ++ info->has_bps_wr_max_length = info->has_bps_wr_max; ++ info->bps_wr_max_length = ++ cfg.buckets[THROTTLE_BPS_WRITE].burst_length; ++ ++ info->has_iops_max_length = info->has_iops_max; ++ info->iops_max_length = ++ cfg.buckets[THROTTLE_OPS_TOTAL].burst_length; ++ info->has_iops_rd_max_length = info->has_iops_rd_max; ++ info->iops_rd_max_length = ++ cfg.buckets[THROTTLE_OPS_READ].burst_length; ++ info->has_iops_wr_max_length = info->has_iops_wr_max; ++ info->iops_wr_max_length = ++ cfg.buckets[THROTTLE_OPS_WRITE].burst_length; ++ ++ info->has_iops_size = cfg.op_size; ++ info->iops_size = cfg.op_size; ++ ++ info->group = ++ g_strdup(throttle_group_get_name(&blkp->throttle_group_member)); ++ } ++ ++ info->write_threshold = bdrv_write_threshold_get(bs); ++ ++ p_image_info = &info->image; ++ info->backing_file_depth = 0; ++ ++ /* ++ * Skip automatically inserted nodes that the user isn't aware of for ++ * query-block (blk != NULL), but not for query-named-block-nodes ++ */ ++ bdrv_query_image_info(bs, p_image_info, flat, blk != NULL, errp); ++ if (*errp) { ++ qapi_free_BlockDeviceInfo(info); ++ return NULL; ++ } ++ ++ backing_info = info->image->backing_image; ++ while (backing_info) { ++ info->backing_file_depth++; ++ backing_info = backing_info->backing_image; ++ } ++ ++ return info; ++} ++ ++/* ++ * Returns 0 on success, with *p_list either set to describe snapshot ++ * information, or NULL because there are no snapshots. Returns -errno on ++ * error, with *p_list untouched. ++ */ ++int bdrv_query_snapshot_info_list(BlockDriverState *bs, ++ SnapshotInfoList **p_list, ++ Error **errp) ++{ ++ int i, sn_count; ++ QEMUSnapshotInfo *sn_tab = NULL; ++ SnapshotInfoList *head = NULL, **tail = &head; ++ SnapshotInfo *info; ++ ++ sn_count = bdrv_snapshot_list(bs, &sn_tab); ++ if (sn_count < 0) { ++ const char *dev = bdrv_get_device_name(bs); ++ switch (sn_count) { ++ case -ENOMEDIUM: ++ error_setg(errp, "Device '%s' is not inserted", dev); ++ break; ++ case -ENOTSUP: ++ error_setg(errp, ++ "Device '%s' does not support internal snapshots", ++ dev); ++ break; ++ default: ++ error_setg_errno(errp, -sn_count, ++ "Can't list snapshots of device '%s'", dev); ++ break; ++ } ++ return sn_count; ++ } ++ ++ for (i = 0; i < sn_count; i++) { ++ info = g_new0(SnapshotInfo, 1); ++ info->id = g_strdup(sn_tab[i].id_str); ++ info->name = g_strdup(sn_tab[i].name); ++ info->vm_state_size = sn_tab[i].vm_state_size; ++ info->date_sec = sn_tab[i].date_sec; ++ info->date_nsec = sn_tab[i].date_nsec; ++ info->vm_clock_sec = sn_tab[i].vm_clock_nsec / 1000000000; ++ info->vm_clock_nsec = sn_tab[i].vm_clock_nsec % 1000000000; ++ info->icount = sn_tab[i].icount; ++ info->has_icount = sn_tab[i].icount != -1ULL; ++ ++ QAPI_LIST_APPEND(tail, info); ++ } ++ ++ g_free(sn_tab); ++ *p_list = head; ++ return 0; ++} ++ ++/** ++ * Helper function for other query info functions. Store information about @bs ++ * in @info, setting @errp on error. ++ */ ++static void GRAPH_RDLOCK ++bdrv_do_query_node_info(BlockDriverState *bs, BlockNodeInfo *info, Error **errp) ++{ ++ int64_t size; ++ const char *backing_filename; ++ BlockDriverInfo bdi; ++ int ret; ++ Error *err = NULL; ++ ++ size = bdrv_getlength(bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "Can't get image size '%s'", ++ bs->exact_filename); ++ return; ++ } ++ ++ bdrv_refresh_filename(bs); ++ ++ info->filename = g_strdup(bs->filename); ++ info->format = g_strdup(bdrv_get_format_name(bs)); ++ info->virtual_size = size; ++ info->actual_size = bdrv_get_allocated_file_size(bs); ++ info->has_actual_size = info->actual_size >= 0; ++ if (bs->encrypted) { ++ info->encrypted = true; ++ info->has_encrypted = true; ++ } ++ if (bdrv_get_info(bs, &bdi) >= 0) { ++ if (bdi.cluster_size != 0) { ++ info->cluster_size = bdi.cluster_size; ++ info->has_cluster_size = true; ++ } ++ info->dirty_flag = bdi.is_dirty; ++ info->has_dirty_flag = true; ++ } ++ info->format_specific = bdrv_get_specific_info(bs, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ backing_filename = bs->backing_file; ++ if (backing_filename[0] != '\0') { ++ char *backing_filename2; ++ ++ info->backing_filename = g_strdup(backing_filename); ++ backing_filename2 = bdrv_get_full_backing_filename(bs, NULL); ++ ++ /* Always report the full_backing_filename if present, even if it's the ++ * same as backing_filename. That they are same is useful info. */ ++ if (backing_filename2) { ++ info->full_backing_filename = g_strdup(backing_filename2); ++ } ++ ++ if (bs->backing_format[0]) { ++ info->backing_filename_format = g_strdup(bs->backing_format); ++ } ++ g_free(backing_filename2); ++ } ++ ++ ret = bdrv_query_snapshot_info_list(bs, &info->snapshots, &err); ++ switch (ret) { ++ case 0: ++ if (info->snapshots) { ++ info->has_snapshots = true; ++ } ++ break; ++ /* recoverable error */ ++ case -ENOMEDIUM: ++ case -ENOTSUP: ++ error_free(err); ++ break; ++ default: ++ error_propagate(errp, err); ++ return; ++ } ++} ++ ++/** ++ * bdrv_query_image_info: ++ * @bs: block node to examine ++ * @p_info: location to store image information ++ * @flat: skip backing node information ++ * @skip_implicit_filters: skip implicit filters in the backing chain ++ * @errp: location to store error information ++ * ++ * Store image information in @p_info, potentially recursively covering the ++ * backing chain. ++ * ++ * If @flat is true, do not query backing image information, i.e. ++ * (*p_info)->has_backing_image will be set to false and ++ * (*p_info)->backing_image to NULL even when the image does in fact have a ++ * backing image. ++ * ++ * If @skip_implicit_filters is true, implicit filter nodes in the backing chain ++ * will be skipped when querying backing image information. ++ * (@skip_implicit_filters is ignored when @flat is true.) ++ * ++ * @p_info will be set only on success. On error, store error in @errp. ++ */ ++void bdrv_query_image_info(BlockDriverState *bs, ++ ImageInfo **p_info, ++ bool flat, ++ bool skip_implicit_filters, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ ImageInfo *info; ++ ++ info = g_new0(ImageInfo, 1); ++ bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp); ++ if (*errp) { ++ goto fail; ++ } ++ ++ if (!flat) { ++ BlockDriverState *backing; ++ ++ /* ++ * Use any filtered child here (for backwards compatibility to when ++ * we always took bs->backing, which might be any filtered child). ++ */ ++ backing = bdrv_filter_or_cow_bs(bs); ++ if (skip_implicit_filters) { ++ backing = bdrv_skip_implicit_filters(backing); ++ } ++ ++ if (backing) { ++ bdrv_query_image_info(backing, &info->backing_image, false, ++ skip_implicit_filters, errp); ++ if (*errp) { ++ goto fail; ++ } ++ } ++ } ++ ++ *p_info = info; ++ return; ++ ++fail: ++ assert(*errp); ++ qapi_free_ImageInfo(info); ++} ++ ++/** ++ * bdrv_query_block_graph_info: ++ * @bs: root node to start from ++ * @p_info: location to store image information ++ * @errp: location to store error information ++ * ++ * Store image information about the graph starting from @bs in @p_info. ++ * ++ * @p_info will be set only on success. On error, store error in @errp. ++ */ ++void bdrv_query_block_graph_info(BlockDriverState *bs, ++ BlockGraphInfo **p_info, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ BlockGraphInfo *info; ++ BlockChildInfoList **children_list_tail; ++ BdrvChild *c; ++ ++ info = g_new0(BlockGraphInfo, 1); ++ bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp); ++ if (*errp) { ++ goto fail; ++ } ++ ++ children_list_tail = &info->children; ++ ++ QLIST_FOREACH(c, &bs->children, next) { ++ BlockChildInfo *c_info; ++ ++ c_info = g_new0(BlockChildInfo, 1); ++ QAPI_LIST_APPEND(children_list_tail, c_info); ++ ++ c_info->name = g_strdup(c->name); ++ bdrv_query_block_graph_info(c->bs, &c_info->info, errp); ++ if (*errp) { ++ goto fail; ++ } ++ } ++ ++ *p_info = info; ++ return; ++ ++fail: ++ assert(*errp != NULL); ++ qapi_free_BlockGraphInfo(info); ++} ++ ++/* @p_info will be set only on success. */ ++static void GRAPH_RDLOCK ++bdrv_query_info(BlockBackend *blk, BlockInfo **p_info, Error **errp) ++{ ++ BlockInfo *info = g_malloc0(sizeof(*info)); ++ BlockDriverState *bs = blk_bs(blk); ++ char *qdev; ++ ++ /* Skip automatically inserted nodes that the user isn't aware of */ ++ bs = bdrv_skip_implicit_filters(bs); ++ ++ info->device = g_strdup(blk_name(blk)); ++ info->type = g_strdup("unknown"); ++ info->locked = blk_dev_is_medium_locked(blk); ++ info->removable = blk_dev_has_removable_media(blk); ++ ++ qdev = blk_get_attached_dev_id(blk); ++ if (qdev && *qdev) { ++ info->qdev = qdev; ++ } else { ++ g_free(qdev); ++ } ++ ++ if (blk_dev_has_tray(blk)) { ++ info->has_tray_open = true; ++ info->tray_open = blk_dev_is_tray_open(blk); ++ } ++ ++ if (blk_iostatus_is_enabled(blk)) { ++ info->has_io_status = true; ++ info->io_status = blk_iostatus(blk); ++ } ++ ++ if (bs && bs->drv) { ++ info->inserted = bdrv_block_device_info(blk, bs, false, errp); ++ if (info->inserted == NULL) { ++ goto err; ++ } ++ } ++ ++ *p_info = info; ++ return; ++ ++ err: ++ qapi_free_BlockInfo(info); ++} ++ ++static uint64List *uint64_list(uint64_t *list, int size) ++{ ++ int i; ++ uint64List *out_list = NULL; ++ uint64List **tail = &out_list; ++ ++ for (i = 0; i < size; i++) { ++ QAPI_LIST_APPEND(tail, list[i]); ++ } ++ ++ return out_list; ++} ++ ++static BlockLatencyHistogramInfo * ++bdrv_latency_histogram_stats(BlockLatencyHistogram *hist) ++{ ++ BlockLatencyHistogramInfo *info; ++ ++ if (!hist->bins) { ++ return NULL; ++ } ++ ++ info = g_new0(BlockLatencyHistogramInfo, 1); ++ info->boundaries = uint64_list(hist->boundaries, hist->nbins - 1); ++ info->bins = uint64_list(hist->bins, hist->nbins); ++ return info; ++} ++ ++static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) ++{ ++ BlockAcctStats *stats = blk_get_stats(blk); ++ BlockAcctTimedStats *ts = NULL; ++ BlockLatencyHistogram *hgram; ++ ++ ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; ++ ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; ++ ds->zone_append_bytes = stats->nr_bytes[BLOCK_ACCT_ZONE_APPEND]; ++ ds->unmap_bytes = stats->nr_bytes[BLOCK_ACCT_UNMAP]; ++ ds->rd_operations = stats->nr_ops[BLOCK_ACCT_READ]; ++ ds->wr_operations = stats->nr_ops[BLOCK_ACCT_WRITE]; ++ ds->zone_append_operations = stats->nr_ops[BLOCK_ACCT_ZONE_APPEND]; ++ ds->unmap_operations = stats->nr_ops[BLOCK_ACCT_UNMAP]; ++ ++ ds->failed_rd_operations = stats->failed_ops[BLOCK_ACCT_READ]; ++ ds->failed_wr_operations = stats->failed_ops[BLOCK_ACCT_WRITE]; ++ ds->failed_zone_append_operations = ++ stats->failed_ops[BLOCK_ACCT_ZONE_APPEND]; ++ ds->failed_flush_operations = stats->failed_ops[BLOCK_ACCT_FLUSH]; ++ ds->failed_unmap_operations = stats->failed_ops[BLOCK_ACCT_UNMAP]; ++ ++ ds->invalid_rd_operations = stats->invalid_ops[BLOCK_ACCT_READ]; ++ ds->invalid_wr_operations = stats->invalid_ops[BLOCK_ACCT_WRITE]; ++ ds->invalid_zone_append_operations = ++ stats->invalid_ops[BLOCK_ACCT_ZONE_APPEND]; ++ ds->invalid_flush_operations = ++ stats->invalid_ops[BLOCK_ACCT_FLUSH]; ++ ds->invalid_unmap_operations = stats->invalid_ops[BLOCK_ACCT_UNMAP]; ++ ++ ds->rd_merged = stats->merged[BLOCK_ACCT_READ]; ++ ds->wr_merged = stats->merged[BLOCK_ACCT_WRITE]; ++ ds->zone_append_merged = stats->merged[BLOCK_ACCT_ZONE_APPEND]; ++ ds->unmap_merged = stats->merged[BLOCK_ACCT_UNMAP]; ++ ds->flush_operations = stats->nr_ops[BLOCK_ACCT_FLUSH]; ++ ds->wr_total_time_ns = stats->total_time_ns[BLOCK_ACCT_WRITE]; ++ ds->zone_append_total_time_ns = ++ stats->total_time_ns[BLOCK_ACCT_ZONE_APPEND]; ++ ds->rd_total_time_ns = stats->total_time_ns[BLOCK_ACCT_READ]; ++ ds->flush_total_time_ns = stats->total_time_ns[BLOCK_ACCT_FLUSH]; ++ ds->unmap_total_time_ns = stats->total_time_ns[BLOCK_ACCT_UNMAP]; ++ ++ ds->has_idle_time_ns = stats->last_access_time_ns > 0; ++ if (ds->has_idle_time_ns) { ++ ds->idle_time_ns = block_acct_idle_time_ns(stats); ++ } ++ ++ ds->account_invalid = stats->account_invalid; ++ ds->account_failed = stats->account_failed; ++ ++ while ((ts = block_acct_interval_next(stats, ts))) { ++ BlockDeviceTimedStats *dev_stats = g_malloc0(sizeof(*dev_stats)); ++ ++ TimedAverage *rd = &ts->latency[BLOCK_ACCT_READ]; ++ TimedAverage *wr = &ts->latency[BLOCK_ACCT_WRITE]; ++ TimedAverage *zap = &ts->latency[BLOCK_ACCT_ZONE_APPEND]; ++ TimedAverage *fl = &ts->latency[BLOCK_ACCT_FLUSH]; ++ ++ dev_stats->interval_length = ts->interval_length; ++ ++ dev_stats->min_rd_latency_ns = timed_average_min(rd); ++ dev_stats->max_rd_latency_ns = timed_average_max(rd); ++ dev_stats->avg_rd_latency_ns = timed_average_avg(rd); ++ ++ dev_stats->min_wr_latency_ns = timed_average_min(wr); ++ dev_stats->max_wr_latency_ns = timed_average_max(wr); ++ dev_stats->avg_wr_latency_ns = timed_average_avg(wr); ++ ++ dev_stats->min_zone_append_latency_ns = timed_average_min(zap); ++ dev_stats->max_zone_append_latency_ns = timed_average_max(zap); ++ dev_stats->avg_zone_append_latency_ns = timed_average_avg(zap); ++ ++ dev_stats->min_flush_latency_ns = timed_average_min(fl); ++ dev_stats->max_flush_latency_ns = timed_average_max(fl); ++ dev_stats->avg_flush_latency_ns = timed_average_avg(fl); ++ ++ dev_stats->avg_rd_queue_depth = ++ block_acct_queue_depth(ts, BLOCK_ACCT_READ); ++ dev_stats->avg_wr_queue_depth = ++ block_acct_queue_depth(ts, BLOCK_ACCT_WRITE); ++ dev_stats->avg_zone_append_queue_depth = ++ block_acct_queue_depth(ts, BLOCK_ACCT_ZONE_APPEND); ++ ++ QAPI_LIST_PREPEND(ds->timed_stats, dev_stats); ++ } ++ ++ hgram = stats->latency_histogram; ++ ds->rd_latency_histogram ++ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_READ]); ++ ds->wr_latency_histogram ++ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_WRITE]); ++ ds->zone_append_latency_histogram ++ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_ZONE_APPEND]); ++ ds->flush_latency_histogram ++ = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_FLUSH]); ++} ++ ++static BlockStats * GRAPH_RDLOCK ++bdrv_query_bds_stats(BlockDriverState *bs, bool blk_level) ++{ ++ BdrvChild *parent_child; ++ BlockDriverState *filter_or_cow_bs; ++ BlockStats *s = NULL; ++ ++ s = g_malloc0(sizeof(*s)); ++ s->stats = g_malloc0(sizeof(*s->stats)); ++ ++ if (!bs) { ++ return s; ++ } ++ ++ /* Skip automatically inserted nodes that the user isn't aware of in ++ * a BlockBackend-level command. Stay at the exact node for a node-level ++ * command. */ ++ if (blk_level) { ++ bs = bdrv_skip_implicit_filters(bs); ++ } ++ ++ if (bdrv_get_node_name(bs)[0]) { ++ s->node_name = g_strdup(bdrv_get_node_name(bs)); ++ } ++ ++ s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset); ++ ++ s->driver_specific = bdrv_get_specific_stats(bs); ++ ++ parent_child = bdrv_primary_child(bs); ++ if (!parent_child || ++ !(parent_child->role & (BDRV_CHILD_DATA | BDRV_CHILD_FILTERED))) ++ { ++ BdrvChild *c; ++ ++ /* ++ * Look for a unique data-storing child. We do not need to look for ++ * filtered children, as there would be only one and it would have been ++ * the primary child. ++ */ ++ parent_child = NULL; ++ QLIST_FOREACH(c, &bs->children, next) { ++ if (c->role & BDRV_CHILD_DATA) { ++ if (parent_child) { ++ /* ++ * There are multiple data-storing children and we cannot ++ * choose between them. ++ */ ++ parent_child = NULL; ++ break; ++ } ++ parent_child = c; ++ } ++ } ++ } ++ if (parent_child) { ++ s->parent = bdrv_query_bds_stats(parent_child->bs, blk_level); ++ } ++ ++ filter_or_cow_bs = bdrv_filter_or_cow_bs(bs); ++ if (blk_level && filter_or_cow_bs) { ++ /* ++ * Put any filtered or COW child here (for backwards ++ * compatibility to when we put bs0->backing here, which might ++ * be either) ++ */ ++ s->backing = bdrv_query_bds_stats(filter_or_cow_bs, blk_level); ++ } ++ ++ return s; ++} ++ ++BlockInfoList *qmp_query_block(Error **errp) ++{ ++ BlockInfoList *head = NULL, **p_next = &head; ++ BlockBackend *blk; ++ Error *local_err = NULL; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { ++ BlockInfoList *info; ++ ++ if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { ++ continue; ++ } ++ ++ info = g_malloc0(sizeof(*info)); ++ bdrv_query_info(blk, &info->value, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ g_free(info); ++ qapi_free_BlockInfoList(head); ++ return NULL; ++ } ++ ++ *p_next = info; ++ p_next = &info->next; ++ } ++ ++ return head; ++} ++ ++BlockStatsList *qmp_query_blockstats(bool has_query_nodes, ++ bool query_nodes, ++ Error **errp) ++{ ++ BlockStatsList *head = NULL, **tail = &head; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* Just to be safe if query_nodes is not always initialized */ ++ if (has_query_nodes && query_nodes) { ++ for (bs = bdrv_next_node(NULL); bs; bs = bdrv_next_node(bs)) { ++ QAPI_LIST_APPEND(tail, bdrv_query_bds_stats(bs, false)); ++ } ++ } else { ++ for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { ++ BlockStats *s; ++ char *qdev; ++ ++ if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { ++ continue; ++ } ++ ++ s = bdrv_query_bds_stats(blk_bs(blk), true); ++ s->device = g_strdup(blk_name(blk)); ++ ++ qdev = blk_get_attached_dev_id(blk); ++ if (qdev && *qdev) { ++ s->qdev = qdev; ++ } else { ++ g_free(qdev); ++ } ++ ++ bdrv_query_blk_stats(s->stats, blk); ++ ++ QAPI_LIST_APPEND(tail, s); ++ } ++ } ++ ++ return head; ++} ++ ++void bdrv_snapshot_dump(QEMUSnapshotInfo *sn) ++{ ++ char clock_buf[128]; ++ char icount_buf[128] = {0}; ++ int64_t secs; ++ char *sizing = NULL; ++ ++ if (!sn) { ++ qemu_printf("%-7s %-16s %8s %19s %15s %10s", ++ "ID", "TAG", "VM_SIZE", "DATE", "VM_CLOCK", "ICOUNT"); ++ } else { ++ g_autoptr(GDateTime) date = g_date_time_new_from_unix_local(sn->date_sec); ++ g_autofree char *date_buf = g_date_time_format(date, "%Y-%m-%d %H:%M:%S"); ++ ++ secs = sn->vm_clock_nsec / 1000000000; ++ snprintf(clock_buf, sizeof(clock_buf), ++ "%04d:%02d:%02d.%03d", ++ (int)(secs / 3600), ++ (int)((secs / 60) % 60), ++ (int)(secs % 60), ++ (int)((sn->vm_clock_nsec / 1000000) % 1000)); ++ sizing = size_to_str(sn->vm_state_size); ++ if (sn->icount != -1ULL) { ++ snprintf(icount_buf, sizeof(icount_buf), ++ "%"PRId64, sn->icount); ++ } else { ++ snprintf(icount_buf, sizeof(icount_buf), "--"); ++ } ++ qemu_printf("%-7s %-16s %8s %19s %15s %10s", ++ sn->id_str, sn->name, ++ sizing, ++ date_buf, ++ clock_buf, ++ icount_buf); ++ } ++ g_free(sizing); ++} ++ ++static void dump_qdict(int indentation, QDict *dict); ++static void dump_qlist(int indentation, QList *list); ++ ++static void dump_qobject(int comp_indent, QObject *obj) ++{ ++ switch (qobject_type(obj)) { ++ case QTYPE_QNUM: { ++ QNum *value = qobject_to(QNum, obj); ++ char *tmp = qnum_to_string(value); ++ qemu_printf("%s", tmp); ++ g_free(tmp); ++ break; ++ } ++ case QTYPE_QSTRING: { ++ QString *value = qobject_to(QString, obj); ++ qemu_printf("%s", qstring_get_str(value)); ++ break; ++ } ++ case QTYPE_QDICT: { ++ QDict *value = qobject_to(QDict, obj); ++ dump_qdict(comp_indent, value); ++ break; ++ } ++ case QTYPE_QLIST: { ++ QList *value = qobject_to(QList, obj); ++ dump_qlist(comp_indent, value); ++ break; ++ } ++ case QTYPE_QBOOL: { ++ QBool *value = qobject_to(QBool, obj); ++ qemu_printf("%s", qbool_get_bool(value) ? "true" : "false"); ++ break; ++ } ++ default: ++ abort(); ++ } ++} ++ ++static void dump_qlist(int indentation, QList *list) ++{ ++ const QListEntry *entry; ++ int i = 0; ++ ++ for (entry = qlist_first(list); entry; entry = qlist_next(entry), i++) { ++ QType type = qobject_type(entry->value); ++ bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); ++ qemu_printf("%*s[%i]:%c", indentation * 4, "", i, ++ composite ? '\n' : ' '); ++ dump_qobject(indentation + 1, entry->value); ++ if (!composite) { ++ qemu_printf("\n"); ++ } ++ } ++} ++ ++static void dump_qdict(int indentation, QDict *dict) ++{ ++ const QDictEntry *entry; ++ ++ for (entry = qdict_first(dict); entry; entry = qdict_next(dict, entry)) { ++ QType type = qobject_type(entry->value); ++ bool composite = (type == QTYPE_QDICT || type == QTYPE_QLIST); ++ char *key = g_malloc(strlen(entry->key) + 1); ++ int i; ++ ++ /* replace dashes with spaces in key (variable) names */ ++ for (i = 0; entry->key[i]; i++) { ++ key[i] = entry->key[i] == '-' ? ' ' : entry->key[i]; ++ } ++ key[i] = 0; ++ qemu_printf("%*s%s:%c", indentation * 4, "", key, ++ composite ? '\n' : ' '); ++ dump_qobject(indentation + 1, entry->value); ++ if (!composite) { ++ qemu_printf("\n"); ++ } ++ g_free(key); ++ } ++} ++ ++/* ++ * Return whether dumping the given QObject with dump_qobject() would ++ * yield an empty dump, i.e. not print anything. ++ */ ++static bool qobject_is_empty_dump(const QObject *obj) ++{ ++ switch (qobject_type(obj)) { ++ case QTYPE_QNUM: ++ case QTYPE_QSTRING: ++ case QTYPE_QBOOL: ++ return false; ++ ++ case QTYPE_QDICT: ++ return qdict_size(qobject_to(QDict, obj)) == 0; ++ ++ case QTYPE_QLIST: ++ return qlist_empty(qobject_to(QList, obj)); ++ ++ default: ++ abort(); ++ } ++} ++ ++/** ++ * Dumps the given ImageInfoSpecific object in a human-readable form, ++ * prepending an optional prefix if the dump is not empty. ++ */ ++void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec, ++ const char *prefix, ++ int indentation) ++{ ++ QObject *obj, *data; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort); ++ visit_complete(v, &obj); ++ data = qdict_get(qobject_to(QDict, obj), "data"); ++ if (!qobject_is_empty_dump(data)) { ++ if (prefix) { ++ qemu_printf("%*s%s", indentation * 4, "", prefix); ++ } ++ dump_qobject(indentation + 1, data); ++ } ++ qobject_unref(obj); ++ visit_free(v); ++} ++ ++/** ++ * Print the given @info object in human-readable form. Every field is indented ++ * using the given @indentation (four spaces per indentation level). ++ * ++ * When using this to print a whole block graph, @protocol can be set to true to ++ * signify that the given information is associated with a protocol node, i.e. ++ * just data storage for an image, such that the data it presents is not really ++ * a full VM disk. If so, several fields change name: For example, "virtual ++ * size" is printed as "file length". ++ * (Consider a qcow2 image, which is represented by a qcow2 node and a file ++ * node. Printing a "virtual size" for the file node does not make sense, ++ * because without the qcow2 node, it is not really a guest disk, so it does not ++ * have a "virtual size". Therefore, we call it "file length" instead.) ++ * ++ * @protocol is ignored when @indentation is 0, because we take that to mean ++ * that the associated node is the root node in the queried block graph, and ++ * thus is always to be interpreted as a standalone guest disk. ++ */ ++void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol) ++{ ++ char *size_buf, *dsize_buf; ++ g_autofree char *ind_s = g_strdup_printf("%*s", indentation * 4, ""); ++ ++ if (indentation == 0) { ++ /* Top level, consider this a normal image */ ++ protocol = false; ++ } ++ ++ if (!info->has_actual_size) { ++ dsize_buf = g_strdup("unavailable"); ++ } else { ++ dsize_buf = size_to_str(info->actual_size); ++ } ++ size_buf = size_to_str(info->virtual_size); ++ qemu_printf("%s%s: %s\n" ++ "%s%s: %s\n" ++ "%s%s: %s (%" PRId64 " bytes)\n" ++ "%sdisk size: %s\n", ++ ind_s, protocol ? "filename" : "image", info->filename, ++ ind_s, protocol ? "protocol type" : "file format", ++ info->format, ++ ind_s, protocol ? "file length" : "virtual size", ++ size_buf, info->virtual_size, ++ ind_s, dsize_buf); ++ g_free(size_buf); ++ g_free(dsize_buf); ++ ++ if (info->has_encrypted && info->encrypted) { ++ qemu_printf("%sencrypted: yes\n", ind_s); ++ } ++ ++ if (info->has_cluster_size) { ++ qemu_printf("%scluster_size: %" PRId64 "\n", ++ ind_s, info->cluster_size); ++ } ++ ++ if (info->has_dirty_flag && info->dirty_flag) { ++ qemu_printf("%scleanly shut down: no\n", ind_s); ++ } ++ ++ if (info->backing_filename) { ++ qemu_printf("%sbacking file: %s", ind_s, info->backing_filename); ++ if (!info->full_backing_filename) { ++ qemu_printf(" (cannot determine actual path)"); ++ } else if (strcmp(info->backing_filename, ++ info->full_backing_filename) != 0) { ++ qemu_printf(" (actual path: %s)", info->full_backing_filename); ++ } ++ qemu_printf("\n"); ++ if (info->backing_filename_format) { ++ qemu_printf("%sbacking file format: %s\n", ++ ind_s, info->backing_filename_format); ++ } ++ } ++ ++ if (info->has_snapshots) { ++ SnapshotInfoList *elem; ++ ++ qemu_printf("%sSnapshot list:\n", ind_s); ++ qemu_printf("%s", ind_s); ++ bdrv_snapshot_dump(NULL); ++ qemu_printf("\n"); ++ ++ /* Ideally bdrv_snapshot_dump() would operate on SnapshotInfoList but ++ * we convert to the block layer's native QEMUSnapshotInfo for now. ++ */ ++ for (elem = info->snapshots; elem; elem = elem->next) { ++ QEMUSnapshotInfo sn = { ++ .vm_state_size = elem->value->vm_state_size, ++ .date_sec = elem->value->date_sec, ++ .date_nsec = elem->value->date_nsec, ++ .vm_clock_nsec = elem->value->vm_clock_sec * 1000000000ULL + ++ elem->value->vm_clock_nsec, ++ .icount = elem->value->has_icount ? ++ elem->value->icount : -1ULL, ++ }; ++ ++ pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id); ++ pstrcpy(sn.name, sizeof(sn.name), elem->value->name); ++ qemu_printf("%s", ind_s); ++ bdrv_snapshot_dump(&sn); ++ qemu_printf("\n"); ++ } ++ } ++ ++ if (info->format_specific) { ++ bdrv_image_info_specific_dump(info->format_specific, ++ "Format specific information:\n", ++ indentation); ++ } ++} +diff --git a/qcow2/lib/block/raw-format.c b/qcow2/lib/block/raw-format.c +new file mode 100644 +index 00000000..ac7e8495 +--- /dev/null ++++ b/qcow2/lib/block/raw-format.c +@@ -0,0 +1,682 @@ ++/* BlockDriver implementation for "raw" format driver ++ * ++ * Copyright (C) 2010-2016 Red Hat, Inc. ++ * Copyright (C) 2010, Blue Swirl ++ * Copyright (C) 2009, Anthony Liguori ++ * ++ * Author: ++ * Laszlo Ersek ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to ++ * deal in the Software without restriction, including without limitation the ++ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block-io.h" ++#include "block/block_int.h" ++#include "qapi/error.h" ++#include "qemu/module.h" ++#include "qemu/option.h" ++#include "qemu/memalign.h" ++ ++typedef struct BDRVRawState { ++ uint64_t offset; ++ uint64_t size; ++ bool has_size; ++} BDRVRawState; ++ ++static const char *const mutable_opts[] = { "offset", "size", NULL }; ++ ++static QemuOptsList raw_runtime_opts = { ++ .name = "raw", ++ .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), ++ .desc = { ++ { ++ .name = "offset", ++ .type = QEMU_OPT_SIZE, ++ .help = "offset in the disk where the image starts", ++ }, ++ { ++ .name = "size", ++ .type = QEMU_OPT_SIZE, ++ .help = "virtual disk size", ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++static QemuOptsList raw_create_opts = { ++ .name = "raw-create-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head), ++ .desc = { ++ { ++ .name = BLOCK_OPT_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Virtual disk size" ++ }, ++ { /* end of list */ } ++ } ++}; ++ ++static int raw_read_options(QDict *options, uint64_t *offset, bool *has_size, ++ uint64_t *size, Error **errp) ++{ ++ QemuOpts *opts = NULL; ++ int ret; ++ ++ opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ ret = -EINVAL; ++ goto end; ++ } ++ ++ *offset = qemu_opt_get_size(opts, "offset", 0); ++ *has_size = qemu_opt_find(opts, "size"); ++ *size = qemu_opt_get_size(opts, "size", 0); ++ ++ ret = 0; ++end: ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++static int GRAPH_RDLOCK ++raw_apply_options(BlockDriverState *bs, BDRVRawState *s, uint64_t offset, ++ bool has_size, uint64_t size, Error **errp) ++{ ++ int64_t real_size = 0; ++ ++ real_size = bdrv_getlength(bs->file->bs); ++ if (real_size < 0) { ++ error_setg_errno(errp, -real_size, "Could not get image size"); ++ return real_size; ++ } ++ ++ /* Check size and offset */ ++ if (offset > real_size) { ++ error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than " ++ "size of the containing file (%" PRId64 ")", ++ s->offset, real_size); ++ return -EINVAL; ++ } ++ ++ if (has_size && (real_size - offset) < size) { ++ error_setg(errp, "The sum of offset (%" PRIu64 ") and size " ++ "(%" PRIu64 ") has to be smaller or equal to the " ++ " actual size of the containing file (%" PRId64 ")", ++ s->offset, s->size, real_size); ++ return -EINVAL; ++ } ++ ++ /* Make sure size is multiple of BDRV_SECTOR_SIZE to prevent rounding ++ * up and leaking out of the specified area. */ ++ if (has_size && !QEMU_IS_ALIGNED(size, BDRV_SECTOR_SIZE)) { ++ error_setg(errp, "Specified size is not multiple of %llu", ++ BDRV_SECTOR_SIZE); ++ return -EINVAL; ++ } ++ ++ s->offset = offset; ++ s->has_size = has_size; ++ s->size = has_size ? size : real_size - offset; ++ ++ return 0; ++} ++ ++static int raw_reopen_prepare(BDRVReopenState *reopen_state, ++ BlockReopenQueue *queue, Error **errp) ++{ ++ bool has_size; ++ uint64_t offset, size; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ assert(reopen_state != NULL); ++ assert(reopen_state->bs != NULL); ++ ++ reopen_state->opaque = g_new0(BDRVRawState, 1); ++ ++ ret = raw_read_options(reopen_state->options, &offset, &has_size, &size, ++ errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = raw_apply_options(reopen_state->bs, reopen_state->opaque, ++ offset, has_size, size, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void raw_reopen_commit(BDRVReopenState *state) ++{ ++ BDRVRawState *new_s = state->opaque; ++ BDRVRawState *s = state->bs->opaque; ++ ++ memcpy(s, new_s, sizeof(BDRVRawState)); ++ ++ g_free(state->opaque); ++ state->opaque = NULL; ++} ++ ++static void raw_reopen_abort(BDRVReopenState *state) ++{ ++ g_free(state->opaque); ++ state->opaque = NULL; ++} ++ ++/* Check and adjust the offset, against 'offset' and 'size' options. */ ++static inline int raw_adjust_offset(BlockDriverState *bs, int64_t *offset, ++ int64_t bytes, bool is_write) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) { ++ /* There's not enough space for the write, or the read request is ++ * out-of-range. Don't read/write anything to prevent leaking out of ++ * the size specified in options. */ ++ return is_write ? -ENOSPC : -EINVAL; ++ } ++ ++ if (*offset > INT64_MAX - s->offset) { ++ return -EINVAL; ++ } ++ *offset += s->offset; ++ ++ return 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &offset, bytes, false); ++ if (ret) { ++ return ret; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); ++ return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, BdrvRequestFlags flags) ++{ ++ void *buf = NULL; ++ BlockDriver *drv; ++ QEMUIOVector local_qiov; ++ int ret; ++ ++ if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) { ++ /* Handling partial writes would be a pain - so we just ++ * require that guests have 512-byte request alignment if ++ * probing occurred */ ++ QEMU_BUILD_BUG_ON(BLOCK_PROBE_BUF_SIZE != 512); ++ QEMU_BUILD_BUG_ON(BDRV_SECTOR_SIZE != 512); ++ assert(offset == 0 && bytes >= BLOCK_PROBE_BUF_SIZE); ++ ++ buf = qemu_try_blockalign(bs->file->bs, 512); ++ if (!buf) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ ret = qemu_iovec_to_buf(qiov, 0, buf, 512); ++ if (ret != 512) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ drv = bdrv_probe_all(buf, 512, NULL); ++ if (drv != bs->drv) { ++ ret = -EPERM; ++ goto fail; ++ } ++ ++ /* Use the checked buffer, a malicious guest might be overwriting its ++ * original buffer in the background. */ ++ qemu_iovec_init(&local_qiov, qiov->niov + 1); ++ qemu_iovec_add(&local_qiov, buf, 512); ++ qemu_iovec_concat(&local_qiov, qiov, 512, qiov->size - 512); ++ qiov = &local_qiov; ++ ++ flags &= ~BDRV_REQ_REGISTERED_BUF; ++ } ++ ++ ret = raw_adjust_offset(bs, &offset, bytes, true); ++ if (ret) { ++ goto fail; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ++ ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); ++ ++fail: ++ if (qiov == &local_qiov) { ++ qemu_iovec_destroy(&local_qiov); ++ } ++ qemu_vfree(buf); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, ++ int64_t bytes, int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ BDRVRawState *s = bs->opaque; ++ *pnum = bytes; ++ *file = bs->file->bs; ++ *map = offset + s->offset; ++ return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &offset, bytes, true); ++ if (ret) { ++ return ret; ++ } ++ return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &offset, bytes, true); ++ if (ret) { ++ return ret; ++ } ++ return bdrv_co_pdiscard(bs->file, offset, bytes); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_zone_report(BlockDriverState *bs, int64_t offset, ++ unsigned int *nr_zones, ++ BlockZoneDescriptor *zones) ++{ ++ return bdrv_co_zone_report(bs->file->bs, offset, nr_zones, zones); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, ++ int64_t offset, int64_t len) ++{ ++ return bdrv_co_zone_mgmt(bs->file->bs, op, offset, len); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_zone_append(BlockDriverState *bs,int64_t *offset, QEMUIOVector *qiov, ++ BdrvRequestFlags flags) ++{ ++ return bdrv_co_zone_append(bs->file->bs, offset, qiov, flags); ++} ++ ++static int64_t coroutine_fn GRAPH_RDLOCK ++raw_co_getlength(BlockDriverState *bs) ++{ ++ int64_t len; ++ BDRVRawState *s = bs->opaque; ++ ++ /* Update size. It should not change unless the file was externally ++ * modified. */ ++ len = bdrv_co_getlength(bs->file->bs); ++ if (len < 0) { ++ return len; ++ } ++ ++ if (len < s->offset) { ++ s->size = 0; ++ } else { ++ if (s->has_size) { ++ /* Try to honour the size */ ++ s->size = MIN(s->size, len - s->offset); ++ } else { ++ s->size = len - s->offset; ++ } ++ } ++ ++ return s->size; ++} ++ ++static BlockMeasureInfo *raw_measure(QemuOpts *opts, BlockDriverState *in_bs, ++ Error **errp) ++{ ++ BlockMeasureInfo *info; ++ int64_t required; ++ ++ if (in_bs) { ++ required = bdrv_getlength(in_bs); ++ if (required < 0) { ++ error_setg_errno(errp, -required, "Unable to get image size"); ++ return NULL; ++ } ++ } else { ++ required = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), ++ BDRV_SECTOR_SIZE); ++ } ++ ++ info = g_new0(BlockMeasureInfo, 1); ++ info->required = required; ++ ++ /* Unallocated sectors count towards the file size in raw images */ ++ info->fully_allocated = info->required; ++ return info; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ return bdrv_co_get_info(bs->file->bs, bdi); ++} ++ ++static void GRAPH_RDLOCK raw_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ bs->bl.has_variable_length = bs->file->bs->bl.has_variable_length; ++ ++ if (bs->probed) { ++ /* To make it easier to protect the first sector, any probed ++ * image is restricted to read-modify-write on sub-sector ++ * operations. */ ++ bs->bl.request_alignment = BDRV_SECTOR_SIZE; ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ ++ if (s->has_size) { ++ error_setg(errp, "Cannot resize fixed-size raw disks"); ++ return -ENOTSUP; ++ } ++ ++ if (INT64_MAX - offset < s->offset) { ++ error_setg(errp, "Disk size too large for the chosen offset"); ++ return -EINVAL; ++ } ++ ++ s->size = offset; ++ offset += s->offset; ++ return bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp); ++} ++ ++static void coroutine_fn GRAPH_RDLOCK ++raw_co_eject(BlockDriverState *bs, bool eject_flag) ++{ ++ bdrv_co_eject(bs->file->bs, eject_flag); ++} ++ ++static void coroutine_fn GRAPH_RDLOCK ++raw_co_lock_medium(BlockDriverState *bs, bool locked) ++{ ++ bdrv_co_lock_medium(bs->file->bs, locked); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) ++{ ++ BDRVRawState *s = bs->opaque; ++ if (s->offset || s->has_size) { ++ return -ENOTSUP; ++ } ++ return bdrv_co_ioctl(bs->file->bs, req, buf); ++} ++ ++static int GRAPH_RDLOCK raw_has_zero_init(BlockDriverState *bs) ++{ ++ return bdrv_has_zero_init(bs->file->bs); ++} ++ ++static int coroutine_fn GRAPH_UNLOCKED ++raw_co_create_opts(BlockDriver *drv, const char *filename, ++ QemuOpts *opts, Error **errp) ++{ ++ return bdrv_co_create_file(filename, opts, errp); ++} ++ ++static int raw_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVRawState *s = bs->opaque; ++ bool has_size; ++ uint64_t offset, size; ++ BdrvChildRole file_role; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ ret = raw_read_options(options, &offset, &has_size, &size, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* ++ * Without offset and a size limit, this driver behaves very much ++ * like a filter. With any such limit, it does not. ++ */ ++ if (offset || has_size) { ++ file_role = BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY; ++ } else { ++ file_role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY; ++ } ++ ++ bdrv_open_child(NULL, options, "file", bs, &child_of_bds, ++ file_role, false, errp); ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ if (!bs->file) { ++ return -EINVAL; ++ } ++ ++ bs->sg = bdrv_is_sg(bs->file->bs); ++ bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | ++ (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); ++ bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | ++ ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & ++ bs->file->bs->supported_zero_flags); ++ bs->supported_truncate_flags = bs->file->bs->supported_truncate_flags & ++ BDRV_REQ_ZERO_WRITE; ++ ++ if (bs->probed && !bdrv_is_read_only(bs)) { ++ bdrv_refresh_filename(bs->file->bs); ++ fprintf(stderr, ++ "WARNING: Image format was not specified for '%s' and probing " ++ "guessed raw.\n" ++ " Automatically detecting the format is dangerous for " ++ "raw images, write operations on block 0 will be restricted.\n" ++ " Specify the 'raw' format explicitly to remove the " ++ "restrictions.\n", ++ bs->file->bs->filename); ++ } ++ ++ ret = raw_apply_options(bs, s, offset, has_size, size, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (bdrv_is_sg(bs) && (s->offset || s->has_size)) { ++ error_setg(errp, "Cannot use offset/size with SCSI generic devices"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) ++{ ++ /* smallest possible positive score so that raw is used if and only if no ++ * other block driver works ++ */ ++ return 1; ++} ++ ++static int GRAPH_RDLOCK ++raw_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz) ++{ ++ BDRVRawState *s = bs->opaque; ++ int ret; ++ ++ ret = bdrv_probe_blocksizes(bs->file->bs, bsz); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (!QEMU_IS_ALIGNED(s->offset, MAX(bsz->log, bsz->phys))) { ++ return -ENOTSUP; ++ } ++ ++ return 0; ++} ++ ++static int GRAPH_RDLOCK ++raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) ++{ ++ BDRVRawState *s = bs->opaque; ++ if (s->offset || s->has_size) { ++ return -ENOTSUP; ++ } ++ return bdrv_probe_geometry(bs->file->bs, geo); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_copy_range_from(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &src_offset, bytes, false); ++ if (ret) { ++ return ret; ++ } ++ return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset, ++ bytes, read_flags, write_flags); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++raw_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ int ret; ++ ++ ret = raw_adjust_offset(bs, &dst_offset, bytes, true); ++ if (ret) { ++ return ret; ++ } ++ return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes, ++ read_flags, write_flags); ++} ++ ++static const char *const raw_strong_runtime_opts[] = { ++ "offset", ++ "size", ++ ++ NULL ++}; ++ ++static void GRAPH_RDLOCK raw_cancel_in_flight(BlockDriverState *bs) ++{ ++ bdrv_cancel_in_flight(bs->file->bs); ++} ++ ++static void raw_child_perm(BlockDriverState *bs, BdrvChild *c, ++ BdrvChildRole role, ++ BlockReopenQueue *reopen_queue, ++ uint64_t parent_perm, uint64_t parent_shared, ++ uint64_t *nperm, uint64_t *nshared) ++{ ++ bdrv_default_perms(bs, c, role, reopen_queue, parent_perm, ++ parent_shared, nperm, nshared); ++ ++ /* ++ * bdrv_default_perms() may add WRITE and/or RESIZE (see comment in ++ * bdrv_default_perms_for_storage() for an explanation) but we only need ++ * them if they are in parent_perm. Drop WRITE and RESIZE whenever possible ++ * to avoid permission conflicts. ++ */ ++ *nperm &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE); ++ *nperm |= parent_perm & (BLK_PERM_WRITE | BLK_PERM_RESIZE); ++} ++ ++BlockDriver bdrv_raw = { ++ .format_name = "raw", ++ .instance_size = sizeof(BDRVRawState), ++ .supports_zoned_children = true, ++ .bdrv_probe = &raw_probe, ++ .bdrv_reopen_prepare = &raw_reopen_prepare, ++ .bdrv_reopen_commit = &raw_reopen_commit, ++ .bdrv_reopen_abort = &raw_reopen_abort, ++ .bdrv_open = &raw_open, ++ .bdrv_child_perm = raw_child_perm, ++ .bdrv_co_create_opts = &raw_co_create_opts, ++ .bdrv_co_preadv = &raw_co_preadv, ++ .bdrv_co_pwritev = &raw_co_pwritev, ++ .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, ++ .bdrv_co_pdiscard = &raw_co_pdiscard, ++ .bdrv_co_zone_report = &raw_co_zone_report, ++ .bdrv_co_zone_mgmt = &raw_co_zone_mgmt, ++ .bdrv_co_zone_append = &raw_co_zone_append, ++ .bdrv_co_block_status = &raw_co_block_status, ++ .bdrv_co_copy_range_from = &raw_co_copy_range_from, ++ .bdrv_co_copy_range_to = &raw_co_copy_range_to, ++ .bdrv_co_truncate = &raw_co_truncate, ++ .bdrv_co_getlength = &raw_co_getlength, ++ .is_format = true, ++ .bdrv_measure = &raw_measure, ++ .bdrv_co_get_info = &raw_co_get_info, ++ .bdrv_refresh_limits = &raw_refresh_limits, ++ .bdrv_probe_blocksizes = &raw_probe_blocksizes, ++ .bdrv_probe_geometry = &raw_probe_geometry, ++ .bdrv_co_eject = &raw_co_eject, ++ .bdrv_co_lock_medium = &raw_co_lock_medium, ++ .bdrv_co_ioctl = &raw_co_ioctl, ++ .create_opts = &raw_create_opts, ++ .bdrv_has_zero_init = &raw_has_zero_init, ++ .strong_runtime_opts = raw_strong_runtime_opts, ++ .mutable_opts = mutable_opts, ++ .bdrv_cancel_in_flight = raw_cancel_in_flight, ++}; ++ ++static void bdrv_raw_init(void) ++{ ++ bdrv_register(&bdrv_raw); ++} ++ ++block_init(bdrv_raw_init); +diff --git a/qcow2/lib/block/snapshot.c b/qcow2/lib/block/snapshot.c +new file mode 100644 +index 00000000..8fd17567 +--- /dev/null ++++ b/qcow2/lib/block/snapshot.c +@@ -0,0 +1,780 @@ ++/* ++ * Block layer snapshot related functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/snapshot.h" ++#include "block/block_int.h" ++#include "block/qdict.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/option.h" ++#include "sysemu/block-backend.h" ++ ++QemuOptsList internal_snapshot_opts = { ++ .name = "snapshot", ++ .head = QTAILQ_HEAD_INITIALIZER(internal_snapshot_opts.head), ++ .desc = { ++ { ++ .name = SNAPSHOT_OPT_ID, ++ .type = QEMU_OPT_STRING, ++ .help = "snapshot id" ++ },{ ++ .name = SNAPSHOT_OPT_NAME, ++ .type = QEMU_OPT_STRING, ++ .help = "snapshot name" ++ },{ ++ /* end of list */ ++ } ++ }, ++}; ++ ++int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, ++ const char *name) ++{ ++ QEMUSnapshotInfo *sn_tab, *sn; ++ int nb_sns, i, ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ ret = -ENOENT; ++ nb_sns = bdrv_snapshot_list(bs, &sn_tab); ++ if (nb_sns < 0) { ++ return ret; ++ } ++ for (i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->name, name)) { ++ *sn_info = *sn; ++ ret = 0; ++ break; ++ } ++ } ++ g_free(sn_tab); ++ return ret; ++} ++ ++/** ++ * Look up an internal snapshot by @id and @name. ++ * @bs: block device to search ++ * @id: unique snapshot ID, or NULL ++ * @name: snapshot name, or NULL ++ * @sn_info: location to store information on the snapshot found ++ * @errp: location to store error, will be set only for exception ++ * ++ * This function will traverse snapshot list in @bs to search the matching ++ * one, @id and @name are the matching condition: ++ * If both @id and @name are specified, find the first one with id @id and ++ * name @name. ++ * If only @id is specified, find the first one with id @id. ++ * If only @name is specified, find the first one with name @name. ++ * if none is specified, abort(). ++ * ++ * Returns: true when a snapshot is found and @sn_info will be filled, false ++ * when error or not found. If all operation succeed but no matching one is ++ * found, @errp will NOT be set. ++ */ ++bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs, ++ const char *id, ++ const char *name, ++ QEMUSnapshotInfo *sn_info, ++ Error **errp) ++{ ++ QEMUSnapshotInfo *sn_tab, *sn; ++ int nb_sns, i; ++ bool ret = false; ++ ++ assert(id || name); ++ GLOBAL_STATE_CODE(); ++ ++ nb_sns = bdrv_snapshot_list(bs, &sn_tab); ++ if (nb_sns < 0) { ++ error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list"); ++ return false; ++ } else if (nb_sns == 0) { ++ return false; ++ } ++ ++ if (id && name) { ++ for (i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) { ++ *sn_info = *sn; ++ ret = true; ++ break; ++ } ++ } ++ } else if (id) { ++ for (i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->id_str, id)) { ++ *sn_info = *sn; ++ ret = true; ++ break; ++ } ++ } ++ } else if (name) { ++ for (i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ if (!strcmp(sn->name, name)) { ++ *sn_info = *sn; ++ ret = true; ++ break; ++ } ++ } ++ } ++ ++ g_free(sn_tab); ++ return ret; ++} ++ ++/** ++ * 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. ++ */ ++static BdrvChild * GRAPH_RDLOCK ++bdrv_snapshot_fallback_child(BlockDriverState *bs) ++{ ++ BdrvChild *fallback = bdrv_primary_child(bs); ++ BdrvChild *child; ++ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ /* We allow fallback only to primary child */ ++ if (!fallback) { ++ return NULL; ++ } ++ ++ /* ++ * Check that there are no other children that would need to be ++ * snapshotted. If there are, it is not safe to fall back to ++ * fallback. ++ */ ++ QLIST_FOREACH(child, &bs->children, next) { ++ if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA | ++ BDRV_CHILD_FILTERED) && ++ child != fallback) ++ { ++ return NULL; ++ } ++ } ++ ++ return fallback; ++} ++ ++static BlockDriverState * GRAPH_RDLOCK ++bdrv_snapshot_fallback(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return child_bs(bdrv_snapshot_fallback_child(bs)); ++} ++ ++int bdrv_can_snapshot(BlockDriverState *bs) ++{ ++ BlockDriver *drv = bs->drv; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv || !bdrv_is_inserted(bs) || !bdrv_is_writable(bs)) { ++ return 0; ++ } ++ ++ if (!drv->bdrv_snapshot_create) { ++ BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); ++ if (fallback_bs) { ++ return bdrv_can_snapshot(fallback_bs); ++ } ++ return 0; ++ } ++ ++ return 1; ++} ++ ++int bdrv_snapshot_create(BlockDriverState *bs, ++ QEMUSnapshotInfo *sn_info) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ if (drv->bdrv_snapshot_create) { ++ return drv->bdrv_snapshot_create(bs, sn_info); ++ } ++ if (fallback_bs) { ++ return bdrv_snapshot_create(fallback_bs, sn_info); ++ } ++ return -ENOTSUP; ++} ++ ++int bdrv_snapshot_goto(BlockDriverState *bs, ++ const char *snapshot_id, ++ Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ BdrvChild *fallback; ++ int ret, open_ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ error_setg(errp, "Block driver is closed"); ++ return -ENOMEDIUM; ++ } ++ ++ if (!QLIST_EMPTY(&bs->dirty_bitmaps)) { ++ error_setg(errp, "Device has active dirty bitmaps"); ++ return -EBUSY; ++ } ++ ++ if (drv->bdrv_snapshot_goto) { ++ ret = drv->bdrv_snapshot_goto(bs, snapshot_id); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to load snapshot"); ++ } ++ return ret; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ fallback = bdrv_snapshot_fallback_child(bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (fallback) { ++ QDict *options; ++ QDict *file_options; ++ Error *local_err = NULL; ++ BlockDriverState *fallback_bs = fallback->bs; ++ char *subqdict_prefix = g_strdup_printf("%s.", fallback->name); ++ ++ options = qdict_clone_shallow(bs->options); ++ ++ /* Prevent it from getting deleted when detached from bs */ ++ bdrv_ref(fallback_bs); ++ ++ qdict_extract_subqdict(options, &file_options, subqdict_prefix); ++ qobject_unref(file_options); ++ g_free(subqdict_prefix); ++ ++ /* 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 */ ++ if (drv->bdrv_close) { ++ drv->bdrv_close(bs); ++ } ++ ++ /* .bdrv_open() will re-attach it */ ++ bdrv_graph_wrlock(); ++ bdrv_unref_child(bs, fallback); ++ bdrv_graph_wrunlock(); ++ ++ ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); ++ open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); ++ qobject_unref(options); ++ if (open_ret < 0) { ++ bdrv_unref(fallback_bs); ++ bs->drv = NULL; ++ /* A bdrv_snapshot_goto() error takes precedence */ ++ error_propagate(errp, local_err); ++ return ret < 0 ? ret : open_ret; ++ } ++ ++ /* ++ * 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. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ assert(bdrv_primary_bs(bs) == fallback_bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_unref(fallback_bs); ++ return ret; ++ } ++ ++ error_setg(errp, "Block driver does not support snapshots"); ++ return -ENOTSUP; ++} ++ ++/** ++ * Delete an internal snapshot by @snapshot_id and @name. ++ * @bs: block device used in the operation ++ * @snapshot_id: unique snapshot ID, or NULL ++ * @name: snapshot name, or NULL ++ * @errp: location to store error ++ * ++ * If both @snapshot_id and @name are specified, delete the first one with ++ * id @snapshot_id and name @name. ++ * If only @snapshot_id is specified, delete the first one with id ++ * @snapshot_id. ++ * If only @name is specified, delete the first one with name @name. ++ * if none is specified, return -EINVAL. ++ * ++ * Returns: 0 on success, -errno on failure. If @bs is not inserted, return ++ * -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs ++ * does not support internal snapshot deletion, return -ENOTSUP. If @bs does ++ * not support parameter @snapshot_id or @name, or one of them is not correctly ++ * specified, return -EINVAL. If @bs can't find one matching @id and @name, ++ * return -ENOENT. If @errp != NULL, it will always be filled with error ++ * message on failure. ++ */ ++int bdrv_snapshot_delete(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!drv) { ++ error_setg(errp, "Device '%s' has no medium", ++ bdrv_get_device_name(bs)); ++ return -ENOMEDIUM; ++ } ++ if (!snapshot_id && !name) { ++ error_setg(errp, "snapshot_id and name are both NULL"); ++ return -EINVAL; ++ } ++ ++ /* drain all pending i/o before deleting snapshot */ ++ bdrv_drained_begin(bs); ++ ++ if (drv->bdrv_snapshot_delete) { ++ ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); ++ } else if (fallback_bs) { ++ ret = bdrv_snapshot_delete(fallback_bs, snapshot_id, name, errp); ++ } else { ++ error_setg(errp, "Block format '%s' used by device '%s' " ++ "does not support internal snapshot deletion", ++ drv->format_name, bdrv_get_device_name(bs)); ++ ret = -ENOTSUP; ++ } ++ ++ bdrv_drained_end(bs); ++ return ret; ++} ++ ++int bdrv_snapshot_list(BlockDriverState *bs, ++ QEMUSnapshotInfo **psn_info) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ BlockDriver *drv = bs->drv; ++ BlockDriverState *fallback_bs = bdrv_snapshot_fallback(bs); ++ ++ if (!drv) { ++ return -ENOMEDIUM; ++ } ++ if (drv->bdrv_snapshot_list) { ++ return drv->bdrv_snapshot_list(bs, psn_info); ++ } ++ if (fallback_bs) { ++ return bdrv_snapshot_list(fallback_bs, psn_info); ++ } ++ return -ENOTSUP; ++} ++ ++/** ++ * Temporarily load an internal snapshot by @snapshot_id and @name. ++ * @bs: block device used in the operation ++ * @snapshot_id: unique snapshot ID, or NULL ++ * @name: snapshot name, or NULL ++ * @errp: location to store error ++ * ++ * If both @snapshot_id and @name are specified, load the first one with ++ * id @snapshot_id and name @name. ++ * If only @snapshot_id is specified, load the first one with id ++ * @snapshot_id. ++ * If only @name is specified, load the first one with name @name. ++ * if none is specified, return -EINVAL. ++ * ++ * Returns: 0 on success, -errno on fail. If @bs is not inserted, return ++ * -ENOMEDIUM. If @bs is not readonly, return -EINVAL. If @bs did not support ++ * internal snapshot, return -ENOTSUP. If qemu can't find a matching @id and ++ * @name, return -ENOENT. If @errp != NULL, it will always be filled on ++ * failure. ++ */ ++int bdrv_snapshot_load_tmp(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp) ++{ ++ BlockDriver *drv = bs->drv; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!drv) { ++ error_setg(errp, "Device '%s' has no medium", ++ bdrv_get_device_name(bs)); ++ return -ENOMEDIUM; ++ } ++ if (!snapshot_id && !name) { ++ error_setg(errp, "snapshot_id and name are both NULL"); ++ return -EINVAL; ++ } ++ if (!bdrv_is_read_only(bs)) { ++ error_setg(errp, "Device is not readonly"); ++ return -EINVAL; ++ } ++ if (drv->bdrv_snapshot_load_tmp) { ++ return drv->bdrv_snapshot_load_tmp(bs, snapshot_id, name, errp); ++ } ++ error_setg(errp, "Block format '%s' used by device '%s' " ++ "does not support temporarily loading internal snapshots", ++ drv->format_name, bdrv_get_device_name(bs)); ++ return -ENOTSUP; ++} ++ ++int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, ++ const char *id_or_name, ++ Error **errp) ++{ ++ int ret; ++ Error *local_err = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ ret = bdrv_snapshot_load_tmp(bs, id_or_name, NULL, &local_err); ++ if (ret == -ENOENT || ret == -EINVAL) { ++ error_free(local_err); ++ local_err = NULL; ++ ret = bdrv_snapshot_load_tmp(bs, NULL, id_or_name, &local_err); ++ } ++ ++ error_propagate(errp, local_err); ++ ++ return ret; ++} ++ ++ ++static int GRAPH_RDLOCK ++bdrv_all_get_snapshot_devices(bool has_devices, strList *devices, ++ GList **all_bdrvs, Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ ++ if (has_devices) { ++ if (!devices) { ++ error_setg(errp, "At least one device is required for snapshot"); ++ return -1; ++ } ++ ++ while (devices) { ++ BlockDriverState *bs = bdrv_find_node(devices->value); ++ if (!bs) { ++ error_setg(errp, "No block device node '%s'", devices->value); ++ return -1; ++ } ++ bdrvs = g_list_append(bdrvs, bs); ++ devices = devices->next; ++ } ++ } else { ++ BlockDriverState *bs; ++ BdrvNextIterator it; ++ for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { ++ bdrvs = g_list_append(bdrvs, bs); ++ } ++ } ++ ++ *all_bdrvs = g_steal_pointer(&bdrvs); ++ return 0; ++} ++ ++ ++static bool GRAPH_RDLOCK bdrv_all_snapshots_includes_bs(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ assert_bdrv_graph_readable(); ++ ++ if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) { ++ return false; ++ } ++ ++ /* Include all nodes that are either in use by a BlockBackend, or that ++ * aren't attached to any node, but owned by the monitor. */ ++ return bdrv_has_blk(bs) || QLIST_EMPTY(&bs->parents); ++} ++ ++/* Group operations. All block drivers are involved. */ ++ ++bool bdrv_all_can_snapshot(bool has_devices, strList *devices, ++ Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return false; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ bool ok = true; ++ ++ if (devices || bdrv_all_snapshots_includes_bs(bs)) { ++ ok = bdrv_can_snapshot(bs); ++ } ++ if (!ok) { ++ error_setg(errp, "Device '%s' is writable but does not support " ++ "snapshots", bdrv_get_device_or_node_name(bs)); ++ return false; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return true; ++} ++ ++int bdrv_all_delete_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return -1; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ QEMUSnapshotInfo sn1, *snapshot = &sn1; ++ int ret = 0; ++ ++ if ((devices || bdrv_all_snapshots_includes_bs(bs)) && ++ bdrv_snapshot_find(bs, snapshot, name) >= 0) ++ { ++ ret = bdrv_snapshot_delete(bs, snapshot->id_str, ++ snapshot->name, errp); ++ } ++ if (ret < 0) { ++ error_prepend(errp, "Could not delete snapshot '%s' on '%s': ", ++ name, bdrv_get_device_or_node_name(bs)); ++ return -1; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return 0; ++} ++ ++ ++int bdrv_all_goto_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (ret < 0) { ++ return -1; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ bool all_snapshots_includes_bs; ++ ++ bdrv_graph_rdlock_main_loop(); ++ all_snapshots_includes_bs = bdrv_all_snapshots_includes_bs(bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ ret = (devices || all_snapshots_includes_bs) ? ++ bdrv_snapshot_goto(bs, name, errp) : 0; ++ if (ret < 0) { ++ bdrv_graph_rdlock_main_loop(); ++ error_prepend(errp, "Could not load snapshot '%s' on '%s': ", ++ name, bdrv_get_device_or_node_name(bs)); ++ bdrv_graph_rdunlock_main_loop(); ++ return -1; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return 0; ++} ++ ++int bdrv_all_has_snapshot(const char *name, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return -1; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ QEMUSnapshotInfo sn; ++ int ret = 0; ++ ++ if (devices || bdrv_all_snapshots_includes_bs(bs)) { ++ ret = bdrv_snapshot_find(bs, &sn, name); ++ } ++ if (ret < 0) { ++ if (ret == -ENOENT) { ++ return 0; ++ } else { ++ error_setg_errno(errp, errno, ++ "Could not check snapshot '%s' on '%s'", ++ name, bdrv_get_device_or_node_name(bs)); ++ return -1; ++ } ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return 1; ++} ++ ++int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn, ++ BlockDriverState *vm_state_bs, ++ uint64_t vm_state_size, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return -1; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ int ret = 0; ++ ++ if (bs == vm_state_bs) { ++ sn->vm_state_size = vm_state_size; ++ ret = bdrv_snapshot_create(bs, sn); ++ } else if (devices || bdrv_all_snapshots_includes_bs(bs)) { ++ sn->vm_state_size = 0; ++ ret = bdrv_snapshot_create(bs, sn); ++ } ++ if (ret < 0) { ++ error_setg(errp, "Could not create snapshot '%s' on '%s'", ++ sn->name, bdrv_get_device_or_node_name(bs)); ++ return -1; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ return 0; ++} ++ ++ ++BlockDriverState *bdrv_all_find_vmstate_bs(const char *vmstate_bs, ++ bool has_devices, strList *devices, ++ Error **errp) ++{ ++ g_autoptr(GList) bdrvs = NULL; ++ GList *iterbdrvs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { ++ return NULL; ++ } ++ ++ iterbdrvs = bdrvs; ++ while (iterbdrvs) { ++ BlockDriverState *bs = iterbdrvs->data; ++ bool found = false; ++ ++ found = (devices || bdrv_all_snapshots_includes_bs(bs)) && ++ bdrv_can_snapshot(bs); ++ ++ if (vmstate_bs) { ++ if (g_str_equal(vmstate_bs, ++ bdrv_get_node_name(bs))) { ++ if (found) { ++ return bs; ++ } else { ++ error_setg(errp, ++ "vmstate block device '%s' does not support snapshots", ++ vmstate_bs); ++ return NULL; ++ } ++ } ++ } else if (found) { ++ return bs; ++ } ++ ++ iterbdrvs = iterbdrvs->next; ++ } ++ ++ if (vmstate_bs) { ++ error_setg(errp, ++ "vmstate block device '%s' does not exist", vmstate_bs); ++ } else { ++ error_setg(errp, ++ "no block device can store vmstate for snapshot"); ++ } ++ return NULL; ++} +diff --git a/qcow2/lib/blockdev.c b/qcow2/lib/blockdev.c +new file mode 100644 +index 00000000..835064ed +--- /dev/null ++++ b/qcow2/lib/blockdev.c +@@ -0,0 +1,3649 @@ ++/* ++ * QEMU host block devices ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ * ++ * This file incorporates work covered by the following copyright and ++ * permission notice: ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "sysemu/block-backend.h" ++#include "sysemu/blockdev.h" ++#include "hw/block/block.h" ++#include "block/blockjob.h" ++#include "block/dirty-bitmap.h" ++#include "block/qdict.h" ++#include "block/throttle-groups.h" ++#include "monitor/monitor.h" ++#include "qemu/error-report.h" ++#include "qemu/option.h" ++#include "qemu/qemu-print.h" ++#include "qemu/config-file.h" ++#include "qapi/qapi-commands-block.h" ++#include "qapi/qapi-commands-transaction.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qobject-output-visitor.h" ++#include "sysemu/sysemu.h" ++#include "sysemu/iothread.h" ++#include "block/block_int.h" ++#include "block/trace.h" ++#include "sysemu/runstate.h" ++#include "sysemu/replay.h" ++#include "qemu/cutils.h" ++#include "qemu/help_option.h" ++#include "qemu/main-loop.h" ++#include "qemu/throttle-options.h" ++ ++/* Protected by BQL */ ++QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = ++ QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); ++ ++void bdrv_set_monitor_owned(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ QTAILQ_INSERT_TAIL(&monitor_bdrv_states, bs, monitor_list); ++} ++ ++static const char *const if_name[IF_COUNT] = { ++ [IF_NONE] = "none", ++ [IF_IDE] = "ide", ++ [IF_SCSI] = "scsi", ++ [IF_FLOPPY] = "floppy", ++ [IF_PFLASH] = "pflash", ++ [IF_MTD] = "mtd", ++ [IF_SD] = "sd", ++ [IF_VIRTIO] = "virtio", ++ [IF_XEN] = "xen", ++}; ++ ++static int if_max_devs[IF_COUNT] = { ++ /* ++ * Do not change these numbers! They govern how drive option ++ * index maps to unit and bus. That mapping is ABI. ++ * ++ * All controllers used to implement if=T drives need to support ++ * if_max_devs[T] units, for any T with if_max_devs[T] != 0. ++ * Otherwise, some index values map to "impossible" bus, unit ++ * values. ++ * ++ * For instance, if you change [IF_SCSI] to 255, -drive ++ * if=scsi,index=12 no longer means bus=1,unit=5, but ++ * bus=0,unit=12. With an lsi53c895a controller (7 units max), ++ * the drive can't be set up. Regression. ++ */ ++ [IF_IDE] = 2, ++ [IF_SCSI] = 7, ++}; ++ ++/** ++ * Boards may call this to offer board-by-board overrides ++ * of the default, global values. ++ */ ++void override_max_devs(BlockInterfaceType type, int max_devs) ++{ ++ BlockBackend *blk; ++ DriveInfo *dinfo; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (max_devs <= 0) { ++ return; ++ } ++ ++ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ dinfo = blk_legacy_dinfo(blk); ++ if (dinfo->type == type) { ++ fprintf(stderr, "Cannot override units-per-bus property of" ++ " the %s interface, because a drive of that type has" ++ " already been added.\n", if_name[type]); ++ g_assert_not_reached(); ++ } ++ } ++ ++ if_max_devs[type] = max_devs; ++} ++ ++/* ++ * We automatically delete the drive when a device using it gets ++ * unplugged. Questionable feature, but we can't just drop it. ++ * Device models call blockdev_mark_auto_del() to schedule the ++ * automatic deletion, and generic qdev code calls blockdev_auto_del() ++ * when deletion is actually safe. ++ */ ++void blockdev_mark_auto_del(BlockBackend *blk) ++{ ++ DriveInfo *dinfo = blk_legacy_dinfo(blk); ++ BlockJob *job; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (!dinfo) { ++ return; ++ } ++ ++ JOB_LOCK_GUARD(); ++ ++ do { ++ job = block_job_next_locked(NULL); ++ while (job && (job->job.cancelled || ++ job->job.deferred_to_main_loop || ++ !block_job_has_bdrv(job, blk_bs(blk)))) ++ { ++ job = block_job_next_locked(job); ++ } ++ if (job) { ++ /* ++ * This drops the job lock temporarily and polls, so we need to ++ * restart processing the list from the start after this. ++ */ ++ job_cancel_locked(&job->job, false); ++ } ++ } while (job); ++ ++ dinfo->auto_del = 1; ++} ++ ++void blockdev_auto_del(BlockBackend *blk) ++{ ++ DriveInfo *dinfo = blk_legacy_dinfo(blk); ++ GLOBAL_STATE_CODE(); ++ ++ if (dinfo && dinfo->auto_del) { ++ monitor_remove_blk(blk); ++ blk_unref(blk); ++ } ++} ++ ++static int drive_index_to_bus_id(BlockInterfaceType type, int index) ++{ ++ int max_devs = if_max_devs[type]; ++ return max_devs ? index / max_devs : 0; ++} ++ ++static int drive_index_to_unit_id(BlockInterfaceType type, int index) ++{ ++ int max_devs = if_max_devs[type]; ++ return max_devs ? index % max_devs : index; ++} ++ ++QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, ++ const char *optstr) ++{ ++ QemuOpts *opts; ++ ++ GLOBAL_STATE_CODE(); ++ ++ opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false); ++ if (!opts) { ++ return NULL; ++ } ++ if (type != IF_DEFAULT) { ++ qemu_opt_set(opts, "if", if_name[type], &error_abort); ++ } ++ if (index >= 0) { ++ qemu_opt_set_number(opts, "index", index, &error_abort); ++ } ++ if (file) ++ qemu_opt_set(opts, "file", file, &error_abort); ++ return opts; ++} ++ ++DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) ++{ ++ BlockBackend *blk; ++ DriveInfo *dinfo; ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ dinfo = blk_legacy_dinfo(blk); ++ if (dinfo && dinfo->type == type ++ && dinfo->bus == bus && dinfo->unit == unit) { ++ return dinfo; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Check board claimed all -drive that are meant to be claimed. ++ * Fatal error if any remain unclaimed. ++ */ ++void drive_check_orphaned(void) ++{ ++ BlockBackend *blk; ++ DriveInfo *dinfo; ++ Location loc; ++ bool orphans = false; ++ ++ GLOBAL_STATE_CODE(); ++ ++ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ dinfo = blk_legacy_dinfo(blk); ++ /* ++ * Ignore default drives, because we create certain default ++ * drives unconditionally, then leave them unclaimed. Not the ++ * users fault. ++ * Ignore IF_VIRTIO or IF_XEN, because it gets desugared into ++ * -device, so we can leave failing to -device. ++ * Ignore IF_NONE, because leaving unclaimed IF_NONE remains ++ * available for device_add is a feature. ++ */ ++ if (dinfo->is_default || dinfo->type == IF_VIRTIO ++ || dinfo->type == IF_XEN || dinfo->type == IF_NONE) { ++ continue; ++ } ++ if (!blk_get_attached_dev(blk)) { ++ loc_push_none(&loc); ++ qemu_opts_loc_restore(dinfo->opts); ++ error_report("machine type does not support" ++ " if=%s,bus=%d,unit=%d", ++ if_name[dinfo->type], dinfo->bus, dinfo->unit); ++ loc_pop(&loc); ++ orphans = true; ++ } ++ } ++ ++ if (orphans) { ++ exit(1); ++ } ++} ++ ++DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) ++{ ++ GLOBAL_STATE_CODE(); ++ return drive_get(type, ++ drive_index_to_bus_id(type, index), ++ drive_index_to_unit_id(type, index)); ++} ++ ++int drive_get_max_bus(BlockInterfaceType type) ++{ ++ int max_bus; ++ BlockBackend *blk; ++ DriveInfo *dinfo; ++ ++ GLOBAL_STATE_CODE(); ++ ++ max_bus = -1; ++ for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { ++ dinfo = blk_legacy_dinfo(blk); ++ if (dinfo && dinfo->type == type && dinfo->bus > max_bus) { ++ max_bus = dinfo->bus; ++ } ++ } ++ return max_bus; ++} ++ ++static void bdrv_format_print(void *opaque, const char *name) ++{ ++ qemu_printf(" %s", name); ++} ++ ++typedef struct { ++ QEMUBH *bh; ++ BlockDriverState *bs; ++} BDRVPutRefBH; ++ ++static int parse_block_error_action(const char *buf, bool is_read, Error **errp) ++{ ++ if (!strcmp(buf, "ignore")) { ++ return BLOCKDEV_ON_ERROR_IGNORE; ++ } else if (!is_read && !strcmp(buf, "enospc")) { ++ return BLOCKDEV_ON_ERROR_ENOSPC; ++ } else if (!strcmp(buf, "stop")) { ++ return BLOCKDEV_ON_ERROR_STOP; ++ } else if (!strcmp(buf, "report")) { ++ return BLOCKDEV_ON_ERROR_REPORT; ++ } else { ++ error_setg(errp, "'%s' invalid %s error action", ++ buf, is_read ? "read" : "write"); ++ return -1; ++ } ++} ++ ++static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, ++ Error **errp) ++{ ++ const QListEntry *entry; ++ for (entry = qlist_first(intervals); entry; entry = qlist_next(entry)) { ++ switch (qobject_type(entry->value)) { ++ ++ case QTYPE_QSTRING: { ++ uint64_t length; ++ const char *str = qstring_get_str(qobject_to(QString, ++ entry->value)); ++ if (parse_uint_full(str, 10, &length) == 0 && ++ length > 0 && length <= UINT_MAX) { ++ block_acct_add_interval(stats, (unsigned) length); ++ } else { ++ error_setg(errp, "Invalid interval length: %s", str); ++ return false; ++ } ++ break; ++ } ++ ++ case QTYPE_QNUM: { ++ int64_t length = qnum_get_int(qobject_to(QNum, entry->value)); ++ ++ if (length > 0 && length <= UINT_MAX) { ++ block_acct_add_interval(stats, (unsigned) length); ++ } else { ++ error_setg(errp, "Invalid interval length: %" PRId64, length); ++ return false; ++ } ++ break; ++ } ++ ++ default: ++ error_setg(errp, "The specification of stats-intervals is invalid"); ++ return false; ++ } ++ } ++ return true; ++} ++ ++typedef enum { MEDIA_DISK, MEDIA_CDROM } DriveMediaType; ++ ++/* All parameters but @opts are optional and may be set to NULL. */ ++static void extract_common_blockdev_options(QemuOpts *opts, int *bdrv_flags, ++ const char **throttling_group, ThrottleConfig *throttle_cfg, ++ BlockdevDetectZeroesOptions *detect_zeroes, Error **errp) ++{ ++ Error *local_error = NULL; ++ const char *aio; ++ ++ if (bdrv_flags) { ++ if (qemu_opt_get_bool(opts, "copy-on-read", false)) { ++ *bdrv_flags |= BDRV_O_COPY_ON_READ; ++ } ++ ++ if ((aio = qemu_opt_get(opts, "aio")) != NULL) { ++ if (bdrv_parse_aio(aio, bdrv_flags) < 0) { ++ error_setg(errp, "invalid aio option"); ++ return; ++ } ++ } ++ } ++ ++ /* disk I/O throttling */ ++ if (throttling_group) { ++ *throttling_group = qemu_opt_get(opts, "throttling.group"); ++ } ++ ++ if (throttle_cfg) { ++ throttle_config_init(throttle_cfg); ++ throttle_cfg->buckets[THROTTLE_BPS_TOTAL].avg = ++ qemu_opt_get_number(opts, "throttling.bps-total", 0); ++ throttle_cfg->buckets[THROTTLE_BPS_READ].avg = ++ qemu_opt_get_number(opts, "throttling.bps-read", 0); ++ throttle_cfg->buckets[THROTTLE_BPS_WRITE].avg = ++ qemu_opt_get_number(opts, "throttling.bps-write", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_TOTAL].avg = ++ qemu_opt_get_number(opts, "throttling.iops-total", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_READ].avg = ++ qemu_opt_get_number(opts, "throttling.iops-read", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_WRITE].avg = ++ qemu_opt_get_number(opts, "throttling.iops-write", 0); ++ ++ throttle_cfg->buckets[THROTTLE_BPS_TOTAL].max = ++ qemu_opt_get_number(opts, "throttling.bps-total-max", 0); ++ throttle_cfg->buckets[THROTTLE_BPS_READ].max = ++ qemu_opt_get_number(opts, "throttling.bps-read-max", 0); ++ throttle_cfg->buckets[THROTTLE_BPS_WRITE].max = ++ qemu_opt_get_number(opts, "throttling.bps-write-max", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_TOTAL].max = ++ qemu_opt_get_number(opts, "throttling.iops-total-max", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_READ].max = ++ qemu_opt_get_number(opts, "throttling.iops-read-max", 0); ++ throttle_cfg->buckets[THROTTLE_OPS_WRITE].max = ++ qemu_opt_get_number(opts, "throttling.iops-write-max", 0); ++ ++ throttle_cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = ++ qemu_opt_get_number(opts, "throttling.bps-total-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_BPS_READ].burst_length = ++ qemu_opt_get_number(opts, "throttling.bps-read-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_BPS_WRITE].burst_length = ++ qemu_opt_get_number(opts, "throttling.bps-write-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = ++ qemu_opt_get_number(opts, "throttling.iops-total-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_OPS_READ].burst_length = ++ qemu_opt_get_number(opts, "throttling.iops-read-max-length", 1); ++ throttle_cfg->buckets[THROTTLE_OPS_WRITE].burst_length = ++ qemu_opt_get_number(opts, "throttling.iops-write-max-length", 1); ++ ++ throttle_cfg->op_size = ++ qemu_opt_get_number(opts, "throttling.iops-size", 0); ++ ++ if (!throttle_is_valid(throttle_cfg, errp)) { ++ return; ++ } ++ } ++ ++ if (detect_zeroes) { ++ *detect_zeroes = ++ qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, ++ qemu_opt_get(opts, "detect-zeroes"), ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, ++ &local_error); ++ if (local_error) { ++ error_propagate(errp, local_error); ++ return; ++ } ++ } ++} ++ ++static OnOffAuto account_get_opt(QemuOpts *opts, const char *name) ++{ ++ if (!qemu_opt_find(opts, name)) { ++ return ON_OFF_AUTO_AUTO; ++ } ++ if (qemu_opt_get_bool(opts, name, true)) { ++ return ON_OFF_AUTO_ON; ++ } ++ return ON_OFF_AUTO_OFF; ++} ++ ++/* Takes the ownership of bs_opts */ ++static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, ++ Error **errp) ++{ ++ const char *buf; ++ int bdrv_flags = 0; ++ int on_read_error, on_write_error; ++ OnOffAuto account_invalid, account_failed; ++ bool writethrough, read_only; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ ThrottleConfig cfg; ++ int snapshot = 0; ++ Error *error = NULL; ++ QemuOpts *opts; ++ QDict *interval_dict = NULL; ++ QList *interval_list = NULL; ++ const char *id; ++ BlockdevDetectZeroesOptions detect_zeroes = ++ BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; ++ const char *throttling_group = NULL; ++ ++ /* Check common options by copying from bs_opts to opts, all other options ++ * stay in bs_opts for processing by bdrv_open(). */ ++ id = qdict_get_try_str(bs_opts, "id"); ++ opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, errp); ++ if (!opts) { ++ goto err_no_opts; ++ } ++ ++ if (!qemu_opts_absorb_qdict(opts, bs_opts, errp)) { ++ goto early_err; ++ } ++ ++ if (id) { ++ qdict_del(bs_opts, "id"); ++ } ++ ++ /* extract parameters */ ++ snapshot = qemu_opt_get_bool(opts, "snapshot", 0); ++ ++ account_invalid = account_get_opt(opts, "stats-account-invalid"); ++ account_failed = account_get_opt(opts, "stats-account-failed"); ++ ++ writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true); ++ ++ id = qemu_opts_id(opts); ++ ++ qdict_extract_subqdict(bs_opts, &interval_dict, "stats-intervals."); ++ qdict_array_split(interval_dict, &interval_list); ++ ++ if (qdict_size(interval_dict) != 0) { ++ error_setg(errp, "Invalid option stats-intervals.%s", ++ qdict_first(interval_dict)->key); ++ goto early_err; ++ } ++ ++ extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg, ++ &detect_zeroes, &error); ++ if (error) { ++ error_propagate(errp, error); ++ goto early_err; ++ } ++ ++ if ((buf = qemu_opt_get(opts, "format")) != NULL) { ++ if (is_help_option(buf)) { ++ qemu_printf("Supported formats:"); ++ bdrv_iterate_format(bdrv_format_print, NULL, false); ++ qemu_printf("\nSupported formats (read-only):"); ++ bdrv_iterate_format(bdrv_format_print, NULL, true); ++ qemu_printf("\n"); ++ goto early_err; ++ } ++ ++ if (qdict_haskey(bs_opts, "driver")) { ++ error_setg(errp, "Cannot specify both 'driver' and 'format'"); ++ goto early_err; ++ } ++ qdict_put_str(bs_opts, "driver", buf); ++ } ++ ++ on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; ++ if ((buf = qemu_opt_get(opts, "werror")) != NULL) { ++ on_write_error = parse_block_error_action(buf, 0, &error); ++ if (error) { ++ error_propagate(errp, error); ++ goto early_err; ++ } ++ } ++ ++ on_read_error = BLOCKDEV_ON_ERROR_REPORT; ++ if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { ++ on_read_error = parse_block_error_action(buf, 1, &error); ++ if (error) { ++ error_propagate(errp, error); ++ goto early_err; ++ } ++ } ++ ++ if (snapshot) { ++ bdrv_flags |= BDRV_O_SNAPSHOT; ++ } ++ ++ read_only = qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false); ++ ++ /* init */ ++ if ((!file || !*file) && !qdict_size(bs_opts)) { ++ BlockBackendRootState *blk_rs; ++ ++ blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL); ++ blk_rs = blk_get_root_state(blk); ++ blk_rs->open_flags = bdrv_flags | (read_only ? 0 : BDRV_O_RDWR); ++ blk_rs->detect_zeroes = detect_zeroes; ++ ++ qobject_unref(bs_opts); ++ } else { ++ if (file && !*file) { ++ file = NULL; ++ } ++ ++ /* bdrv_open() defaults to the values in bdrv_flags (for compatibility ++ * with other callers) rather than what we want as the real defaults. ++ * Apply the defaults here instead. */ ++ qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, ++ read_only ? "on" : "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_AUTO_READ_ONLY, "on"); ++ assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0); ++ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ bdrv_flags |= BDRV_O_INACTIVE; ++ } ++ ++ blk = blk_new_open(file, NULL, bs_opts, bdrv_flags, errp); ++ if (!blk) { ++ goto err_no_bs_opts; ++ } ++ bs = blk_bs(blk); ++ ++ bs->detect_zeroes = detect_zeroes; ++ ++ block_acct_setup(blk_get_stats(blk), account_invalid, account_failed); ++ ++ if (!parse_stats_intervals(blk_get_stats(blk), interval_list, errp)) { ++ blk_unref(blk); ++ blk = NULL; ++ goto err_no_bs_opts; ++ } ++ } ++ ++ /* disk I/O throttling */ ++ if (throttle_enabled(&cfg)) { ++ if (!throttling_group) { ++ throttling_group = id; ++ } ++ blk_io_limits_enable(blk, throttling_group); ++ blk_set_io_limits(blk, &cfg); ++ } ++ ++ blk_set_enable_write_cache(blk, !writethrough); ++ blk_set_on_error(blk, on_read_error, on_write_error); ++ ++ if (!monitor_add_blk(blk, id, errp)) { ++ blk_unref(blk); ++ blk = NULL; ++ goto err_no_bs_opts; ++ } ++ ++err_no_bs_opts: ++ qemu_opts_del(opts); ++ qobject_unref(interval_dict); ++ qobject_unref(interval_list); ++ return blk; ++ ++early_err: ++ qemu_opts_del(opts); ++ qobject_unref(interval_dict); ++ qobject_unref(interval_list); ++err_no_opts: ++ qobject_unref(bs_opts); ++ return NULL; ++} ++ ++/* Takes the ownership of bs_opts */ ++BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp) ++{ ++ int bdrv_flags = 0; ++ ++ GLOBAL_STATE_CODE(); ++ /* bdrv_open() defaults to the values in bdrv_flags (for compatibility ++ * with other callers) rather than what we want as the real defaults. ++ * Apply the defaults here instead. */ ++ qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_DIRECT, "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off"); ++ qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, "off"); ++ ++ if (runstate_check(RUN_STATE_INMIGRATE)) { ++ bdrv_flags |= BDRV_O_INACTIVE; ++ } ++ ++ return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp); ++} ++ ++void blockdev_close_all_bdrv_states(void) ++{ ++ BlockDriverState *bs, *next_bs; ++ ++ GLOBAL_STATE_CODE(); ++ QTAILQ_FOREACH_SAFE(bs, &monitor_bdrv_states, monitor_list, next_bs) { ++ bdrv_unref(bs); ++ } ++} ++ ++/* Iterates over the list of monitor-owned BlockDriverStates */ ++BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ return bs ? QTAILQ_NEXT(bs, monitor_list) ++ : QTAILQ_FIRST(&monitor_bdrv_states); ++} ++ ++static bool qemu_opt_rename(QemuOpts *opts, const char *from, const char *to, ++ Error **errp) ++{ ++ const char *value; ++ ++ value = qemu_opt_get(opts, from); ++ if (value) { ++ if (qemu_opt_find(opts, to)) { ++ error_setg(errp, "'%s' and its alias '%s' can't be used at the " ++ "same time", to, from); ++ return false; ++ } ++ } ++ ++ /* rename all items in opts */ ++ while ((value = qemu_opt_get(opts, from))) { ++ qemu_opt_set(opts, to, value, &error_abort); ++ qemu_opt_unset(opts, from); ++ } ++ return true; ++} ++ ++QemuOptsList qemu_legacy_drive_opts = { ++ .name = "drive", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_legacy_drive_opts.head), ++ .desc = { ++ { ++ .name = "bus", ++ .type = QEMU_OPT_NUMBER, ++ .help = "bus number", ++ },{ ++ .name = "unit", ++ .type = QEMU_OPT_NUMBER, ++ .help = "unit number (i.e. lun for scsi)", ++ },{ ++ .name = "index", ++ .type = QEMU_OPT_NUMBER, ++ .help = "index number", ++ },{ ++ .name = "media", ++ .type = QEMU_OPT_STRING, ++ .help = "media type (disk, cdrom)", ++ },{ ++ .name = "if", ++ .type = QEMU_OPT_STRING, ++ .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", ++ },{ ++ .name = "file", ++ .type = QEMU_OPT_STRING, ++ .help = "file name", ++ }, ++ ++ /* Options that are passed on, but have special semantics with -drive */ ++ { ++ .name = BDRV_OPT_READ_ONLY, ++ .type = QEMU_OPT_BOOL, ++ .help = "open drive file as read-only", ++ },{ ++ .name = "rerror", ++ .type = QEMU_OPT_STRING, ++ .help = "read error action", ++ },{ ++ .name = "werror", ++ .type = QEMU_OPT_STRING, ++ .help = "write error action", ++ },{ ++ .name = "copy-on-read", ++ .type = QEMU_OPT_BOOL, ++ .help = "copy read data from backing file into image file", ++ }, ++ ++ { /* end of list */ } ++ }, ++}; ++ ++DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, ++ Error **errp) ++{ ++ const char *value; ++ BlockBackend *blk; ++ DriveInfo *dinfo = NULL; ++ QDict *bs_opts; ++ QemuOpts *legacy_opts; ++ DriveMediaType media = MEDIA_DISK; ++ BlockInterfaceType type; ++ int max_devs, bus_id, unit_id, index; ++ const char *werror, *rerror; ++ bool read_only = false; ++ bool copy_on_read; ++ const char *filename; ++ int i; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Change legacy command line options into QMP ones */ ++ static const struct { ++ const char *from; ++ const char *to; ++ } opt_renames[] = { ++ { "iops", "throttling.iops-total" }, ++ { "iops_rd", "throttling.iops-read" }, ++ { "iops_wr", "throttling.iops-write" }, ++ ++ { "bps", "throttling.bps-total" }, ++ { "bps_rd", "throttling.bps-read" }, ++ { "bps_wr", "throttling.bps-write" }, ++ ++ { "iops_max", "throttling.iops-total-max" }, ++ { "iops_rd_max", "throttling.iops-read-max" }, ++ { "iops_wr_max", "throttling.iops-write-max" }, ++ ++ { "bps_max", "throttling.bps-total-max" }, ++ { "bps_rd_max", "throttling.bps-read-max" }, ++ { "bps_wr_max", "throttling.bps-write-max" }, ++ ++ { "iops_size", "throttling.iops-size" }, ++ ++ { "group", "throttling.group" }, ++ ++ { "readonly", BDRV_OPT_READ_ONLY }, ++ }; ++ ++ for (i = 0; i < ARRAY_SIZE(opt_renames); i++) { ++ if (!qemu_opt_rename(all_opts, opt_renames[i].from, ++ opt_renames[i].to, errp)) { ++ return NULL; ++ } ++ } ++ ++ value = qemu_opt_get(all_opts, "cache"); ++ if (value) { ++ int flags = 0; ++ bool writethrough; ++ ++ if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) { ++ error_setg(errp, "invalid cache option"); ++ return NULL; ++ } ++ ++ /* Specific options take precedence */ ++ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_WB)) { ++ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_WB, ++ !writethrough, &error_abort); ++ } ++ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_DIRECT)) { ++ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_DIRECT, ++ !!(flags & BDRV_O_NOCACHE), &error_abort); ++ } ++ if (!qemu_opt_get(all_opts, BDRV_OPT_CACHE_NO_FLUSH)) { ++ qemu_opt_set_bool(all_opts, BDRV_OPT_CACHE_NO_FLUSH, ++ !!(flags & BDRV_O_NO_FLUSH), &error_abort); ++ } ++ qemu_opt_unset(all_opts, "cache"); ++ } ++ ++ /* Get a QDict for processing the options */ ++ bs_opts = qdict_new(); ++ qemu_opts_to_qdict(all_opts, bs_opts); ++ ++ legacy_opts = qemu_opts_create(&qemu_legacy_drive_opts, NULL, 0, ++ &error_abort); ++ if (!qemu_opts_absorb_qdict(legacy_opts, bs_opts, errp)) { ++ goto fail; ++ } ++ ++ /* Media type */ ++ value = qemu_opt_get(legacy_opts, "media"); ++ if (value) { ++ if (!strcmp(value, "disk")) { ++ media = MEDIA_DISK; ++ } else if (!strcmp(value, "cdrom")) { ++ media = MEDIA_CDROM; ++ read_only = true; ++ } else { ++ error_setg(errp, "'%s' invalid media", value); ++ goto fail; ++ } ++ } ++ ++ /* copy-on-read is disabled with a warning for read-only devices */ ++ read_only |= qemu_opt_get_bool(legacy_opts, BDRV_OPT_READ_ONLY, false); ++ copy_on_read = qemu_opt_get_bool(legacy_opts, "copy-on-read", false); ++ ++ if (read_only && copy_on_read) { ++ warn_report("disabling copy-on-read on read-only drive"); ++ copy_on_read = false; ++ } ++ ++ qdict_put_str(bs_opts, BDRV_OPT_READ_ONLY, read_only ? "on" : "off"); ++ qdict_put_str(bs_opts, "copy-on-read", copy_on_read ? "on" : "off"); ++ ++ /* Controller type */ ++ value = qemu_opt_get(legacy_opts, "if"); ++ if (value) { ++ for (type = 0; ++ type < IF_COUNT && strcmp(value, if_name[type]); ++ type++) { ++ } ++ if (type == IF_COUNT) { ++ error_setg(errp, "unsupported bus type '%s'", value); ++ goto fail; ++ } ++ } else { ++ type = block_default_type; ++ } ++ ++ /* Device address specified by bus/unit or index. ++ * If none was specified, try to find the first free one. */ ++ bus_id = qemu_opt_get_number(legacy_opts, "bus", 0); ++ unit_id = qemu_opt_get_number(legacy_opts, "unit", -1); ++ index = qemu_opt_get_number(legacy_opts, "index", -1); ++ ++ max_devs = if_max_devs[type]; ++ ++ if (index != -1) { ++ if (bus_id != 0 || unit_id != -1) { ++ error_setg(errp, "index cannot be used with bus and unit"); ++ goto fail; ++ } ++ bus_id = drive_index_to_bus_id(type, index); ++ unit_id = drive_index_to_unit_id(type, index); ++ } ++ ++ if (unit_id == -1) { ++ unit_id = 0; ++ while (drive_get(type, bus_id, unit_id) != NULL) { ++ unit_id++; ++ if (max_devs && unit_id >= max_devs) { ++ unit_id -= max_devs; ++ bus_id++; ++ } ++ } ++ } ++ ++ if (max_devs && unit_id >= max_devs) { ++ error_setg(errp, "unit %d too big (max is %d)", unit_id, max_devs - 1); ++ goto fail; ++ } ++ ++ if (drive_get(type, bus_id, unit_id) != NULL) { ++ error_setg(errp, "drive with bus=%d, unit=%d (index=%d) exists", ++ bus_id, unit_id, index); ++ goto fail; ++ } ++ ++ /* no id supplied -> create one */ ++ if (qemu_opts_id(all_opts) == NULL) { ++ char *new_id; ++ const char *mediastr = ""; ++ if (type == IF_IDE || type == IF_SCSI) { ++ mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd"; ++ } ++ if (max_devs) { ++ new_id = g_strdup_printf("%s%i%s%i", if_name[type], bus_id, ++ mediastr, unit_id); ++ } else { ++ new_id = g_strdup_printf("%s%s%i", if_name[type], ++ mediastr, unit_id); ++ } ++ qdict_put_str(bs_opts, "id", new_id); ++ g_free(new_id); ++ } ++ ++ /* Add virtio block device */ ++ if (type == IF_VIRTIO) { ++ QemuOpts *devopts; ++ devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, ++ &error_abort); ++ qemu_opt_set(devopts, "driver", "virtio-blk", &error_abort); ++ qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), ++ &error_abort); ++ } else if (type == IF_XEN) { ++ QemuOpts *devopts; ++ devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, ++ &error_abort); ++ qemu_opt_set(devopts, "driver", ++ (media == MEDIA_CDROM) ? "xen-cdrom" : "xen-disk", ++ &error_abort); ++ qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), ++ &error_abort); ++ } ++ ++ filename = qemu_opt_get(legacy_opts, "file"); ++ ++ /* Check werror/rerror compatibility with if=... */ ++ werror = qemu_opt_get(legacy_opts, "werror"); ++ if (werror != NULL) { ++ if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && ++ type != IF_NONE) { ++ error_setg(errp, "werror is not supported by this bus type"); ++ goto fail; ++ } ++ qdict_put_str(bs_opts, "werror", werror); ++ } ++ ++ rerror = qemu_opt_get(legacy_opts, "rerror"); ++ if (rerror != NULL) { ++ if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && ++ type != IF_NONE) { ++ error_setg(errp, "rerror is not supported by this bus type"); ++ goto fail; ++ } ++ qdict_put_str(bs_opts, "rerror", rerror); ++ } ++ ++ /* Actual block device init: Functionality shared with blockdev-add */ ++ blk = blockdev_init(filename, bs_opts, errp); ++ bs_opts = NULL; ++ if (!blk) { ++ goto fail; ++ } ++ ++ /* Create legacy DriveInfo */ ++ dinfo = g_malloc0(sizeof(*dinfo)); ++ dinfo->opts = all_opts; ++ ++ dinfo->type = type; ++ dinfo->bus = bus_id; ++ dinfo->unit = unit_id; ++ ++ blk_set_legacy_dinfo(blk, dinfo); ++ ++ switch(type) { ++ case IF_IDE: ++ case IF_SCSI: ++ case IF_XEN: ++ case IF_NONE: ++ dinfo->media_cd = media == MEDIA_CDROM; ++ break; ++ default: ++ break; ++ } ++ ++fail: ++ qemu_opts_del(legacy_opts); ++ qobject_unref(bs_opts); ++ return dinfo; ++} ++ ++static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) ++{ ++ BlockDriverState *bs; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_lookup_bs(name, name, errp); ++ if (bs == NULL) { ++ return NULL; ++ } ++ ++ if (!bdrv_is_root_node(bs)) { ++ error_setg(errp, "Need a root block node"); ++ return NULL; ++ } ++ ++ if (!bdrv_is_inserted(bs)) { ++ error_setg(errp, "Device has no medium"); ++ bs = NULL; ++ } ++ ++ return bs; ++} ++ ++static void blockdev_do_action(TransactionAction *action, Error **errp) ++{ ++ TransactionActionList list; ++ ++ list.value = action; ++ list.next = NULL; ++ qmp_transaction(&list, NULL, errp); ++} ++ ++void qmp_blockdev_snapshot_sync(const char *device, const char *node_name, ++ const char *snapshot_file, ++ const char *snapshot_node_name, ++ const char *format, ++ bool has_mode, NewImageMode mode, Error **errp) ++{ ++ BlockdevSnapshotSync snapshot = { ++ .device = (char *) device, ++ .node_name = (char *) node_name, ++ .snapshot_file = (char *) snapshot_file, ++ .snapshot_node_name = (char *) snapshot_node_name, ++ .format = (char *) format, ++ .has_mode = has_mode, ++ .mode = mode, ++ }; ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, ++ .u.blockdev_snapshot_sync.data = &snapshot, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++void qmp_blockdev_snapshot(const char *node, const char *overlay, ++ Error **errp) ++{ ++ BlockdevSnapshot snapshot_data = { ++ .node = (char *) node, ++ .overlay = (char *) overlay ++ }; ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT, ++ .u.blockdev_snapshot.data = &snapshot_data, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++void qmp_blockdev_snapshot_internal_sync(const char *device, ++ const char *name, ++ Error **errp) ++{ ++ BlockdevSnapshotInternal snapshot = { ++ .device = (char *) device, ++ .name = (char *) name ++ }; ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC, ++ .u.blockdev_snapshot_internal_sync.data = &snapshot, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, ++ const char *id, ++ const char *name, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ QEMUSnapshotInfo sn; ++ Error *local_err = NULL; ++ SnapshotInfo *info = NULL; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = qmp_get_root_bs(device, errp); ++ if (!bs) { ++ return NULL; ++ } ++ ++ if (!id && !name) { ++ error_setg(errp, "Name or id must be provided"); ++ return NULL; ++ } ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { ++ return NULL; ++ } ++ ++ ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return NULL; ++ } ++ if (!ret) { ++ error_setg(errp, ++ "Snapshot with id '%s' and name '%s' does not exist on " ++ "device '%s'", ++ STR_OR_NULL(id), STR_OR_NULL(name), device); ++ return NULL; ++ } ++ ++ bdrv_snapshot_delete(bs, id, name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return NULL; ++ } ++ ++ info = g_new0(SnapshotInfo, 1); ++ info->id = g_strdup(sn.id_str); ++ info->name = g_strdup(sn.name); ++ info->date_nsec = sn.date_nsec; ++ info->date_sec = sn.date_sec; ++ info->vm_state_size = sn.vm_state_size; ++ info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000; ++ info->vm_clock_sec = sn.vm_clock_nsec / 1000000000; ++ if (sn.icount != -1ULL) { ++ info->icount = sn.icount; ++ info->has_icount = true; ++ } ++ ++ return info; ++} ++ ++/* internal snapshot private data */ ++typedef struct InternalSnapshotState { ++ BlockDriverState *bs; ++ QEMUSnapshotInfo sn; ++ bool created; ++} InternalSnapshotState; ++ ++static void internal_snapshot_abort(void *opaque); ++static void internal_snapshot_clean(void *opaque); ++TransactionActionDrv internal_snapshot_drv = { ++ .abort = internal_snapshot_abort, ++ .clean = internal_snapshot_clean, ++}; ++ ++static void internal_snapshot_action(BlockdevSnapshotInternal *internal, ++ Transaction *tran, Error **errp) ++{ ++ Error *local_err = NULL; ++ const char *device; ++ const char *name; ++ BlockDriverState *bs; ++ QEMUSnapshotInfo old_sn, *sn; ++ bool ret; ++ int64_t rt; ++ InternalSnapshotState *state = g_new0(InternalSnapshotState, 1); ++ int ret1; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ tran_add(tran, &internal_snapshot_drv, state); ++ ++ device = internal->device; ++ name = internal->name; ++ ++ bs = qmp_get_root_bs(device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ state->bs = bs; ++ ++ /* Paired with .clean() */ ++ bdrv_drained_begin(bs); ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { ++ return; ++ } ++ ++ if (bdrv_is_read_only(bs)) { ++ error_setg(errp, "Device '%s' is read only", device); ++ return; ++ } ++ ++ if (!bdrv_can_snapshot(bs)) { ++ error_setg(errp, "Block format '%s' used by device '%s' " ++ "does not support internal snapshots", ++ bs->drv->format_name, device); ++ return; ++ } ++ ++ if (!strlen(name)) { ++ error_setg(errp, "Name is empty"); ++ return; ++ } ++ ++ /* check whether a snapshot with name exist */ ++ ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, ++ &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } else if (ret) { ++ error_setg(errp, ++ "Snapshot with name '%s' already exists on device '%s'", ++ name, device); ++ return; ++ } ++ ++ /* 3. take the snapshot */ ++ sn = &state->sn; ++ pstrcpy(sn->name, sizeof(sn->name), name); ++ rt = g_get_real_time(); ++ sn->date_sec = rt / G_USEC_PER_SEC; ++ sn->date_nsec = (rt % G_USEC_PER_SEC) * 1000; ++ sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ if (replay_mode != REPLAY_MODE_NONE) { ++ sn->icount = replay_get_current_icount(); ++ } else { ++ sn->icount = -1ULL; ++ } ++ ++ ret1 = bdrv_snapshot_create(bs, sn); ++ if (ret1 < 0) { ++ error_setg_errno(errp, -ret1, ++ "Failed to create snapshot '%s' on device '%s'", ++ name, device); ++ return; ++ } ++ ++ /* 4. succeed, mark a snapshot is created */ ++ state->created = true; ++} ++ ++static void internal_snapshot_abort(void *opaque) ++{ ++ InternalSnapshotState *state = opaque; ++ BlockDriverState *bs = state->bs; ++ QEMUSnapshotInfo *sn = &state->sn; ++ Error *local_error = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!state->created) { ++ return; ++ } ++ ++ if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { ++ error_reportf_err(local_error, ++ "Failed to delete snapshot with id '%s' and " ++ "name '%s' on device '%s' in abort: ", ++ sn->id_str, sn->name, ++ bdrv_get_device_name(bs)); ++ } ++} ++ ++static void internal_snapshot_clean(void *opaque) ++{ ++ g_autofree InternalSnapshotState *state = opaque; ++ ++ if (!state->bs) { ++ return; ++ } ++ ++ bdrv_drained_end(state->bs); ++} ++ ++/* external snapshot private data */ ++typedef struct ExternalSnapshotState { ++ BlockDriverState *old_bs; ++ BlockDriverState *new_bs; ++ bool overlay_appended; ++} ExternalSnapshotState; ++ ++static void external_snapshot_commit(void *opaque); ++static void external_snapshot_abort(void *opaque); ++static void external_snapshot_clean(void *opaque); ++TransactionActionDrv external_snapshot_drv = { ++ .commit = external_snapshot_commit, ++ .abort = external_snapshot_abort, ++ .clean = external_snapshot_clean, ++}; ++ ++static void external_snapshot_action(TransactionAction *action, ++ Transaction *tran, Error **errp) ++{ ++ int ret; ++ int flags = 0; ++ QDict *options = NULL; ++ Error *local_err = NULL; ++ /* Device and node name of the image to generate the snapshot from */ ++ const char *device; ++ const char *node_name; ++ /* Reference to the new image (for 'blockdev-snapshot') */ ++ const char *snapshot_ref; ++ /* File name of the new image (for 'blockdev-snapshot-sync') */ ++ const char *new_image_file; ++ ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1); ++ uint64_t perm, shared; ++ ++ /* TODO We'll eventually have to take a writer lock in this function */ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ tran_add(tran, &external_snapshot_drv, state); ++ ++ /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar ++ * purpose but a different set of parameters */ ++ switch (action->type) { ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT: ++ { ++ BlockdevSnapshot *s = action->u.blockdev_snapshot.data; ++ device = s->node; ++ node_name = s->node; ++ new_image_file = NULL; ++ snapshot_ref = s->overlay; ++ } ++ break; ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: ++ { ++ BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data; ++ device = s->device; ++ node_name = s->node_name; ++ new_image_file = s->snapshot_file; ++ snapshot_ref = NULL; ++ } ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ /* start processing */ ++ ++ state->old_bs = bdrv_lookup_bs(device, node_name, errp); ++ if (!state->old_bs) { ++ return; ++ } ++ ++ /* Paired with .clean() */ ++ bdrv_drained_begin(state->old_bs); ++ ++ if (!bdrv_is_inserted(state->old_bs)) { ++ error_setg(errp, "Device '%s' has no medium", ++ bdrv_get_device_or_node_name(state->old_bs)); ++ return; ++ } ++ ++ if (bdrv_op_is_blocked(state->old_bs, ++ BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) { ++ return; ++ } ++ ++ if (!bdrv_is_read_only(state->old_bs)) { ++ ret = bdrv_flush(state->old_bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Write to node '%s' failed", ++ bdrv_get_device_or_node_name(state->old_bs)); ++ return; ++ } ++ } ++ ++ if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) { ++ BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data; ++ const char *format = s->format ?: "qcow2"; ++ enum NewImageMode mode; ++ const char *snapshot_node_name = s->snapshot_node_name; ++ ++ if (node_name && !snapshot_node_name) { ++ error_setg(errp, "New overlay node-name missing"); ++ return; ++ } ++ ++ if (snapshot_node_name && ++ bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { ++ error_setg(errp, "New overlay node-name already in use"); ++ return; ++ } ++ ++ flags = state->old_bs->open_flags; ++ flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_COPY_ON_READ); ++ flags |= BDRV_O_NO_BACKING; ++ ++ /* create new image w/backing file */ ++ mode = s->has_mode ? s->mode : NEW_IMAGE_MODE_ABSOLUTE_PATHS; ++ if (mode != NEW_IMAGE_MODE_EXISTING) { ++ int64_t size = bdrv_getlength(state->old_bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "bdrv_getlength failed"); ++ return; ++ } ++ bdrv_refresh_filename(state->old_bs); ++ ++ bdrv_img_create(new_image_file, format, ++ state->old_bs->filename, ++ state->old_bs->drv->format_name, ++ NULL, size, flags, false, &local_err); ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ } ++ ++ options = qdict_new(); ++ if (snapshot_node_name) { ++ qdict_put_str(options, "node-name", snapshot_node_name); ++ } ++ qdict_put_str(options, "driver", format); ++ } ++ ++ state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags, ++ errp); ++ ++ /* We will manually add the backing_hd field to the bs later */ ++ if (!state->new_bs) { ++ return; ++ } ++ ++ /* ++ * Allow attaching a backing file to an overlay that's already in use only ++ * if the parents don't assume that they are already seeing a valid image. ++ * (Specifically, allow it as a mirror target, which is write-only access.) ++ */ ++ bdrv_get_cumulative_perm(state->new_bs, &perm, &shared); ++ if (perm & BLK_PERM_CONSISTENT_READ) { ++ error_setg(errp, "The overlay is already in use"); ++ return; ++ } ++ ++ if (state->new_bs->drv->is_filter) { ++ error_setg(errp, "Filters cannot be used as overlays"); ++ return; ++ } ++ ++ if (bdrv_cow_child(state->new_bs)) { ++ error_setg(errp, "The overlay already has a backing image"); ++ return; ++ } ++ ++ if (!state->new_bs->drv->supports_backing) { ++ error_setg(errp, "The overlay does not support backing images"); ++ return; ++ } ++ ++ ret = bdrv_append(state->new_bs, state->old_bs, errp); ++ if (ret < 0) { ++ return; ++ } ++ state->overlay_appended = true; ++} ++ ++static void external_snapshot_commit(void *opaque) ++{ ++ ExternalSnapshotState *state = opaque; ++ ++ /* We don't need (or want) to use the transactional ++ * bdrv_reopen_multiple() across all the entries at once, because we ++ * don't want to abort all of them if one of them fails the reopen */ ++ if (!qatomic_read(&state->old_bs->copy_on_read)) { ++ bdrv_reopen_set_read_only(state->old_bs, true, NULL); ++ } ++} ++ ++static void external_snapshot_abort(void *opaque) ++{ ++ ExternalSnapshotState *state = opaque; ++ if (state->new_bs) { ++ if (state->overlay_appended) { ++ AioContext *aio_context; ++ AioContext *tmp_context; ++ int ret; ++ ++ aio_context = bdrv_get_aio_context(state->old_bs); ++ ++ bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd() ++ close state->old_bs; we need it */ ++ bdrv_set_backing_hd(state->new_bs, NULL, &error_abort); ++ ++ /* ++ * The call to bdrv_set_backing_hd() above returns state->old_bs to ++ * the main AioContext. As we're still going to be using it, return ++ * it to the AioContext it was before. ++ */ ++ tmp_context = bdrv_get_aio_context(state->old_bs); ++ if (aio_context != tmp_context) { ++ ret = bdrv_try_change_aio_context(state->old_bs, ++ aio_context, NULL, NULL); ++ assert(ret == 0); ++ } ++ ++ bdrv_drained_begin(state->new_bs); ++ bdrv_graph_wrlock(); ++ bdrv_replace_node(state->new_bs, state->old_bs, &error_abort); ++ bdrv_graph_wrunlock(); ++ bdrv_drained_end(state->new_bs); ++ ++ bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ ++ } ++ } ++} ++ ++static void external_snapshot_clean(void *opaque) ++{ ++ g_autofree ExternalSnapshotState *state = opaque; ++ ++ if (!state->old_bs) { ++ return; ++ } ++ ++ bdrv_drained_end(state->old_bs); ++ bdrv_unref(state->new_bs); ++} ++ ++typedef struct DriveBackupState { ++ BlockDriverState *bs; ++ BlockJob *job; ++} DriveBackupState; ++ ++static BlockJob *do_backup_common(BackupCommon *backup, ++ BlockDriverState *bs, ++ BlockDriverState *target_bs, ++ AioContext *aio_context, ++ JobTxn *txn, Error **errp); ++ ++static void drive_backup_commit(void *opaque); ++static void drive_backup_abort(void *opaque); ++static void drive_backup_clean(void *opaque); ++TransactionActionDrv drive_backup_drv = { ++ .commit = drive_backup_commit, ++ .abort = drive_backup_abort, ++ .clean = drive_backup_clean, ++}; ++ ++static void drive_backup_action(DriveBackup *backup, ++ JobTxn *block_job_txn, ++ Transaction *tran, Error **errp) ++{ ++ DriveBackupState *state = g_new0(DriveBackupState, 1); ++ BlockDriverState *bs; ++ BlockDriverState *target_bs; ++ BlockDriverState *source = NULL; ++ AioContext *aio_context; ++ const char *format; ++ QDict *options; ++ Error *local_err = NULL; ++ int flags; ++ int64_t size; ++ bool set_backing_hd = false; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ tran_add(tran, &drive_backup_drv, state); ++ ++ if (!backup->has_mode) { ++ backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; ++ } ++ ++ bs = bdrv_lookup_bs(backup->device, backup->device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ if (!bs->drv) { ++ error_setg(errp, "Device has no medium"); ++ return; ++ } ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ state->bs = bs; ++ /* Paired with .clean() */ ++ bdrv_drained_begin(bs); ++ ++ format = backup->format; ++ if (!format && backup->mode != NEW_IMAGE_MODE_EXISTING) { ++ format = bs->drv->format_name; ++ } ++ ++ /* Early check to avoid creating target */ ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) { ++ bdrv_graph_rdunlock_main_loop(); ++ return; ++ } ++ ++ flags = bs->open_flags | BDRV_O_RDWR; ++ ++ /* ++ * See if we have a backing HD we can use to create our new image ++ * on top of. ++ */ ++ if (backup->sync == MIRROR_SYNC_MODE_TOP) { ++ /* ++ * Backup will not replace the source by the target, so none ++ * of the filters skipped here will be removed (in contrast to ++ * mirror). Therefore, we can skip all of them when looking ++ * for the first COW relationship. ++ */ ++ source = bdrv_cow_bs(bdrv_skip_filters(bs)); ++ if (!source) { ++ backup->sync = MIRROR_SYNC_MODE_FULL; ++ } ++ } ++ if (backup->sync == MIRROR_SYNC_MODE_NONE) { ++ source = bs; ++ flags |= BDRV_O_NO_BACKING; ++ set_backing_hd = true; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ size = bdrv_getlength(bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "bdrv_getlength failed"); ++ return; ++ } ++ ++ if (backup->mode != NEW_IMAGE_MODE_EXISTING) { ++ assert(format); ++ if (source) { ++ /* Implicit filters should not appear in the filename */ ++ BlockDriverState *explicit_backing; ++ ++ bdrv_graph_rdlock_main_loop(); ++ explicit_backing = bdrv_skip_implicit_filters(source); ++ bdrv_refresh_filename(explicit_backing); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_img_create(backup->target, format, ++ explicit_backing->filename, ++ explicit_backing->drv->format_name, NULL, ++ size, flags, false, &local_err); ++ } else { ++ bdrv_img_create(backup->target, format, NULL, NULL, NULL, ++ size, flags, false, &local_err); ++ } ++ } ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ options = qdict_new(); ++ qdict_put_str(options, "discard", "unmap"); ++ qdict_put_str(options, "detect-zeroes", "unmap"); ++ if (format) { ++ qdict_put_str(options, "driver", format); ++ } ++ ++ target_bs = bdrv_open(backup->target, NULL, options, flags, errp); ++ if (!target_bs) { ++ return; ++ } ++ ++ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); ++ if (ret < 0) { ++ bdrv_unref(target_bs); ++ return; ++ } ++ ++ if (set_backing_hd) { ++ if (bdrv_set_backing_hd(target_bs, source, errp) < 0) { ++ goto unref; ++ } ++ } ++ ++ state->job = do_backup_common(qapi_DriveBackup_base(backup), ++ bs, target_bs, aio_context, ++ block_job_txn, errp); ++ ++unref: ++ bdrv_unref(target_bs); ++} ++ ++static void drive_backup_commit(void *opaque) ++{ ++ DriveBackupState *state = opaque; ++ ++ assert(state->job); ++ job_start(&state->job->job); ++} ++ ++static void drive_backup_abort(void *opaque) ++{ ++ DriveBackupState *state = opaque; ++ ++ if (state->job) { ++ job_cancel_sync(&state->job->job, true); ++ } ++} ++ ++static void drive_backup_clean(void *opaque) ++{ ++ g_autofree DriveBackupState *state = opaque; ++ ++ if (!state->bs) { ++ return; ++ } ++ ++ bdrv_drained_end(state->bs); ++} ++ ++typedef struct BlockdevBackupState { ++ BlockDriverState *bs; ++ BlockJob *job; ++} BlockdevBackupState; ++ ++static void blockdev_backup_commit(void *opaque); ++static void blockdev_backup_abort(void *opaque); ++static void blockdev_backup_clean(void *opaque); ++TransactionActionDrv blockdev_backup_drv = { ++ .commit = blockdev_backup_commit, ++ .abort = blockdev_backup_abort, ++ .clean = blockdev_backup_clean, ++}; ++ ++static void blockdev_backup_action(BlockdevBackup *backup, ++ JobTxn *block_job_txn, ++ Transaction *tran, Error **errp) ++{ ++ BlockdevBackupState *state = g_new0(BlockdevBackupState, 1); ++ BlockDriverState *bs; ++ BlockDriverState *target_bs; ++ AioContext *aio_context; ++ int ret; ++ ++ tran_add(tran, &blockdev_backup_drv, state); ++ ++ bs = bdrv_lookup_bs(backup->device, backup->device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ target_bs = bdrv_lookup_bs(backup->target, backup->target, errp); ++ if (!target_bs) { ++ return; ++ } ++ ++ /* Honor bdrv_try_change_aio_context() context acquisition requirements. */ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); ++ if (ret < 0) { ++ return; ++ } ++ ++ state->bs = bs; ++ ++ /* Paired with .clean() */ ++ bdrv_drained_begin(state->bs); ++ ++ state->job = do_backup_common(qapi_BlockdevBackup_base(backup), ++ bs, target_bs, aio_context, ++ block_job_txn, errp); ++} ++ ++static void blockdev_backup_commit(void *opaque) ++{ ++ BlockdevBackupState *state = opaque; ++ ++ assert(state->job); ++ job_start(&state->job->job); ++} ++ ++static void blockdev_backup_abort(void *opaque) ++{ ++ BlockdevBackupState *state = opaque; ++ ++ if (state->job) { ++ job_cancel_sync(&state->job->job, true); ++ } ++} ++ ++static void blockdev_backup_clean(void *opaque) ++{ ++ g_autofree BlockdevBackupState *state = opaque; ++ ++ if (!state->bs) { ++ return; ++ } ++ ++ bdrv_drained_end(state->bs); ++} ++ ++typedef struct BlockDirtyBitmapState { ++ BdrvDirtyBitmap *bitmap; ++ BlockDriverState *bs; ++ HBitmap *backup; ++ bool was_enabled; ++} BlockDirtyBitmapState; ++ ++static void block_dirty_bitmap_add_abort(void *opaque); ++TransactionActionDrv block_dirty_bitmap_add_drv = { ++ .abort = block_dirty_bitmap_add_abort, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_add_action(BlockDirtyBitmapAdd *action, ++ Transaction *tran, Error **errp) ++{ ++ Error *local_err = NULL; ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_add_drv, state); ++ ++ /* AIO context taken and released within qmp_block_dirty_bitmap_add */ ++ qmp_block_dirty_bitmap_add(action->node, action->name, ++ action->has_granularity, action->granularity, ++ action->has_persistent, action->persistent, ++ action->has_disabled, action->disabled, ++ &local_err); ++ ++ if (!local_err) { ++ state->bitmap = block_dirty_bitmap_lookup(action->node, action->name, ++ NULL, &error_abort); ++ } else { ++ error_propagate(errp, local_err); ++ } ++} ++ ++static void block_dirty_bitmap_add_abort(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (state->bitmap) { ++ bdrv_release_dirty_bitmap(state->bitmap); ++ } ++} ++ ++static void block_dirty_bitmap_restore(void *opaque); ++static void block_dirty_bitmap_free_backup(void *opaque); ++TransactionActionDrv block_dirty_bitmap_clear_drv = { ++ .abort = block_dirty_bitmap_restore, ++ .commit = block_dirty_bitmap_free_backup, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_clear_action(BlockDirtyBitmap *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_clear_drv, state); ++ ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->name, ++ &state->bs, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_DEFAULT, errp)) { ++ return; ++ } ++ ++ bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); ++} ++ ++static void block_dirty_bitmap_restore(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (state->backup) { ++ bdrv_restore_dirty_bitmap(state->bitmap, state->backup); ++ } ++} ++ ++static void block_dirty_bitmap_free_backup(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ hbitmap_free(state->backup); ++} ++ ++static void block_dirty_bitmap_enable_abort(void *opaque); ++TransactionActionDrv block_dirty_bitmap_enable_drv = { ++ .abort = block_dirty_bitmap_enable_abort, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_enable_action(BlockDirtyBitmap *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_enable_drv, state); ++ ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->name, ++ NULL, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return; ++ } ++ ++ state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); ++ bdrv_enable_dirty_bitmap(state->bitmap); ++} ++ ++static void block_dirty_bitmap_enable_abort(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (!state->was_enabled) { ++ bdrv_disable_dirty_bitmap(state->bitmap); ++ } ++} ++ ++static void block_dirty_bitmap_disable_abort(void *opaque); ++TransactionActionDrv block_dirty_bitmap_disable_drv = { ++ .abort = block_dirty_bitmap_disable_abort, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_disable_action(BlockDirtyBitmap *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_disable_drv, state); ++ ++ state->bitmap = block_dirty_bitmap_lookup(action->node, ++ action->name, ++ NULL, ++ errp); ++ if (!state->bitmap) { ++ return; ++ } ++ ++ if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return; ++ } ++ ++ state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); ++ bdrv_disable_dirty_bitmap(state->bitmap); ++} ++ ++static void block_dirty_bitmap_disable_abort(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (state->was_enabled) { ++ bdrv_enable_dirty_bitmap(state->bitmap); ++ } ++} ++ ++TransactionActionDrv block_dirty_bitmap_merge_drv = { ++ .commit = block_dirty_bitmap_free_backup, ++ .abort = block_dirty_bitmap_restore, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_merge_action(BlockDirtyBitmapMerge *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_merge_drv, state); ++ ++ state->bitmap = block_dirty_bitmap_merge(action->node, action->target, ++ action->bitmaps, &state->backup, ++ errp); ++} ++ ++static void block_dirty_bitmap_remove_commit(void *opaque); ++static void block_dirty_bitmap_remove_abort(void *opaque); ++TransactionActionDrv block_dirty_bitmap_remove_drv = { ++ .commit = block_dirty_bitmap_remove_commit, ++ .abort = block_dirty_bitmap_remove_abort, ++ .clean = g_free, ++}; ++ ++static void block_dirty_bitmap_remove_action(BlockDirtyBitmap *action, ++ Transaction *tran, Error **errp) ++{ ++ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1); ++ ++ tran_add(tran, &block_dirty_bitmap_remove_drv, state); ++ ++ ++ state->bitmap = block_dirty_bitmap_remove(action->node, action->name, ++ false, &state->bs, errp); ++ if (state->bitmap) { ++ bdrv_dirty_bitmap_skip_store(state->bitmap, true); ++ bdrv_dirty_bitmap_set_busy(state->bitmap, true); ++ } ++} ++ ++static void block_dirty_bitmap_remove_abort(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ if (state->bitmap) { ++ bdrv_dirty_bitmap_skip_store(state->bitmap, false); ++ bdrv_dirty_bitmap_set_busy(state->bitmap, false); ++ } ++} ++ ++static void block_dirty_bitmap_remove_commit(void *opaque) ++{ ++ BlockDirtyBitmapState *state = opaque; ++ ++ bdrv_dirty_bitmap_set_busy(state->bitmap, false); ++ bdrv_release_dirty_bitmap(state->bitmap); ++} ++ ++static void abort_commit(void *opaque); ++TransactionActionDrv abort_drv = { ++ .commit = abort_commit, ++}; ++ ++static void abort_action(Transaction *tran, Error **errp) ++{ ++ tran_add(tran, &abort_drv, NULL); ++ error_setg(errp, "Transaction aborted using Abort action"); ++} ++ ++static void abort_commit(void *opaque) ++{ ++ g_assert_not_reached(); /* this action never succeeds */ ++} ++ ++static void transaction_action(TransactionAction *act, JobTxn *block_job_txn, ++ Transaction *tran, Error **errp) ++{ ++ switch (act->type) { ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT: ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: ++ external_snapshot_action(act, tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_DRIVE_BACKUP: ++ drive_backup_action(act->u.drive_backup.data, ++ block_job_txn, tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP: ++ blockdev_backup_action(act->u.blockdev_backup.data, ++ block_job_txn, tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_ABORT: ++ abort_action(tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC: ++ internal_snapshot_action(act->u.blockdev_snapshot_internal_sync.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD: ++ block_dirty_bitmap_add_action(act->u.block_dirty_bitmap_add.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR: ++ block_dirty_bitmap_clear_action(act->u.block_dirty_bitmap_clear.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE: ++ block_dirty_bitmap_enable_action(act->u.block_dirty_bitmap_enable.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE: ++ block_dirty_bitmap_disable_action( ++ act->u.block_dirty_bitmap_disable.data, tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE: ++ block_dirty_bitmap_merge_action(act->u.block_dirty_bitmap_merge.data, ++ tran, errp); ++ return; ++ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE: ++ block_dirty_bitmap_remove_action(act->u.block_dirty_bitmap_remove.data, ++ tran, errp); ++ return; ++ /* ++ * Where are transactions for MIRROR, COMMIT and STREAM? ++ * Although these blockjobs use transaction callbacks like the backup job, ++ * these jobs do not necessarily adhere to transaction semantics. ++ * These jobs may not fully undo all of their actions on abort, nor do they ++ * necessarily work in transactions with more than one job in them. ++ */ ++ case TRANSACTION_ACTION_KIND__MAX: ++ default: ++ g_assert_not_reached(); ++ }; ++} ++ ++ ++/* ++ * 'Atomic' group operations. The operations are performed as a set, and if ++ * any fail then we roll back all operations in the group. ++ * ++ * Always run under BQL. ++ */ ++void qmp_transaction(TransactionActionList *actions, ++ struct TransactionProperties *properties, ++ Error **errp) ++{ ++ TransactionActionList *act; ++ JobTxn *block_job_txn = NULL; ++ Error *local_err = NULL; ++ Transaction *tran; ++ ActionCompletionMode comp_mode = ++ properties ? properties->completion_mode : ++ ACTION_COMPLETION_MODE_INDIVIDUAL; ++ ++ GLOBAL_STATE_CODE(); ++ ++ /* Does this transaction get canceled as a group on failure? ++ * If not, we don't really need to make a JobTxn. ++ */ ++ if (comp_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { ++ for (act = actions; act; act = act->next) { ++ TransactionActionKind type = act->value->type; ++ ++ if (type != TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP && ++ type != TRANSACTION_ACTION_KIND_DRIVE_BACKUP) ++ { ++ error_setg(errp, ++ "Action '%s' does not support transaction property " ++ "completion-mode = %s", ++ TransactionActionKind_str(type), ++ ActionCompletionMode_str(comp_mode)); ++ return; ++ } ++ } ++ ++ block_job_txn = job_txn_new(); ++ } ++ ++ /* drain all i/o before any operations */ ++ bdrv_drain_all(); ++ ++ tran = tran_new(); ++ ++ /* We don't do anything in this loop that commits us to the operations */ ++ for (act = actions; act; act = act->next) { ++ transaction_action(act->value, block_job_txn, tran, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ goto delete_and_fail; ++ } ++ } ++ ++ tran_commit(tran); ++ ++ /* success */ ++ goto exit; ++ ++delete_and_fail: ++ /* failure, and it is all-or-none; roll back all operations */ ++ tran_abort(tran); ++exit: ++ job_txn_unref(block_job_txn); ++} ++ ++BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, ++ const char *name, ++ Error **errp) ++{ ++ BdrvDirtyBitmap *bitmap; ++ BlockDriverState *bs; ++ BlockDirtyBitmapSha256 *ret = NULL; ++ char *sha256; ++ ++ bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); ++ if (!bitmap || !bs) { ++ return NULL; ++ } ++ ++ sha256 = bdrv_dirty_bitmap_sha256(bitmap, errp); ++ if (sha256 == NULL) { ++ return NULL; ++ } ++ ++ ret = g_new(BlockDirtyBitmapSha256, 1); ++ ret->sha256 = sha256; ++ ++ return ret; ++} ++ ++void coroutine_fn qmp_block_resize(const char *device, const char *node_name, ++ int64_t size, Error **errp) ++{ ++ Error *local_err = NULL; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ AioContext *old_ctx; ++ ++ bs = bdrv_lookup_bs(device, node_name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ if (size < 0) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size"); ++ return; ++ } ++ ++ bdrv_graph_co_rdlock(); ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, errp)) { ++ bdrv_graph_co_rdunlock(); ++ return; ++ } ++ bdrv_graph_co_rdunlock(); ++ ++ blk = blk_co_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp); ++ if (!blk) { ++ return; ++ } ++ ++ bdrv_drained_begin(bs); ++ ++ old_ctx = bdrv_co_enter(bs); ++ blk_co_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp); ++ bdrv_co_leave(bs, old_ctx); ++ ++ bdrv_drained_end(bs); ++ blk_co_unref(blk); ++} ++ ++void qmp_block_stream(const char *job_id, const char *device, ++ const char *base, ++ const char *base_node, ++ const char *backing_file, ++ bool has_backing_mask_protocol, ++ bool backing_mask_protocol, ++ const char *bottom, ++ bool has_speed, int64_t speed, ++ bool has_on_error, BlockdevOnError on_error, ++ const char *filter_node_name, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, ++ Error **errp) ++{ ++ BlockDriverState *bs, *iter, *iter_end; ++ BlockDriverState *base_bs = NULL; ++ BlockDriverState *bottom_bs = NULL; ++ AioContext *aio_context; ++ Error *local_err = NULL; ++ int job_flags = JOB_DEFAULT; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (base && base_node) { ++ error_setg(errp, "'base' and 'base-node' cannot be specified " ++ "at the same time"); ++ return; ++ } ++ ++ if (base && bottom) { ++ error_setg(errp, "'base' and 'bottom' cannot be specified " ++ "at the same time"); ++ return; ++ } ++ ++ if (bottom && base_node) { ++ error_setg(errp, "'bottom' and 'base-node' cannot be specified " ++ "at the same time"); ++ return; ++ } ++ ++ if (!has_backing_mask_protocol) { ++ backing_mask_protocol = false; ++ } ++ ++ if (!has_on_error) { ++ on_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ ++ bs = bdrv_lookup_bs(device, device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ bdrv_graph_rdlock_main_loop(); ++ if (base) { ++ base_bs = bdrv_find_backing_image(bs, base); ++ if (base_bs == NULL) { ++ error_setg(errp, "Can't find '%s' in the backing chain", base); ++ goto out_rdlock; ++ } ++ assert(bdrv_get_aio_context(base_bs) == aio_context); ++ } ++ ++ if (base_node) { ++ base_bs = bdrv_lookup_bs(NULL, base_node, errp); ++ if (!base_bs) { ++ goto out_rdlock; ++ } ++ if (bs == base_bs || !bdrv_chain_contains(bs, base_bs)) { ++ error_setg(errp, "Node '%s' is not a backing image of '%s'", ++ base_node, device); ++ goto out_rdlock; ++ } ++ assert(bdrv_get_aio_context(base_bs) == aio_context); ++ ++ bdrv_refresh_filename(base_bs); ++ } ++ ++ if (bottom) { ++ bottom_bs = bdrv_lookup_bs(NULL, bottom, errp); ++ if (!bottom_bs) { ++ goto out_rdlock; ++ } ++ if (!bottom_bs->drv) { ++ error_setg(errp, "Node '%s' is not open", bottom); ++ goto out_rdlock; ++ } ++ if (bottom_bs->drv->is_filter) { ++ error_setg(errp, "Node '%s' is a filter, use a non-filter node " ++ "as 'bottom'", bottom); ++ goto out_rdlock; ++ } ++ if (!bdrv_chain_contains(bs, bottom_bs)) { ++ error_setg(errp, "Node '%s' is not in a chain starting from '%s'", ++ bottom, device); ++ goto out_rdlock; ++ } ++ assert(bdrv_get_aio_context(bottom_bs) == aio_context); ++ } ++ ++ /* ++ * Check for op blockers in the whole chain between bs and base (or bottom) ++ */ ++ iter_end = bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs; ++ for (iter = bs; iter && iter != iter_end; ++ iter = bdrv_filter_or_cow_bs(iter)) ++ { ++ if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) { ++ goto out_rdlock; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* if we are streaming the entire chain, the result will have no backing ++ * file, and specifying one is therefore an error */ ++ if (!base_bs && backing_file) { ++ error_setg(errp, "backing file specified, but streaming the " ++ "entire chain"); ++ return; ++ } ++ ++ if (has_auto_finalize && !auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (has_auto_dismiss && !auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ ++ stream_start(job_id, bs, base_bs, backing_file, ++ backing_mask_protocol, ++ bottom_bs, job_flags, has_speed ? speed : 0, on_error, ++ filter_node_name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ trace_qmp_block_stream(bs); ++ return; ++ ++out_rdlock: ++ bdrv_graph_rdunlock_main_loop(); ++} ++ ++void qmp_block_commit(const char *job_id, const char *device, ++ const char *base_node, ++ const char *base, ++ const char *top_node, ++ const char *top, ++ const char *backing_file, ++ bool has_backing_mask_protocol, ++ bool backing_mask_protocol, ++ bool has_speed, int64_t speed, ++ bool has_on_error, BlockdevOnError on_error, ++ const char *filter_node_name, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BlockDriverState *iter; ++ BlockDriverState *base_bs, *top_bs; ++ AioContext *aio_context; ++ Error *local_err = NULL; ++ int job_flags = JOB_DEFAULT; ++ uint64_t top_perm, top_shared; ++ ++ /* TODO We'll eventually have to take a writer lock in this function */ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!has_speed) { ++ speed = 0; ++ } ++ if (!has_on_error) { ++ on_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (has_auto_finalize && !auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (has_auto_dismiss && !auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ if (!has_backing_mask_protocol) { ++ backing_mask_protocol = false; ++ } ++ ++ /* Important Note: ++ * libvirt relies on the DeviceNotFound error class in order to probe for ++ * live commit feature versions; for this to work, we must make sure to ++ * perform the device lookup before any generic errors that may occur in a ++ * scenario in which all optional arguments are omitted. */ ++ bs = qmp_get_root_bs(device, &local_err); ++ if (!bs) { ++ bs = bdrv_lookup_bs(device, device, NULL); ++ if (!bs) { ++ error_free(local_err); ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", device); ++ } else { ++ error_propagate(errp, local_err); ++ } ++ return; ++ } ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, errp)) { ++ return; ++ } ++ ++ /* default top_bs is the active layer */ ++ top_bs = bs; ++ ++ if (top_node && top) { ++ error_setg(errp, "'top-node' and 'top' are mutually exclusive"); ++ return; ++ } else if (top_node) { ++ top_bs = bdrv_lookup_bs(NULL, top_node, errp); ++ if (top_bs == NULL) { ++ return; ++ } ++ if (!bdrv_chain_contains(bs, top_bs)) { ++ error_setg(errp, "'%s' is not in this backing file chain", ++ top_node); ++ return; ++ } ++ } else if (top) { ++ /* This strcmp() is just a shortcut, there is no need to ++ * refresh @bs's filename. If it mismatches, ++ * bdrv_find_backing_image() will do the refresh and may still ++ * return @bs. */ ++ if (strcmp(bs->filename, top) != 0) { ++ top_bs = bdrv_find_backing_image(bs, top); ++ } ++ } ++ ++ if (top_bs == NULL) { ++ error_setg(errp, "Top image file %s not found", top ? top : "NULL"); ++ return; ++ } ++ ++ assert(bdrv_get_aio_context(top_bs) == aio_context); ++ ++ if (base_node && base) { ++ error_setg(errp, "'base-node' and 'base' are mutually exclusive"); ++ return; ++ } else if (base_node) { ++ base_bs = bdrv_lookup_bs(NULL, base_node, errp); ++ if (base_bs == NULL) { ++ return; ++ } ++ if (!bdrv_chain_contains(top_bs, base_bs)) { ++ error_setg(errp, "'%s' is not in this backing file chain", ++ base_node); ++ return; ++ } ++ } else if (base) { ++ base_bs = bdrv_find_backing_image(top_bs, base); ++ if (base_bs == NULL) { ++ error_setg(errp, "Can't find '%s' in the backing chain", base); ++ return; ++ } ++ } else { ++ base_bs = bdrv_find_base(top_bs); ++ if (base_bs == NULL) { ++ error_setg(errp, "There is no backimg image"); ++ return; ++ } ++ } ++ ++ assert(bdrv_get_aio_context(base_bs) == aio_context); ++ ++ for (iter = top_bs; iter != bdrv_filter_or_cow_bs(base_bs); ++ iter = bdrv_filter_or_cow_bs(iter)) ++ { ++ if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { ++ return; ++ } ++ } ++ ++ /* Do not allow attempts to commit an image into itself */ ++ if (top_bs == base_bs) { ++ error_setg(errp, "cannot commit an image into itself"); ++ return; ++ } ++ ++ /* ++ * Active commit is required if and only if someone has taken a ++ * WRITE permission on the top node. Historically, we have always ++ * used active commit for top nodes, so continue that practice ++ * lest we possibly break clients that rely on this behavior, e.g. ++ * to later attach this node to a writing parent. ++ * (Active commit is never really wrong.) ++ */ ++ bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared); ++ if (top_perm & BLK_PERM_WRITE || ++ bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) ++ { ++ if (backing_file) { ++ if (bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) { ++ error_setg(errp, "'backing-file' specified," ++ " but 'top' is the active layer"); ++ } else { ++ error_setg(errp, "'backing-file' specified, but 'top' has a " ++ "writer on it"); ++ } ++ return; ++ } ++ if (!job_id) { ++ /* ++ * Emulate here what block_job_create() does, because it ++ * is possible that @bs != @top_bs (the block job should ++ * be named after @bs, even if @top_bs is the actual ++ * source) ++ */ ++ job_id = bdrv_get_device_name(bs); ++ } ++ commit_active_start(job_id, top_bs, base_bs, job_flags, speed, on_error, ++ filter_node_name, NULL, NULL, false, &local_err); ++ } else { ++ BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); ++ if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { ++ return; ++ } ++ commit_start(job_id, bs, base_bs, top_bs, job_flags, ++ speed, on_error, backing_file, ++ backing_mask_protocol, ++ filter_node_name, &local_err); ++ } ++ if (local_err != NULL) { ++ error_propagate(errp, local_err); ++ return; ++ } ++} ++ ++/* Common QMP interface for drive-backup and blockdev-backup */ ++static BlockJob *do_backup_common(BackupCommon *backup, ++ BlockDriverState *bs, ++ BlockDriverState *target_bs, ++ AioContext *aio_context, ++ JobTxn *txn, Error **errp) ++{ ++ BlockJob *job = NULL; ++ BdrvDirtyBitmap *bmap = NULL; ++ BackupPerf perf = { .max_workers = 64 }; ++ int job_flags = JOB_DEFAULT; ++ ++ if (!backup->has_speed) { ++ backup->speed = 0; ++ } ++ if (!backup->has_on_source_error) { ++ backup->on_source_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (!backup->has_on_target_error) { ++ backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (!backup->has_auto_finalize) { ++ backup->auto_finalize = true; ++ } ++ if (!backup->has_auto_dismiss) { ++ backup->auto_dismiss = true; ++ } ++ if (!backup->has_compress) { ++ backup->compress = false; ++ } ++ ++ if (backup->x_perf) { ++ if (backup->x_perf->has_use_copy_range) { ++ perf.use_copy_range = backup->x_perf->use_copy_range; ++ } ++ if (backup->x_perf->has_max_workers) { ++ perf.max_workers = backup->x_perf->max_workers; ++ } ++ if (backup->x_perf->has_max_chunk) { ++ perf.max_chunk = backup->x_perf->max_chunk; ++ } ++ } ++ ++ if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) || ++ (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) { ++ /* done before desugaring 'incremental' to print the right message */ ++ if (!backup->bitmap) { ++ error_setg(errp, "must provide a valid bitmap name for " ++ "'%s' sync mode", MirrorSyncMode_str(backup->sync)); ++ return NULL; ++ } ++ } ++ ++ if (backup->sync == MIRROR_SYNC_MODE_INCREMENTAL) { ++ if (backup->has_bitmap_mode && ++ backup->bitmap_mode != BITMAP_SYNC_MODE_ON_SUCCESS) { ++ error_setg(errp, "Bitmap sync mode must be '%s' " ++ "when using sync mode '%s'", ++ BitmapSyncMode_str(BITMAP_SYNC_MODE_ON_SUCCESS), ++ MirrorSyncMode_str(backup->sync)); ++ return NULL; ++ } ++ backup->has_bitmap_mode = true; ++ backup->sync = MIRROR_SYNC_MODE_BITMAP; ++ backup->bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS; ++ } ++ ++ if (backup->bitmap) { ++ bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap); ++ if (!bmap) { ++ error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); ++ return NULL; ++ } ++ if (!backup->has_bitmap_mode) { ++ error_setg(errp, "Bitmap sync mode must be given " ++ "when providing a bitmap"); ++ return NULL; ++ } ++ if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_ALLOW_RO, errp)) { ++ return NULL; ++ } ++ ++ /* This does not produce a useful bitmap artifact: */ ++ if (backup->sync == MIRROR_SYNC_MODE_NONE) { ++ error_setg(errp, "sync mode '%s' does not produce meaningful bitmap" ++ " outputs", MirrorSyncMode_str(backup->sync)); ++ return NULL; ++ } ++ ++ /* If the bitmap isn't used for input or output, this is useless: */ ++ if (backup->bitmap_mode == BITMAP_SYNC_MODE_NEVER && ++ backup->sync != MIRROR_SYNC_MODE_BITMAP) { ++ error_setg(errp, "Bitmap sync mode '%s' has no meaningful effect" ++ " when combined with sync mode '%s'", ++ BitmapSyncMode_str(backup->bitmap_mode), ++ MirrorSyncMode_str(backup->sync)); ++ return NULL; ++ } ++ } ++ ++ if (!backup->bitmap && backup->has_bitmap_mode) { ++ error_setg(errp, "Cannot specify bitmap sync mode without a bitmap"); ++ return NULL; ++ } ++ ++ if (!backup->auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (!backup->auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ ++ job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, ++ backup->sync, bmap, backup->bitmap_mode, ++ backup->compress, backup->discard_source, ++ backup->filter_node_name, ++ &perf, ++ backup->on_source_error, ++ backup->on_target_error, ++ job_flags, NULL, NULL, txn, errp); ++ return job; ++} ++ ++void qmp_drive_backup(DriveBackup *backup, Error **errp) ++{ ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_DRIVE_BACKUP, ++ .u.drive_backup.data = backup, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++BlockDeviceInfoList *qmp_query_named_block_nodes(bool has_flat, ++ bool flat, ++ Error **errp) ++{ ++ bool return_flat = has_flat && flat; ++ ++ return bdrv_named_nodes_list(return_flat, errp); ++} ++ ++XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) ++{ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ return bdrv_get_xdbg_block_graph(errp); ++} ++ ++void qmp_blockdev_backup(BlockdevBackup *backup, Error **errp) ++{ ++ TransactionAction action = { ++ .type = TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP, ++ .u.blockdev_backup.data = backup, ++ }; ++ blockdev_do_action(&action, errp); ++} ++ ++/* Parameter check and block job starting for drive mirroring. ++ * Caller should hold @device and @target's aio context (must be the same). ++ **/ ++static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, ++ BlockDriverState *target, ++ const char *replaces, ++ enum MirrorSyncMode sync, ++ BlockMirrorBackingMode backing_mode, ++ bool zero_target, ++ bool has_speed, int64_t speed, ++ bool has_granularity, uint32_t granularity, ++ bool has_buf_size, int64_t buf_size, ++ bool has_on_source_error, ++ BlockdevOnError on_source_error, ++ bool has_on_target_error, ++ BlockdevOnError on_target_error, ++ bool has_unmap, bool unmap, ++ const char *filter_node_name, ++ bool has_copy_mode, MirrorCopyMode copy_mode, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, ++ Error **errp) ++{ ++ BlockDriverState *unfiltered_bs; ++ int job_flags = JOB_DEFAULT; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!has_speed) { ++ speed = 0; ++ } ++ if (!has_on_source_error) { ++ on_source_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (!has_on_target_error) { ++ on_target_error = BLOCKDEV_ON_ERROR_REPORT; ++ } ++ if (!has_granularity) { ++ granularity = 0; ++ } ++ if (!has_buf_size) { ++ buf_size = 0; ++ } ++ if (!has_unmap) { ++ unmap = true; ++ } ++ if (!has_copy_mode) { ++ copy_mode = MIRROR_COPY_MODE_BACKGROUND; ++ } ++ if (has_auto_finalize && !auto_finalize) { ++ job_flags |= JOB_MANUAL_FINALIZE; ++ } ++ if (has_auto_dismiss && !auto_dismiss) { ++ job_flags |= JOB_MANUAL_DISMISS; ++ } ++ ++ if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", ++ "a value in range [512B, 64MB]"); ++ return; ++ } ++ if (granularity & (granularity - 1)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", ++ "a power of 2"); ++ return; ++ } ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { ++ return; ++ } ++ if (bdrv_op_is_blocked(target, BLOCK_OP_TYPE_MIRROR_TARGET, errp)) { ++ return; ++ } ++ ++ if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) { ++ sync = MIRROR_SYNC_MODE_FULL; ++ } ++ ++ if (!replaces) { ++ /* We want to mirror from @bs, but keep implicit filters on top */ ++ unfiltered_bs = bdrv_skip_implicit_filters(bs); ++ if (unfiltered_bs != bs) { ++ replaces = unfiltered_bs->node_name; ++ } ++ } ++ ++ if (replaces) { ++ BlockDriverState *to_replace_bs; ++ int64_t bs_size, replace_size; ++ ++ bs_size = bdrv_getlength(bs); ++ if (bs_size < 0) { ++ error_setg_errno(errp, -bs_size, "Failed to query device's size"); ++ return; ++ } ++ ++ to_replace_bs = check_to_replace_node(bs, replaces, errp); ++ if (!to_replace_bs) { ++ return; ++ } ++ ++ replace_size = bdrv_getlength(to_replace_bs); ++ ++ if (replace_size < 0) { ++ error_setg_errno(errp, -replace_size, ++ "Failed to query the replacement node's size"); ++ return; ++ } ++ if (bs_size != replace_size) { ++ error_setg(errp, "cannot replace image with a mirror image of " ++ "different size"); ++ return; ++ } ++ } ++ ++ /* pass the node name to replace to mirror start since it's loose coupling ++ * and will allow to check whether the node still exist at mirror completion ++ */ ++ mirror_start(job_id, bs, target, ++ replaces, job_flags, ++ speed, granularity, buf_size, sync, backing_mode, zero_target, ++ on_source_error, on_target_error, unmap, filter_node_name, ++ copy_mode, errp); ++} ++ ++void qmp_drive_mirror(DriveMirror *arg, Error **errp) ++{ ++ BlockDriverState *bs; ++ BlockDriverState *target_backing_bs, *target_bs; ++ AioContext *aio_context; ++ BlockMirrorBackingMode backing_mode; ++ Error *local_err = NULL; ++ QDict *options = NULL; ++ int flags; ++ int64_t size; ++ const char *format = arg->format; ++ bool zero_target; ++ int ret; ++ ++ bs = qmp_get_root_bs(arg->device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ /* Early check to avoid creating target */ ++ bdrv_graph_rdlock_main_loop(); ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) { ++ bdrv_graph_rdunlock_main_loop(); ++ return; ++ } ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ if (!arg->has_mode) { ++ arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; ++ } ++ ++ if (!arg->format) { ++ format = (arg->mode == NEW_IMAGE_MODE_EXISTING ++ ? NULL : bs->drv->format_name); ++ } ++ ++ flags = bs->open_flags | BDRV_O_RDWR; ++ target_backing_bs = bdrv_cow_bs(bdrv_skip_filters(bs)); ++ if (!target_backing_bs && arg->sync == MIRROR_SYNC_MODE_TOP) { ++ arg->sync = MIRROR_SYNC_MODE_FULL; ++ } ++ if (arg->sync == MIRROR_SYNC_MODE_NONE) { ++ target_backing_bs = bs; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ size = bdrv_getlength(bs); ++ if (size < 0) { ++ error_setg_errno(errp, -size, "bdrv_getlength failed"); ++ return; ++ } ++ ++ if (arg->replaces) { ++ if (!arg->node_name) { ++ error_setg(errp, "a node-name must be provided when replacing a" ++ " named node of the graph"); ++ return; ++ } ++ } ++ ++ if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) { ++ backing_mode = MIRROR_SOURCE_BACKING_CHAIN; ++ } else { ++ backing_mode = MIRROR_OPEN_BACKING_CHAIN; ++ } ++ ++ /* Don't open backing image in create() */ ++ flags |= BDRV_O_NO_BACKING; ++ ++ if ((arg->sync == MIRROR_SYNC_MODE_FULL || !target_backing_bs) ++ && arg->mode != NEW_IMAGE_MODE_EXISTING) ++ { ++ /* create new image w/o backing file */ ++ assert(format); ++ bdrv_img_create(arg->target, format, ++ NULL, NULL, NULL, size, flags, false, &local_err); ++ } else { ++ BlockDriverState *explicit_backing; ++ ++ switch (arg->mode) { ++ case NEW_IMAGE_MODE_EXISTING: ++ break; ++ case NEW_IMAGE_MODE_ABSOLUTE_PATHS: ++ /* ++ * Create new image with backing file. ++ * Implicit filters should not appear in the filename. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ explicit_backing = bdrv_skip_implicit_filters(target_backing_bs); ++ bdrv_refresh_filename(explicit_backing); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ bdrv_img_create(arg->target, format, ++ explicit_backing->filename, ++ explicit_backing->drv->format_name, ++ NULL, size, flags, false, &local_err); ++ break; ++ default: ++ abort(); ++ } ++ } ++ ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ options = qdict_new(); ++ if (arg->node_name) { ++ qdict_put_str(options, "node-name", arg->node_name); ++ } ++ if (format) { ++ qdict_put_str(options, "driver", format); ++ } ++ ++ /* Mirroring takes care of copy-on-write using the source's backing ++ * file. ++ */ ++ target_bs = bdrv_open(arg->target, NULL, options, flags, errp); ++ if (!target_bs) { ++ return; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && ++ (arg->mode == NEW_IMAGE_MODE_EXISTING || ++ !bdrv_has_zero_init(target_bs))); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ ++ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); ++ if (ret < 0) { ++ bdrv_unref(target_bs); ++ return; ++ } ++ ++ blockdev_mirror_common(arg->job_id, bs, target_bs, ++ arg->replaces, arg->sync, ++ backing_mode, zero_target, ++ arg->has_speed, arg->speed, ++ arg->has_granularity, arg->granularity, ++ arg->has_buf_size, arg->buf_size, ++ arg->has_on_source_error, arg->on_source_error, ++ arg->has_on_target_error, arg->on_target_error, ++ arg->has_unmap, arg->unmap, ++ NULL, ++ arg->has_copy_mode, arg->copy_mode, ++ arg->has_auto_finalize, arg->auto_finalize, ++ arg->has_auto_dismiss, arg->auto_dismiss, ++ errp); ++ bdrv_unref(target_bs); ++} ++ ++void qmp_blockdev_mirror(const char *job_id, ++ const char *device, const char *target, ++ const char *replaces, ++ MirrorSyncMode sync, ++ bool has_speed, int64_t speed, ++ bool has_granularity, uint32_t granularity, ++ bool has_buf_size, int64_t buf_size, ++ bool has_on_source_error, ++ BlockdevOnError on_source_error, ++ bool has_on_target_error, ++ BlockdevOnError on_target_error, ++ const char *filter_node_name, ++ bool has_copy_mode, MirrorCopyMode copy_mode, ++ bool has_auto_finalize, bool auto_finalize, ++ bool has_auto_dismiss, bool auto_dismiss, ++ Error **errp) ++{ ++ BlockDriverState *bs; ++ BlockDriverState *target_bs; ++ AioContext *aio_context; ++ BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN; ++ bool zero_target; ++ int ret; ++ ++ bs = qmp_get_root_bs(device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ target_bs = bdrv_lookup_bs(target, target, errp); ++ if (!target_bs) { ++ return; ++ } ++ ++ zero_target = (sync == MIRROR_SYNC_MODE_FULL); ++ ++ aio_context = bdrv_get_aio_context(bs); ++ ++ ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); ++ if (ret < 0) { ++ return; ++ } ++ ++ blockdev_mirror_common(job_id, bs, target_bs, ++ replaces, sync, backing_mode, ++ zero_target, has_speed, speed, ++ has_granularity, granularity, ++ has_buf_size, buf_size, ++ has_on_source_error, on_source_error, ++ has_on_target_error, on_target_error, ++ true, true, filter_node_name, ++ has_copy_mode, copy_mode, ++ has_auto_finalize, auto_finalize, ++ has_auto_dismiss, auto_dismiss, ++ errp); ++} ++ ++/* ++ * Get a block job using its ID. Called with job_mutex held. ++ */ ++static BlockJob *find_block_job_locked(const char *id, Error **errp) ++{ ++ BlockJob *job; ++ ++ assert(id != NULL); ++ ++ job = block_job_get_locked(id); ++ ++ if (!job) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE, ++ "Block job '%s' not found", id); ++ return NULL; ++ } ++ ++ return job; ++} ++ ++void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ block_job_set_speed_locked(job, speed, errp); ++} ++ ++void qmp_block_job_cancel(const char *device, ++ bool has_force, bool force, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ if (!has_force) { ++ force = false; ++ } ++ ++ if (job_user_paused_locked(&job->job) && !force) { ++ error_setg(errp, "The block job for device '%s' is currently paused", ++ device); ++ return; ++ } ++ ++ trace_qmp_block_job_cancel(job); ++ job_user_cancel_locked(&job->job, force, errp); ++} ++ ++void qmp_block_job_pause(const char *device, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_block_job_pause(job); ++ job_user_pause_locked(&job->job, errp); ++} ++ ++void qmp_block_job_resume(const char *device, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_block_job_resume(job); ++ job_user_resume_locked(&job->job, errp); ++} ++ ++void qmp_block_job_complete(const char *device, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(device, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_block_job_complete(job); ++ job_complete_locked(&job->job, errp); ++} ++ ++void qmp_block_job_finalize(const char *id, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_block_job_finalize(job); ++ job_ref_locked(&job->job); ++ job_finalize_locked(&job->job, errp); ++ ++ job_unref_locked(&job->job); ++} ++ ++void qmp_block_job_dismiss(const char *id, Error **errp) ++{ ++ BlockJob *bjob; ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ bjob = find_block_job_locked(id, errp); ++ ++ if (!bjob) { ++ return; ++ } ++ ++ trace_qmp_block_job_dismiss(bjob); ++ job = &bjob->job; ++ job_dismiss_locked(&job, errp); ++} ++ ++void qmp_block_job_change(BlockJobChangeOptions *opts, Error **errp) ++{ ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_block_job_locked(opts->id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ block_job_change_locked(job, opts, errp); ++} ++ ++void qmp_change_backing_file(const char *device, ++ const char *image_node_name, ++ const char *backing_file, ++ Error **errp) ++{ ++ BlockDriverState *bs = NULL; ++ BlockDriverState *image_bs = NULL; ++ Error *local_err = NULL; ++ bool ro; ++ int ret; ++ ++ bs = qmp_get_root_bs(device, errp); ++ if (!bs) { ++ return; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ ++ image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ goto out_rdlock; ++ } ++ ++ if (!image_bs) { ++ error_setg(errp, "image file not found"); ++ goto out_rdlock; ++ } ++ ++ if (bdrv_find_base(image_bs) == image_bs) { ++ error_setg(errp, "not allowing backing file change on an image " ++ "without a backing file"); ++ goto out_rdlock; ++ } ++ ++ /* even though we are not necessarily operating on bs, we need it to ++ * determine if block ops are currently prohibited on the chain */ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) { ++ goto out_rdlock; ++ } ++ ++ /* final sanity check */ ++ if (!bdrv_chain_contains(bs, image_bs)) { ++ error_setg(errp, "'%s' and image file are not in the same chain", ++ device); ++ goto out_rdlock; ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ /* if not r/w, reopen to make r/w */ ++ ro = bdrv_is_read_only(image_bs); ++ ++ if (ro) { ++ if (bdrv_reopen_set_read_only(image_bs, false, errp) != 0) { ++ return; ++ } ++ } ++ ++ ret = bdrv_change_backing_file(image_bs, backing_file, ++ image_bs->drv ? image_bs->drv->format_name : "", ++ false); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not change backing file to '%s'", ++ backing_file); ++ /* don't exit here, so we can try to restore open flags if ++ * appropriate */ ++ } ++ ++ if (ro) { ++ bdrv_reopen_set_read_only(image_bs, true, errp); ++ } ++ return; ++ ++out_rdlock: ++ bdrv_graph_rdunlock_main_loop(); ++} ++ ++void qmp_blockdev_add(BlockdevOptions *options, Error **errp) ++{ ++ BlockDriverState *bs; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ QDict *qdict; ++ ++ visit_type_BlockdevOptions(v, NULL, &options, &error_abort); ++ visit_complete(v, &obj); ++ qdict = qobject_to(QDict, obj); ++ ++ qdict_flatten(qdict); ++ ++ if (!qdict_get_try_str(qdict, "node-name")) { ++ error_setg(errp, "'node-name' must be specified for the root node"); ++ goto fail; ++ } ++ ++ bs = bds_tree_init(qdict, errp); ++ if (!bs) { ++ goto fail; ++ } ++ ++ bdrv_set_monitor_owned(bs); ++ ++fail: ++ visit_free(v); ++} ++ ++void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp) ++{ ++ BlockReopenQueue *queue = NULL; ++ ++ /* Add each one of the BDS that we want to reopen to the queue */ ++ for (; reopen_list != NULL; reopen_list = reopen_list->next) { ++ BlockdevOptions *options = reopen_list->value; ++ BlockDriverState *bs; ++ QObject *obj; ++ Visitor *v; ++ QDict *qdict; ++ ++ /* Check for the selected node name */ ++ if (!options->node_name) { ++ error_setg(errp, "node-name not specified"); ++ goto fail; ++ } ++ ++ bs = bdrv_find_node(options->node_name); ++ if (!bs) { ++ error_setg(errp, "Failed to find node with node-name='%s'", ++ options->node_name); ++ goto fail; ++ } ++ ++ /* Put all options in a QDict and flatten it */ ++ v = qobject_output_visitor_new(&obj); ++ visit_type_BlockdevOptions(v, NULL, &options, &error_abort); ++ visit_complete(v, &obj); ++ visit_free(v); ++ ++ qdict = qobject_to(QDict, obj); ++ ++ qdict_flatten(qdict); ++ ++ queue = bdrv_reopen_queue(queue, bs, qdict, false); ++ } ++ ++ /* Perform the reopen operation */ ++ bdrv_reopen_multiple(queue, errp); ++ queue = NULL; ++ ++fail: ++ bdrv_reopen_queue_free(queue); ++} ++ ++void qmp_blockdev_del(const char *node_name, Error **errp) ++{ ++ BlockDriverState *bs; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_find_node(node_name); ++ if (!bs) { ++ error_setg(errp, "Failed to find node with node-name='%s'", node_name); ++ return; ++ } ++ if (bdrv_has_blk(bs)) { ++ error_setg(errp, "Node %s is in use", node_name); ++ return; ++ } ++ ++ if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, errp)) { ++ return; ++ } ++ ++ if (!QTAILQ_IN_USE(bs, monitor_list)) { ++ error_setg(errp, "Node %s is not owned by the monitor", ++ bs->node_name); ++ return; ++ } ++ ++ if (bs->refcnt > 1) { ++ error_setg(errp, "Block device %s is in use", ++ bdrv_get_device_or_node_name(bs)); ++ return; ++ } ++ ++ QTAILQ_REMOVE(&monitor_bdrv_states, bs, monitor_list); ++ bdrv_unref(bs); ++} ++ ++static BdrvChild * GRAPH_RDLOCK ++bdrv_find_child(BlockDriverState *parent_bs, const char *child_name) ++{ ++ BdrvChild *child; ++ ++ QLIST_FOREACH(child, &parent_bs->children, next) { ++ if (strcmp(child->name, child_name) == 0) { ++ return child; ++ } ++ } ++ ++ return NULL; ++} ++ ++void qmp_x_blockdev_change(const char *parent, const char *child, ++ const char *node, Error **errp) ++{ ++ BlockDriverState *parent_bs, *new_bs = NULL; ++ BdrvChild *p_child; ++ ++ bdrv_graph_wrlock(); ++ ++ parent_bs = bdrv_lookup_bs(parent, parent, errp); ++ if (!parent_bs) { ++ goto out; ++ } ++ ++ if (!child == !node) { ++ if (child) { ++ error_setg(errp, "The parameters child and node are in conflict"); ++ } else { ++ error_setg(errp, "Either child or node must be specified"); ++ } ++ goto out; ++ } ++ ++ if (child) { ++ p_child = bdrv_find_child(parent_bs, child); ++ if (!p_child) { ++ error_setg(errp, "Node '%s' does not have child '%s'", ++ parent, child); ++ goto out; ++ } ++ bdrv_del_child(parent_bs, p_child, errp); ++ } ++ ++ if (node) { ++ new_bs = bdrv_find_node(node); ++ if (!new_bs) { ++ error_setg(errp, "Node '%s' not found", node); ++ goto out; ++ } ++ bdrv_add_child(parent_bs, new_bs, errp); ++ } ++ ++out: ++ bdrv_graph_wrunlock(); ++} ++ ++BlockJobInfoList *qmp_query_block_jobs(Error **errp) ++{ ++ BlockJobInfoList *head = NULL, **tail = &head; ++ BlockJob *job; ++ ++ JOB_LOCK_GUARD(); ++ ++ for (job = block_job_next_locked(NULL); job; ++ job = block_job_next_locked(job)) { ++ BlockJobInfo *value; ++ ++ if (block_job_is_internal(job)) { ++ continue; ++ } ++ value = block_job_query_locked(job, errp); ++ if (!value) { ++ qapi_free_BlockJobInfoList(head); ++ return NULL; ++ } ++ QAPI_LIST_APPEND(tail, value); ++ } ++ ++ return head; ++} ++ ++void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, ++ bool has_force, bool force, Error **errp) ++{ ++ AioContext *new_context; ++ BlockDriverState *bs; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ bs = bdrv_find_node(node_name); ++ if (!bs) { ++ error_setg(errp, "Failed to find node with node-name='%s'", node_name); ++ return; ++ } ++ ++ /* Protects against accidents. */ ++ if (!(has_force && force) && bdrv_has_blk(bs)) { ++ error_setg(errp, "Node %s is associated with a BlockBackend and could " ++ "be in use (use force=true to override this check)", ++ node_name); ++ return; ++ } ++ ++ if (iothread->type == QTYPE_QSTRING) { ++ IOThread *obj = iothread_by_id(iothread->u.s); ++ if (!obj) { ++ error_setg(errp, "Cannot find iothread %s", iothread->u.s); ++ return; ++ } ++ ++ new_context = iothread_get_aio_context(obj); ++ } else { ++ new_context = qemu_get_aio_context(); ++ } ++ ++ bdrv_try_change_aio_context(bs, new_context, NULL, errp); ++} ++ ++QemuOptsList qemu_common_drive_opts = { ++ .name = "drive", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), ++ .desc = { ++ { ++ .name = "snapshot", ++ .type = QEMU_OPT_BOOL, ++ .help = "enable/disable snapshot mode", ++ },{ ++ .name = "aio", ++ .type = QEMU_OPT_STRING, ++ .help = "host AIO implementation (threads, native, io_uring)", ++ },{ ++ .name = BDRV_OPT_CACHE_WB, ++ .type = QEMU_OPT_BOOL, ++ .help = "Enable writeback mode", ++ },{ ++ .name = "format", ++ .type = QEMU_OPT_STRING, ++ .help = "disk format (raw, qcow2, ...)", ++ },{ ++ .name = "rerror", ++ .type = QEMU_OPT_STRING, ++ .help = "read error action", ++ },{ ++ .name = "werror", ++ .type = QEMU_OPT_STRING, ++ .help = "write error action", ++ },{ ++ .name = BDRV_OPT_READ_ONLY, ++ .type = QEMU_OPT_BOOL, ++ .help = "open drive file as read-only", ++ }, ++ ++ THROTTLE_OPTS, ++ ++ { ++ .name = "throttling.group", ++ .type = QEMU_OPT_STRING, ++ .help = "name of the block throttling group", ++ },{ ++ .name = "copy-on-read", ++ .type = QEMU_OPT_BOOL, ++ .help = "copy read data from backing file into image file", ++ },{ ++ .name = "detect-zeroes", ++ .type = QEMU_OPT_STRING, ++ .help = "try to optimize zero writes (off, on, unmap)", ++ },{ ++ .name = "stats-account-invalid", ++ .type = QEMU_OPT_BOOL, ++ .help = "whether to account for invalid I/O operations " ++ "in the statistics", ++ },{ ++ .name = "stats-account-failed", ++ .type = QEMU_OPT_BOOL, ++ .help = "whether to account for failed I/O operations " ++ "in the statistics", ++ }, ++ { /* end of list */ } ++ }, ++}; ++ ++QemuOptsList qemu_drive_opts = { ++ .name = "drive", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), ++ .desc = { ++ /* ++ * no elements => accept any params ++ * validation will happen later ++ */ ++ { /* end of list */ } ++ }, ++}; +diff --git a/qcow2/lib/blockjob.c b/qcow2/lib/blockjob.c +new file mode 100644 +index 00000000..d5f29e14 +--- /dev/null ++++ b/qcow2/lib/blockjob.c +@@ -0,0 +1,630 @@ ++/* ++ * QEMU System Emulator block driver ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/aio-wait.h" ++#include "block/block.h" ++#include "block/blockjob_int.h" ++#include "block/block_int.h" ++#include "block/trace.h" ++#include "sysemu/block-backend.h" ++#include "qapi/error.h" ++#include "qapi/qapi-events-block-core.h" ++#include "qapi/qmp/qerror.h" ++#include "qemu/main-loop.h" ++#include "qemu/timer.h" ++ ++static bool is_block_job(Job *job) ++{ ++ return job_type(job) == JOB_TYPE_BACKUP || ++ job_type(job) == JOB_TYPE_COMMIT || ++ job_type(job) == JOB_TYPE_MIRROR || ++ job_type(job) == JOB_TYPE_STREAM; ++} ++ ++BlockJob *block_job_next_locked(BlockJob *bjob) ++{ ++ Job *job = bjob ? &bjob->job : NULL; ++ GLOBAL_STATE_CODE(); ++ ++ do { ++ job = job_next_locked(job); ++ } while (job && !is_block_job(job)); ++ ++ return job ? container_of(job, BlockJob, job) : NULL; ++} ++ ++BlockJob *block_job_get_locked(const char *id) ++{ ++ Job *job = job_get_locked(id); ++ GLOBAL_STATE_CODE(); ++ ++ if (job && is_block_job(job)) { ++ return container_of(job, BlockJob, job); ++ } else { ++ return NULL; ++ } ++} ++ ++BlockJob *block_job_get(const char *id) ++{ ++ JOB_LOCK_GUARD(); ++ return block_job_get_locked(id); ++} ++ ++void block_job_free(Job *job) ++{ ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ GLOBAL_STATE_CODE(); ++ ++ block_job_remove_all_bdrv(bjob); ++ ratelimit_destroy(&bjob->limit); ++ error_free(bjob->blocker); ++} ++ ++static char *child_job_get_parent_desc(BdrvChild *c) ++{ ++ BlockJob *job = c->opaque; ++ return g_strdup_printf("%s job '%s'", job_type_str(&job->job), job->job.id); ++} ++ ++static void child_job_drained_begin(BdrvChild *c) ++{ ++ BlockJob *job = c->opaque; ++ job_pause(&job->job); ++} ++ ++static bool child_job_drained_poll(BdrvChild *c) ++{ ++ BlockJob *bjob = c->opaque; ++ Job *job = &bjob->job; ++ const BlockJobDriver *drv = block_job_driver(bjob); ++ ++ /* An inactive or completed job doesn't have any pending requests. Jobs ++ * with !job->busy are either already paused or have a pause point after ++ * being reentered, so no job driver code will run before they pause. */ ++ WITH_JOB_LOCK_GUARD() { ++ if (!job->busy || job_is_completed_locked(job)) { ++ return false; ++ } ++ } ++ ++ /* Otherwise, assume that it isn't fully stopped yet, but allow the job to ++ * override this assumption. */ ++ if (drv->drained_poll) { ++ return drv->drained_poll(bjob); ++ } else { ++ return true; ++ } ++} ++ ++static void child_job_drained_end(BdrvChild *c) ++{ ++ BlockJob *job = c->opaque; ++ 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 AioContext *child_job_get_parent_aio_context(BdrvChild *c) ++{ ++ BlockJob *job = c->opaque; ++ IO_CODE(); ++ JOB_LOCK_GUARD(); ++ ++ return job->job.aio_context; ++} ++ ++static const BdrvChildClass child_job = { ++ .get_parent_desc = child_job_get_parent_desc, ++ .drained_begin = child_job_drained_begin, ++ .drained_poll = child_job_drained_poll, ++ .drained_end = child_job_drained_end, ++ .change_aio_ctx = child_job_change_aio_ctx, ++ .stay_at_node = true, ++ .get_parent_aio_context = child_job_get_parent_aio_context, ++}; ++ ++void block_job_remove_all_bdrv(BlockJob *job) ++{ ++ GLOBAL_STATE_CODE(); ++ /* ++ * bdrv_root_unref_child() may reach child_job_[can_]set_aio_ctx(), ++ * which will also traverse job->nodes, so consume the list one by ++ * one to make sure that such a concurrent access does not attempt ++ * to process an already freed BdrvChild. ++ */ ++ bdrv_graph_wrlock(); ++ while (job->nodes) { ++ GSList *l = job->nodes; ++ BdrvChild *c = l->data; ++ ++ job->nodes = l->next; ++ ++ bdrv_op_unblock_all(c->bs, job->blocker); ++ bdrv_root_unref_child(c); ++ ++ g_slist_free_1(l); ++ } ++ bdrv_graph_wrunlock(); ++} ++ ++bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) ++{ ++ GSList *el; ++ GLOBAL_STATE_CODE(); ++ ++ for (el = job->nodes; el; el = el->next) { ++ BdrvChild *c = el->data; ++ if (c->bs == bs) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, ++ uint64_t perm, uint64_t shared_perm, Error **errp) ++{ ++ BdrvChild *c; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_ref(bs); ++ ++ c = bdrv_root_attach_child(bs, name, &child_job, 0, perm, shared_perm, job, ++ errp); ++ if (c == NULL) { ++ return -EPERM; ++ } ++ ++ job->nodes = g_slist_prepend(job->nodes, c); ++ bdrv_op_block_all(bs, job->blocker); ++ ++ return 0; ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_on_idle_locked(Notifier *n, void *opaque) ++{ ++ aio_wait_kick(); ++} ++ ++bool block_job_is_internal(BlockJob *job) ++{ ++ return (job->job.id == NULL); ++} ++ ++const BlockJobDriver *block_job_driver(BlockJob *job) ++{ ++ return container_of(job->job.driver, BlockJobDriver, job_driver); ++} ++ ++/* Assumes the job_mutex is held */ ++static bool job_timer_pending(Job *job) ++{ ++ return timer_pending(&job->sleep_timer); ++} ++ ++bool block_job_set_speed_locked(BlockJob *job, int64_t speed, Error **errp) ++{ ++ const BlockJobDriver *drv = block_job_driver(job); ++ int64_t old_speed = job->speed; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (job_apply_verb_locked(&job->job, JOB_VERB_SET_SPEED, errp) < 0) { ++ return false; ++ } ++ if (speed < 0) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "speed", ++ "a non-negative value"); ++ return false; ++ } ++ ++ ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME); ++ ++ job->speed = speed; ++ ++ if (drv->set_speed) { ++ job_unlock(); ++ drv->set_speed(job, speed); ++ job_lock(); ++ } ++ ++ if (speed && speed <= old_speed) { ++ return true; ++ } ++ ++ /* kick only if a timer is pending */ ++ job_enter_cond_locked(&job->job, job_timer_pending); ++ ++ return true; ++} ++ ++static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) ++{ ++ JOB_LOCK_GUARD(); ++ return block_job_set_speed_locked(job, speed, errp); ++} ++ ++void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, ++ Error **errp) ++{ ++ const BlockJobDriver *drv = block_job_driver(job); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (job_apply_verb_locked(&job->job, JOB_VERB_CHANGE, errp)) { ++ return; ++ } ++ ++ if (drv->change) { ++ job_unlock(); ++ drv->change(job, opts, errp); ++ job_lock(); ++ } else { ++ error_setg(errp, "Job type does not support change"); ++ } ++} ++ ++void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) ++{ ++ IO_CODE(); ++ ratelimit_calculate_delay(&job->limit, n); ++} ++ ++void block_job_ratelimit_sleep(BlockJob *job) ++{ ++ uint64_t delay_ns; ++ ++ /* ++ * Sleep at least once. If the job is reentered early, keep waiting until ++ * we've waited for the full time that is necessary to keep the job at the ++ * right speed. ++ * ++ * Make sure to recalculate the delay after each (possibly interrupted) ++ * sleep because the speed can change while the job has yielded. ++ */ ++ do { ++ delay_ns = ratelimit_calculate_delay(&job->limit, 0); ++ job_sleep_ns(&job->job, delay_ns); ++ } while (delay_ns && !job_is_cancelled(&job->job)); ++} ++ ++BlockJobInfo *block_job_query_locked(BlockJob *job, Error **errp) ++{ ++ BlockJobInfo *info; ++ uint64_t progress_current, progress_total; ++ const BlockJobDriver *drv = block_job_driver(job); ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (block_job_is_internal(job)) { ++ error_setg(errp, "Cannot query QEMU internal jobs"); ++ return NULL; ++ } ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ ++ info = g_new0(BlockJobInfo, 1); ++ info->type = job_type(&job->job); ++ info->device = g_strdup(job->job.id); ++ info->busy = job->job.busy; ++ info->paused = job->job.pause_count > 0; ++ info->offset = progress_current; ++ info->len = progress_total; ++ info->speed = job->speed; ++ info->io_status = job->iostatus; ++ info->ready = job_is_ready_locked(&job->job), ++ info->status = job->job.status; ++ info->auto_finalize = job->job.auto_finalize; ++ info->auto_dismiss = job->job.auto_dismiss; ++ if (job->job.ret) { ++ info->error = job->job.err ? ++ g_strdup(error_get_pretty(job->job.err)) : ++ g_strdup(strerror(-job->job.ret)); ++ } ++ if (drv->query) { ++ job_unlock(); ++ drv->query(job, info); ++ job_lock(); ++ } ++ return info; ++} ++ ++/* Called with job lock held */ ++static void block_job_iostatus_set_err_locked(BlockJob *job, int error) ++{ ++ if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { ++ job->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : ++ BLOCK_DEVICE_IO_STATUS_FAILED; ++ } ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_event_cancelled_locked(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ uint64_t progress_current, progress_total; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ ++ qapi_event_send_block_job_cancelled(job_type(&job->job), ++ job->job.id, ++ progress_total, ++ progress_current, ++ job->speed); ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_event_completed_locked(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ const char *msg = NULL; ++ uint64_t progress_current, progress_total; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ if (job->job.ret < 0) { ++ msg = error_get_pretty(job->job.err); ++ } ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ ++ qapi_event_send_block_job_completed(job_type(&job->job), ++ job->job.id, ++ progress_total, ++ progress_current, ++ job->speed, ++ msg); ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_event_pending_locked(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ qapi_event_send_block_job_pending(job_type(&job->job), ++ job->job.id); ++} ++ ++/* Called with job_mutex lock held. */ ++static void block_job_event_ready_locked(Notifier *n, void *opaque) ++{ ++ BlockJob *job = opaque; ++ uint64_t progress_current, progress_total; ++ ++ if (block_job_is_internal(job)) { ++ return; ++ } ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ ++ qapi_event_send_block_job_ready(job_type(&job->job), ++ job->job.id, ++ progress_total, ++ progress_current, ++ job->speed); ++} ++ ++ ++void *block_job_create(const char *job_id, const BlockJobDriver *driver, ++ JobTxn *txn, BlockDriverState *bs, uint64_t perm, ++ uint64_t shared_perm, int64_t speed, int flags, ++ BlockCompletionFunc *cb, void *opaque, Error **errp) ++{ ++ BlockJob *job; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ bdrv_graph_wrlock(); ++ ++ if (job_id == NULL && !(flags & JOB_INTERNAL)) { ++ job_id = bdrv_get_device_name(bs); ++ } ++ ++ job = job_create(job_id, &driver->job_driver, txn, bdrv_get_aio_context(bs), ++ flags, cb, opaque, errp); ++ if (job == NULL) { ++ bdrv_graph_wrunlock(); ++ return NULL; ++ } ++ ++ assert(is_block_job(&job->job)); ++ assert(job->job.driver->free == &block_job_free); ++ assert(job->job.driver->user_resume == &block_job_user_resume); ++ ++ ratelimit_init(&job->limit); ++ ++ job->finalize_cancelled_notifier.notify = block_job_event_cancelled_locked; ++ job->finalize_completed_notifier.notify = block_job_event_completed_locked; ++ job->pending_notifier.notify = block_job_event_pending_locked; ++ job->ready_notifier.notify = block_job_event_ready_locked; ++ job->idle_notifier.notify = block_job_on_idle_locked; ++ ++ WITH_JOB_LOCK_GUARD() { ++ notifier_list_add(&job->job.on_finalize_cancelled, ++ &job->finalize_cancelled_notifier); ++ notifier_list_add(&job->job.on_finalize_completed, ++ &job->finalize_completed_notifier); ++ notifier_list_add(&job->job.on_pending, &job->pending_notifier); ++ notifier_list_add(&job->job.on_ready, &job->ready_notifier); ++ notifier_list_add(&job->job.on_idle, &job->idle_notifier); ++ } ++ ++ error_setg(&job->blocker, "block device is in use by block job: %s", ++ job_type_str(&job->job)); ++ ++ ret = block_job_add_bdrv(job, "main node", bs, perm, shared_perm, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); ++ ++ if (!block_job_set_speed(job, speed, errp)) { ++ goto fail; ++ } ++ ++ bdrv_graph_wrunlock(); ++ return job; ++ ++fail: ++ bdrv_graph_wrunlock(); ++ job_early_fail(&job->job); ++ return NULL; ++} ++ ++void block_job_iostatus_reset_locked(BlockJob *job) ++{ ++ GLOBAL_STATE_CODE(); ++ if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { ++ return; ++ } ++ assert(job->job.user_paused && job->job.pause_count > 0); ++ job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; ++} ++ ++static void block_job_iostatus_reset(BlockJob *job) ++{ ++ JOB_LOCK_GUARD(); ++ block_job_iostatus_reset_locked(job); ++} ++ ++void block_job_user_resume(Job *job) ++{ ++ BlockJob *bjob = container_of(job, BlockJob, job); ++ GLOBAL_STATE_CODE(); ++ block_job_iostatus_reset(bjob); ++} ++ ++BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, ++ int is_read, int error) ++{ ++ BlockErrorAction action; ++ IO_CODE(); ++ ++ switch (on_err) { ++ case BLOCKDEV_ON_ERROR_ENOSPC: ++ case BLOCKDEV_ON_ERROR_AUTO: ++ action = (error == ENOSPC) ? ++ BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT; ++ break; ++ case BLOCKDEV_ON_ERROR_STOP: ++ action = BLOCK_ERROR_ACTION_STOP; ++ break; ++ case BLOCKDEV_ON_ERROR_REPORT: ++ action = BLOCK_ERROR_ACTION_REPORT; ++ break; ++ case BLOCKDEV_ON_ERROR_IGNORE: ++ action = BLOCK_ERROR_ACTION_IGNORE; ++ break; ++ default: ++ abort(); ++ } ++ if (!block_job_is_internal(job)) { ++ qapi_event_send_block_job_error(job->job.id, ++ is_read ? IO_OPERATION_TYPE_READ : ++ IO_OPERATION_TYPE_WRITE, ++ action); ++ } ++ if (action == BLOCK_ERROR_ACTION_STOP) { ++ WITH_JOB_LOCK_GUARD() { ++ if (!job->job.user_paused) { ++ job_pause_locked(&job->job); ++ /* ++ * make the pause user visible, which will be ++ * resumed from QMP. ++ */ ++ job->job.user_paused = true; ++ } ++ block_job_iostatus_set_err_locked(job, error); ++ } ++ } ++ return action; ++} ++ ++AioContext *block_job_get_aio_context(BlockJob *job) ++{ ++ GLOBAL_STATE_CODE(); ++ return job->job.aio_context; ++} +diff --git a/qcow2/lib/crypto/hash.c b/qcow2/lib/crypto/hash.c +new file mode 100644 +index 00000000..b0f8228b +--- /dev/null ++++ b/qcow2/lib/crypto/hash.c +@@ -0,0 +1,144 @@ ++/* ++ * QEMU Crypto hash algorithms ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "crypto/hash.h" ++#include "hashpriv.h" ++ ++static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { ++ [QCRYPTO_HASH_ALG_MD5] = 16, ++ [QCRYPTO_HASH_ALG_SHA1] = 20, ++ [QCRYPTO_HASH_ALG_SHA224] = 28, ++ [QCRYPTO_HASH_ALG_SHA256] = 32, ++ [QCRYPTO_HASH_ALG_SHA384] = 48, ++ [QCRYPTO_HASH_ALG_SHA512] = 64, ++ [QCRYPTO_HASH_ALG_RIPEMD160] = 20, ++}; ++ ++size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) ++{ ++ assert(alg < G_N_ELEMENTS(qcrypto_hash_alg_size)); ++ return qcrypto_hash_alg_size[alg]; ++} ++ ++int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ uint8_t **result, ++ size_t *resultlen, ++ Error **errp) ++{ ++#ifdef CONFIG_AF_ALG ++ int ret; ++ /* ++ * TODO: ++ * Maybe we should treat some afalg errors as fatal ++ */ ++ ret = qcrypto_hash_afalg_driver.hash_bytesv(alg, iov, niov, ++ result, resultlen, ++ NULL); ++ if (ret == 0) { ++ return ret; ++ } ++#endif ++ ++ return qcrypto_hash_lib_driver.hash_bytesv(alg, iov, niov, ++ result, resultlen, ++ errp); ++} ++ ++ ++int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ uint8_t **result, ++ size_t *resultlen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = (char *)buf, ++ .iov_len = len }; ++ return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp); ++} ++ ++static const char hex[] = "0123456789abcdef"; ++ ++int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ char **digest, ++ Error **errp) ++{ ++ uint8_t *result = NULL; ++ size_t resultlen = 0; ++ size_t i; ++ ++ if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { ++ return -1; ++ } ++ ++ *digest = g_new0(char, (resultlen * 2) + 1); ++ for (i = 0 ; i < resultlen ; i++) { ++ (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf]; ++ (*digest)[(i * 2) + 1] = hex[result[i] & 0xf]; ++ } ++ (*digest)[resultlen * 2] = '\0'; ++ g_free(result); ++ return 0; ++} ++ ++int qcrypto_hash_digest(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ char **digest, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; ++ ++ return qcrypto_hash_digestv(alg, &iov, 1, digest, errp); ++} ++ ++int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ char **base64, ++ Error **errp) ++{ ++ uint8_t *result = NULL; ++ size_t resultlen = 0; ++ ++ if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { ++ return -1; ++ } ++ ++ *base64 = g_base64_encode(result, resultlen); ++ g_free(result); ++ return 0; ++} ++ ++int qcrypto_hash_base64(QCryptoHashAlgorithm alg, ++ const char *buf, ++ size_t len, ++ char **base64, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; ++ ++ return qcrypto_hash_base64v(alg, &iov, 1, base64, errp); ++} +diff --git a/qcow2/lib/crypto/tlscreds.c b/qcow2/lib/crypto/tlscreds.c +new file mode 100644 +index 00000000..084ce0d5 +--- /dev/null ++++ b/qcow2/lib/crypto/tlscreds.c +@@ -0,0 +1,294 @@ ++/* ++ * QEMU crypto TLS credential support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-types-crypto.h" ++#include "qemu/module.h" ++#include "tlscredspriv.h" ++#include "trace.h" ++ ++#define DH_BITS 2048 ++ ++#ifdef CONFIG_GNUTLS ++int ++qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, ++ const char *filename, ++ gnutls_dh_params_t *dh_params, ++ Error **errp) ++{ ++ int ret; ++ ++ trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : ""); ++ ++ if (filename == NULL) { ++ ret = gnutls_dh_params_init(dh_params); ++ if (ret < 0) { ++ error_setg(errp, "Unable to initialize DH parameters: %s", ++ gnutls_strerror(ret)); ++ return -1; ++ } ++ ret = gnutls_dh_params_generate2(*dh_params, DH_BITS); ++ if (ret < 0) { ++ gnutls_dh_params_deinit(*dh_params); ++ *dh_params = NULL; ++ error_setg(errp, "Unable to generate DH parameters: %s", ++ gnutls_strerror(ret)); ++ return -1; ++ } ++ } else { ++ GError *gerr = NULL; ++ gchar *contents; ++ gsize len; ++ gnutls_datum_t data; ++ if (!g_file_get_contents(filename, ++ &contents, ++ &len, ++ &gerr)) { ++ ++ error_setg(errp, "%s", gerr->message); ++ g_error_free(gerr); ++ return -1; ++ } ++ data.data = (unsigned char *)contents; ++ data.size = len; ++ ret = gnutls_dh_params_init(dh_params); ++ if (ret < 0) { ++ g_free(contents); ++ error_setg(errp, "Unable to initialize DH parameters: %s", ++ gnutls_strerror(ret)); ++ return -1; ++ } ++ ret = gnutls_dh_params_import_pkcs3(*dh_params, ++ &data, ++ GNUTLS_X509_FMT_PEM); ++ g_free(contents); ++ if (ret < 0) { ++ gnutls_dh_params_deinit(*dh_params); ++ *dh_params = NULL; ++ error_setg(errp, "Unable to load DH parameters from %s: %s", ++ filename, gnutls_strerror(ret)); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++ ++int ++qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, ++ const char *filename, ++ bool required, ++ char **cred, ++ Error **errp) ++{ ++ struct stat sb; ++ int ret = -1; ++ ++ if (!creds->dir) { ++ if (required) { ++ error_setg(errp, "Missing 'dir' property value"); ++ return -1; ++ } else { ++ return 0; ++ } ++ } ++ ++ *cred = g_strdup_printf("%s/%s", creds->dir, filename); ++ ++ if (stat(*cred, &sb) < 0) { ++ if (errno == ENOENT && !required) { ++ ret = 0; ++ } else { ++ error_setg_errno(errp, errno, ++ "Unable to access credentials %s", ++ *cred); ++ } ++ g_free(*cred); ++ *cred = NULL; ++ goto cleanup; ++ } ++ ++ ret = 0; ++ cleanup: ++ trace_qcrypto_tls_creds_get_path(creds, filename, ++ *cred ? *cred : ""); ++ return ret; ++} ++ ++ ++#endif /* ! CONFIG_GNUTLS */ ++ ++ ++static void ++qcrypto_tls_creds_prop_set_verify(Object *obj, ++ bool value, ++ Error **errp G_GNUC_UNUSED) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ creds->verifyPeer = value; ++} ++ ++ ++static bool ++qcrypto_tls_creds_prop_get_verify(Object *obj, ++ Error **errp G_GNUC_UNUSED) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ return creds->verifyPeer; ++} ++ ++ ++static void ++qcrypto_tls_creds_prop_set_dir(Object *obj, ++ const char *value, ++ Error **errp G_GNUC_UNUSED) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ creds->dir = g_strdup(value); ++} ++ ++ ++static char * ++qcrypto_tls_creds_prop_get_dir(Object *obj, ++ Error **errp G_GNUC_UNUSED) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ return g_strdup(creds->dir); ++} ++ ++ ++static void ++qcrypto_tls_creds_prop_set_priority(Object *obj, ++ const char *value, ++ Error **errp G_GNUC_UNUSED) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ creds->priority = g_strdup(value); ++} ++ ++ ++static char * ++qcrypto_tls_creds_prop_get_priority(Object *obj, ++ Error **errp G_GNUC_UNUSED) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ return g_strdup(creds->priority); ++} ++ ++ ++static void ++qcrypto_tls_creds_prop_set_endpoint(Object *obj, ++ int value, ++ Error **errp G_GNUC_UNUSED) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ creds->endpoint = value; ++} ++ ++ ++static int ++qcrypto_tls_creds_prop_get_endpoint(Object *obj, ++ Error **errp G_GNUC_UNUSED) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ return creds->endpoint; ++} ++ ++ ++static void ++qcrypto_tls_creds_class_init(ObjectClass *oc, void *data) ++{ ++ object_class_property_add_bool(oc, "verify-peer", ++ qcrypto_tls_creds_prop_get_verify, ++ qcrypto_tls_creds_prop_set_verify); ++ object_class_property_add_str(oc, "dir", ++ qcrypto_tls_creds_prop_get_dir, ++ qcrypto_tls_creds_prop_set_dir); ++ object_class_property_add_enum(oc, "endpoint", ++ "QCryptoTLSCredsEndpoint", ++ &QCryptoTLSCredsEndpoint_lookup, ++ qcrypto_tls_creds_prop_get_endpoint, ++ qcrypto_tls_creds_prop_set_endpoint); ++ object_class_property_add_str(oc, "priority", ++ qcrypto_tls_creds_prop_get_priority, ++ qcrypto_tls_creds_prop_set_priority); ++} ++ ++ ++static void ++qcrypto_tls_creds_init(Object *obj) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ creds->verifyPeer = true; ++} ++ ++ ++static void ++qcrypto_tls_creds_finalize(Object *obj) ++{ ++ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); ++ ++ g_free(creds->dir); ++ g_free(creds->priority); ++} ++ ++bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds, ++ QCryptoTLSCredsEndpoint endpoint, ++ Error **errp) ++{ ++ if (creds->endpoint != endpoint) { ++ error_setg(errp, "Expected TLS credentials for a %s endpoint", ++ QCryptoTLSCredsEndpoint_str(endpoint)); ++ return false; ++ } ++ return true; ++} ++ ++static const TypeInfo qcrypto_tls_creds_info = { ++ .parent = TYPE_OBJECT, ++ .name = TYPE_QCRYPTO_TLS_CREDS, ++ .instance_size = sizeof(QCryptoTLSCreds), ++ .instance_init = qcrypto_tls_creds_init, ++ .instance_finalize = qcrypto_tls_creds_finalize, ++ .class_init = qcrypto_tls_creds_class_init, ++ .class_size = sizeof(QCryptoTLSCredsClass), ++ .abstract = true, ++}; ++ ++ ++static void ++qcrypto_tls_creds_register_types(void) ++{ ++ type_register_static(&qcrypto_tls_creds_info); ++} ++ ++ ++type_init(qcrypto_tls_creds_register_types); +diff --git a/qcow2/lib/crypto/tlscredspriv.h b/qcow2/lib/crypto/tlscredspriv.h +new file mode 100644 +index 00000000..df9815a2 +--- /dev/null ++++ b/qcow2/lib/crypto/tlscredspriv.h +@@ -0,0 +1,86 @@ ++/* ++ * QEMU crypto TLS credential support private helpers ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#ifndef QCRYPTO_TLSCREDSPRIV_H ++#define QCRYPTO_TLSCREDSPRIV_H ++ ++#include "crypto/tlscreds.h" ++ ++#ifdef CONFIG_GNUTLS ++#include ++#endif ++ ++struct QCryptoTLSCreds { ++ Object parent_obj; ++ char *dir; ++ QCryptoTLSCredsEndpoint endpoint; ++#ifdef CONFIG_GNUTLS ++ gnutls_dh_params_t dh_params; ++#endif ++ bool verifyPeer; ++ char *priority; ++}; ++ ++struct QCryptoTLSCredsAnon { ++ QCryptoTLSCreds parent_obj; ++#ifdef CONFIG_GNUTLS ++ union { ++ gnutls_anon_server_credentials_t server; ++ gnutls_anon_client_credentials_t client; ++ } data; ++#endif ++}; ++ ++struct QCryptoTLSCredsPSK { ++ QCryptoTLSCreds parent_obj; ++ char *username; ++#ifdef CONFIG_GNUTLS ++ union { ++ gnutls_psk_server_credentials_t server; ++ gnutls_psk_client_credentials_t client; ++ } data; ++#endif ++}; ++ ++struct QCryptoTLSCredsX509 { ++ QCryptoTLSCreds parent_obj; ++#ifdef CONFIG_GNUTLS ++ gnutls_certificate_credentials_t data; ++#endif ++ bool sanityCheck; ++ char *passwordid; ++}; ++ ++#ifdef CONFIG_GNUTLS ++ ++int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, ++ const char *filename, ++ bool required, ++ char **cred, ++ Error **errp); ++ ++int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, ++ const char *filename, ++ gnutls_dh_params_t *dh_params, ++ Error **errp); ++ ++#endif ++ ++#endif /* QCRYPTO_TLSCREDSPRIV_H */ +diff --git a/qcow2/lib/crypto/tlssession.c b/qcow2/lib/crypto/tlssession.c +new file mode 100644 +index 00000000..77286e23 +--- /dev/null ++++ b/qcow2/lib/crypto/tlssession.c +@@ -0,0 +1,717 @@ ++/* ++ * QEMU crypto TLS session support ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "crypto/tlssession.h" ++#include "crypto/tlscredsanon.h" ++#include "crypto/tlscredspsk.h" ++#include "crypto/tlscredsx509.h" ++#include "qapi/error.h" ++#include "authz/base.h" ++#include "tlscredspriv.h" ++#include "trace.h" ++ ++#ifdef CONFIG_GNUTLS ++ ++ ++#include ++ ++ ++struct QCryptoTLSSession { ++ QCryptoTLSCreds *creds; ++ gnutls_session_t handle; ++ char *hostname; ++ char *authzid; ++ bool handshakeComplete; ++ QCryptoTLSSessionWriteFunc writeFunc; ++ QCryptoTLSSessionReadFunc readFunc; ++ void *opaque; ++ char *peername; ++ ++ /* ++ * Allow concurrent reads and writes, so track ++ * errors separately ++ */ ++ Error *rerr; ++ Error *werr; ++}; ++ ++ ++void ++qcrypto_tls_session_free(QCryptoTLSSession *session) ++{ ++ if (!session) { ++ return; ++ } ++ ++ error_free(session->rerr); ++ error_free(session->werr); ++ ++ gnutls_deinit(session->handle); ++ g_free(session->hostname); ++ g_free(session->peername); ++ g_free(session->authzid); ++ object_unref(OBJECT(session->creds)); ++ g_free(session); ++} ++ ++ ++static ssize_t ++qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) ++{ ++ QCryptoTLSSession *session = opaque; ++ ssize_t ret; ++ ++ if (!session->writeFunc) { ++ errno = EIO; ++ return -1; ++ }; ++ ++ error_free(session->werr); ++ session->werr = NULL; ++ ++ ret = session->writeFunc(buf, len, session->opaque, &session->werr); ++ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { ++ errno = EAGAIN; ++ return -1; ++ } else if (ret < 0) { ++ errno = EIO; ++ return -1; ++ } else { ++ return ret; ++ } ++} ++ ++ ++static ssize_t ++qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) ++{ ++ QCryptoTLSSession *session = opaque; ++ ssize_t ret; ++ ++ if (!session->readFunc) { ++ errno = EIO; ++ return -1; ++ }; ++ ++ error_free(session->rerr); ++ session->rerr = NULL; ++ ++ ret = session->readFunc(buf, len, session->opaque, &session->rerr); ++ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { ++ errno = EAGAIN; ++ return -1; ++ } else if (ret < 0) { ++ errno = EIO; ++ return -1; ++ } else { ++ return ret; ++ } ++} ++ ++#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" ++#define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK" ++ ++QCryptoTLSSession * ++qcrypto_tls_session_new(QCryptoTLSCreds *creds, ++ const char *hostname, ++ const char *authzid, ++ QCryptoTLSCredsEndpoint endpoint, ++ Error **errp) ++{ ++ QCryptoTLSSession *session; ++ int ret; ++ ++ session = g_new0(QCryptoTLSSession, 1); ++ trace_qcrypto_tls_session_new( ++ session, creds, hostname ? hostname : "", ++ authzid ? authzid : "", endpoint); ++ ++ if (hostname) { ++ session->hostname = g_strdup(hostname); ++ } ++ if (authzid) { ++ session->authzid = g_strdup(authzid); ++ } ++ session->creds = creds; ++ object_ref(OBJECT(creds)); ++ ++ if (creds->endpoint != endpoint) { ++ error_setg(errp, "Credentials endpoint doesn't match session"); ++ goto error; ++ } ++ ++ if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { ++ ret = gnutls_init(&session->handle, GNUTLS_SERVER); ++ } else { ++ ret = gnutls_init(&session->handle, GNUTLS_CLIENT); ++ } ++ if (ret < 0) { ++ error_setg(errp, "Cannot initialize TLS session: %s", ++ gnutls_strerror(ret)); ++ goto error; ++ } ++ ++ if (object_dynamic_cast(OBJECT(creds), ++ TYPE_QCRYPTO_TLS_CREDS_ANON)) { ++ QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); ++ char *prio; ++ ++ if (creds->priority != NULL) { ++ prio = g_strdup_printf("%s:%s", ++ creds->priority, ++ TLS_PRIORITY_ADDITIONAL_ANON); ++ } else { ++ prio = g_strdup(CONFIG_TLS_PRIORITY ":" ++ TLS_PRIORITY_ADDITIONAL_ANON); ++ } ++ ++ ret = gnutls_priority_set_direct(session->handle, prio, NULL); ++ if (ret < 0) { ++ error_setg(errp, "Unable to set TLS session priority %s: %s", ++ prio, gnutls_strerror(ret)); ++ g_free(prio); ++ goto error; ++ } ++ g_free(prio); ++ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { ++ ret = gnutls_credentials_set(session->handle, ++ GNUTLS_CRD_ANON, ++ acreds->data.server); ++ } else { ++ ret = gnutls_credentials_set(session->handle, ++ GNUTLS_CRD_ANON, ++ acreds->data.client); ++ } ++ if (ret < 0) { ++ error_setg(errp, "Cannot set session credentials: %s", ++ gnutls_strerror(ret)); ++ goto error; ++ } ++ } else if (object_dynamic_cast(OBJECT(creds), ++ TYPE_QCRYPTO_TLS_CREDS_PSK)) { ++ QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); ++ char *prio; ++ ++ if (creds->priority != NULL) { ++ prio = g_strdup_printf("%s:%s", ++ creds->priority, ++ TLS_PRIORITY_ADDITIONAL_PSK); ++ } else { ++ prio = g_strdup(CONFIG_TLS_PRIORITY ":" ++ TLS_PRIORITY_ADDITIONAL_PSK); ++ } ++ ++ ret = gnutls_priority_set_direct(session->handle, prio, NULL); ++ if (ret < 0) { ++ error_setg(errp, "Unable to set TLS session priority %s: %s", ++ prio, gnutls_strerror(ret)); ++ g_free(prio); ++ goto error; ++ } ++ g_free(prio); ++ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { ++ ret = gnutls_credentials_set(session->handle, ++ GNUTLS_CRD_PSK, ++ pcreds->data.server); ++ } else { ++ ret = gnutls_credentials_set(session->handle, ++ GNUTLS_CRD_PSK, ++ pcreds->data.client); ++ } ++ if (ret < 0) { ++ error_setg(errp, "Cannot set session credentials: %s", ++ gnutls_strerror(ret)); ++ goto error; ++ } ++ } else if (object_dynamic_cast(OBJECT(creds), ++ TYPE_QCRYPTO_TLS_CREDS_X509)) { ++ QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); ++ const char *prio = creds->priority; ++ if (!prio) { ++ prio = CONFIG_TLS_PRIORITY; ++ } ++ ++ ret = gnutls_priority_set_direct(session->handle, prio, NULL); ++ if (ret < 0) { ++ error_setg(errp, "Cannot set default TLS session priority %s: %s", ++ prio, gnutls_strerror(ret)); ++ goto error; ++ } ++ ret = gnutls_credentials_set(session->handle, ++ GNUTLS_CRD_CERTIFICATE, ++ tcreds->data); ++ if (ret < 0) { ++ error_setg(errp, "Cannot set session credentials: %s", ++ gnutls_strerror(ret)); ++ goto error; ++ } ++ ++ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { ++ /* This requests, but does not enforce a client cert. ++ * The cert checking code later does enforcement */ ++ gnutls_certificate_server_set_request(session->handle, ++ GNUTLS_CERT_REQUEST); ++ } ++ } else { ++ error_setg(errp, "Unsupported TLS credentials type %s", ++ object_get_typename(OBJECT(creds))); ++ goto error; ++ } ++ ++ gnutls_transport_set_ptr(session->handle, session); ++ gnutls_transport_set_push_function(session->handle, ++ qcrypto_tls_session_push); ++ gnutls_transport_set_pull_function(session->handle, ++ qcrypto_tls_session_pull); ++ ++ return session; ++ ++ error: ++ qcrypto_tls_session_free(session); ++ return NULL; ++} ++ ++static int ++qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, ++ Error **errp) ++{ ++ int ret; ++ unsigned int status; ++ const gnutls_datum_t *certs; ++ unsigned int nCerts, i; ++ time_t now; ++ gnutls_x509_crt_t cert = NULL; ++ Error *err = NULL; ++ ++ now = time(NULL); ++ if (now == ((time_t)-1)) { ++ error_setg_errno(errp, errno, "Cannot get current time"); ++ return -1; ++ } ++ ++ ret = gnutls_certificate_verify_peers2(session->handle, &status); ++ if (ret < 0) { ++ error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); ++ return -1; ++ } ++ ++ if (status != 0) { ++ const char *reason = "Invalid certificate"; ++ ++ if (status & GNUTLS_CERT_INVALID) { ++ reason = "The certificate is not trusted"; ++ } ++ ++ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { ++ reason = "The certificate hasn't got a known issuer"; ++ } ++ ++ if (status & GNUTLS_CERT_REVOKED) { ++ reason = "The certificate has been revoked"; ++ } ++ ++ if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { ++ reason = "The certificate uses an insecure algorithm"; ++ } ++ ++ error_setg(errp, "%s", reason); ++ return -1; ++ } ++ ++ certs = gnutls_certificate_get_peers(session->handle, &nCerts); ++ if (!certs) { ++ error_setg(errp, "No certificate peers"); ++ return -1; ++ } ++ ++ for (i = 0; i < nCerts; i++) { ++ ret = gnutls_x509_crt_init(&cert); ++ if (ret < 0) { ++ error_setg(errp, "Cannot initialize certificate: %s", ++ gnutls_strerror(ret)); ++ return -1; ++ } ++ ++ ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); ++ if (ret < 0) { ++ error_setg(errp, "Cannot import certificate: %s", ++ gnutls_strerror(ret)); ++ goto error; ++ } ++ ++ if (gnutls_x509_crt_get_expiration_time(cert) < now) { ++ error_setg(errp, "The certificate has expired"); ++ goto error; ++ } ++ ++ if (gnutls_x509_crt_get_activation_time(cert) > now) { ++ error_setg(errp, "The certificate is not yet activated"); ++ goto error; ++ } ++ ++ if (gnutls_x509_crt_get_activation_time(cert) > now) { ++ error_setg(errp, "The certificate is not yet activated"); ++ goto error; ++ } ++ ++ if (i == 0) { ++ size_t dnameSize = 1024; ++ session->peername = g_malloc(dnameSize); ++ requery: ++ ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); ++ if (ret < 0) { ++ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { ++ session->peername = g_realloc(session->peername, ++ dnameSize); ++ goto requery; ++ } ++ error_setg(errp, "Cannot get client distinguished name: %s", ++ gnutls_strerror(ret)); ++ goto error; ++ } ++ if (session->authzid) { ++ bool allow; ++ ++ allow = qauthz_is_allowed_by_id(session->authzid, ++ session->peername, &err); ++ if (err) { ++ error_propagate(errp, err); ++ goto error; ++ } ++ if (!allow) { ++ error_setg(errp, "TLS x509 authz check for %s is denied", ++ session->peername); ++ goto error; ++ } ++ } ++ if (session->hostname) { ++ if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) { ++ error_setg(errp, ++ "Certificate does not match the hostname %s", ++ session->hostname); ++ goto error; ++ } ++ } else { ++ if (session->creds->endpoint == ++ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { ++ error_setg(errp, "No hostname for certificate validation"); ++ goto error; ++ } ++ } ++ } ++ ++ gnutls_x509_crt_deinit(cert); ++ } ++ ++ return 0; ++ ++ error: ++ gnutls_x509_crt_deinit(cert); ++ return -1; ++} ++ ++ ++int ++qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, ++ Error **errp) ++{ ++ if (object_dynamic_cast(OBJECT(session->creds), ++ TYPE_QCRYPTO_TLS_CREDS_ANON)) { ++ trace_qcrypto_tls_session_check_creds(session, "nop"); ++ return 0; ++ } else if (object_dynamic_cast(OBJECT(session->creds), ++ TYPE_QCRYPTO_TLS_CREDS_PSK)) { ++ trace_qcrypto_tls_session_check_creds(session, "nop"); ++ return 0; ++ } else if (object_dynamic_cast(OBJECT(session->creds), ++ TYPE_QCRYPTO_TLS_CREDS_X509)) { ++ if (session->creds->verifyPeer) { ++ int ret = qcrypto_tls_session_check_certificate(session, ++ errp); ++ trace_qcrypto_tls_session_check_creds(session, ++ ret == 0 ? "pass" : "fail"); ++ return ret; ++ } else { ++ trace_qcrypto_tls_session_check_creds(session, "skip"); ++ return 0; ++ } ++ } else { ++ trace_qcrypto_tls_session_check_creds(session, "error"); ++ error_setg(errp, "Unexpected credential type %s", ++ object_get_typename(OBJECT(session->creds))); ++ return -1; ++ } ++} ++ ++ ++void ++qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, ++ QCryptoTLSSessionWriteFunc writeFunc, ++ QCryptoTLSSessionReadFunc readFunc, ++ void *opaque) ++{ ++ session->writeFunc = writeFunc; ++ session->readFunc = readFunc; ++ session->opaque = opaque; ++} ++ ++ ++ssize_t ++qcrypto_tls_session_write(QCryptoTLSSession *session, ++ const char *buf, ++ size_t len, ++ Error **errp) ++{ ++ ssize_t ret = gnutls_record_send(session->handle, buf, len); ++ ++ if (ret < 0) { ++ if (ret == GNUTLS_E_AGAIN) { ++ return QCRYPTO_TLS_SESSION_ERR_BLOCK; ++ } else { ++ if (session->werr) { ++ error_propagate(errp, session->werr); ++ session->werr = NULL; ++ } else { ++ error_setg(errp, ++ "Cannot write to TLS channel: %s", ++ gnutls_strerror(ret)); ++ } ++ return -1; ++ } ++ } ++ ++ return ret; ++} ++ ++ ++ssize_t ++qcrypto_tls_session_read(QCryptoTLSSession *session, ++ char *buf, ++ size_t len, ++ bool gracefulTermination, ++ Error **errp) ++{ ++ ssize_t ret = gnutls_record_recv(session->handle, buf, len); ++ ++ if (ret < 0) { ++ if (ret == GNUTLS_E_AGAIN) { ++ return QCRYPTO_TLS_SESSION_ERR_BLOCK; ++ } else if ((ret == GNUTLS_E_PREMATURE_TERMINATION) && ++ gracefulTermination){ ++ return 0; ++ } else { ++ if (session->rerr) { ++ error_propagate(errp, session->rerr); ++ session->rerr = NULL; ++ } else { ++ error_setg(errp, ++ "Cannot read from TLS channel: %s", ++ gnutls_strerror(ret)); ++ } ++ return -1; ++ } ++ } ++ ++ return ret; ++} ++ ++ ++size_t ++qcrypto_tls_session_check_pending(QCryptoTLSSession *session) ++{ ++ return gnutls_record_check_pending(session->handle); ++} ++ ++ ++int ++qcrypto_tls_session_handshake(QCryptoTLSSession *session, ++ Error **errp) ++{ ++ int ret = gnutls_handshake(session->handle); ++ if (ret == 0) { ++ session->handshakeComplete = true; ++ } else { ++ if (ret == GNUTLS_E_INTERRUPTED || ++ ret == GNUTLS_E_AGAIN) { ++ ret = 1; ++ } else { ++ if (session->rerr || session->werr) { ++ error_setg(errp, "TLS handshake failed: %s: %s", ++ gnutls_strerror(ret), ++ error_get_pretty(session->rerr ? ++ session->rerr : session->werr)); ++ } else { ++ error_setg(errp, "TLS handshake failed: %s", ++ gnutls_strerror(ret)); ++ } ++ ret = -1; ++ } ++ } ++ error_free(session->rerr); ++ error_free(session->werr); ++ session->rerr = session->werr = NULL; ++ ++ return ret; ++} ++ ++ ++QCryptoTLSSessionHandshakeStatus ++qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) ++{ ++ if (session->handshakeComplete) { ++ return QCRYPTO_TLS_HANDSHAKE_COMPLETE; ++ } else if (gnutls_record_get_direction(session->handle) == 0) { ++ return QCRYPTO_TLS_HANDSHAKE_RECVING; ++ } else { ++ return QCRYPTO_TLS_HANDSHAKE_SENDING; ++ } ++} ++ ++ ++int ++qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, ++ Error **errp) ++{ ++ gnutls_cipher_algorithm_t cipher; ++ int ssf; ++ ++ cipher = gnutls_cipher_get(session->handle); ++ ssf = gnutls_cipher_get_key_size(cipher); ++ if (!ssf) { ++ error_setg(errp, "Cannot get TLS cipher key size"); ++ return -1; ++ } ++ return ssf; ++} ++ ++ ++char * ++qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) ++{ ++ if (session->peername) { ++ return g_strdup(session->peername); ++ } ++ return NULL; ++} ++ ++ ++#else /* ! CONFIG_GNUTLS */ ++ ++ ++QCryptoTLSSession * ++qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, ++ const char *hostname G_GNUC_UNUSED, ++ const char *authzid G_GNUC_UNUSED, ++ QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, ++ Error **errp) ++{ ++ error_setg(errp, "TLS requires GNUTLS support"); ++ return NULL; ++} ++ ++ ++void ++qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) ++{ ++} ++ ++ ++int ++qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, ++ Error **errp) ++{ ++ error_setg(errp, "TLS requires GNUTLS support"); ++ return -1; ++} ++ ++ ++void ++qcrypto_tls_session_set_callbacks( ++ QCryptoTLSSession *sess G_GNUC_UNUSED, ++ QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, ++ QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, ++ void *opaque G_GNUC_UNUSED) ++{ ++} ++ ++ ++ssize_t ++qcrypto_tls_session_write(QCryptoTLSSession *sess, ++ const char *buf, ++ size_t len, ++ Error **errp) ++{ ++ error_setg(errp, "TLS requires GNUTLS support"); ++ return -1; ++} ++ ++ ++ssize_t ++qcrypto_tls_session_read(QCryptoTLSSession *sess, ++ char *buf, ++ size_t len, ++ bool gracefulTermination, ++ Error **errp) ++{ ++ error_setg(errp, "TLS requires GNUTLS support"); ++ return -1; ++} ++ ++ ++size_t ++qcrypto_tls_session_check_pending(QCryptoTLSSession *session) ++{ ++ return 0; ++} ++ ++ ++int ++qcrypto_tls_session_handshake(QCryptoTLSSession *sess, ++ Error **errp) ++{ ++ error_setg(errp, "TLS requires GNUTLS support"); ++ return -1; ++} ++ ++ ++QCryptoTLSSessionHandshakeStatus ++qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) ++{ ++ return QCRYPTO_TLS_HANDSHAKE_COMPLETE; ++} ++ ++ ++int ++qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, ++ Error **errp) ++{ ++ error_setg(errp, "TLS requires GNUTLS support"); ++ return -1; ++} ++ ++ ++char * ++qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) ++{ ++ return NULL; ++} ++ ++#endif +diff --git a/qcow2/lib/hw/block/block.c b/qcow2/lib/hw/block/block.c +new file mode 100644 +index 00000000..3ceca7dc +--- /dev/null ++++ b/qcow2/lib/hw/block/block.c +@@ -0,0 +1,278 @@ ++/* ++ * Common code for block device models ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block_int-common.h" ++#include "sysemu/blockdev.h" ++#include "sysemu/block-backend.h" ++#include "hw/block/block.h" ++#include "qapi/error.h" ++#include "qapi/qapi-types-block.h" ++ ++/* ++ * Read the non-zeroes parts of @blk into @buf ++ * Reading all of the @blk is expensive if the zeroes parts of @blk ++ * is large enough. Therefore check the block status and only write ++ * the non-zeroes block into @buf. ++ * ++ * Return 0 on success, non-zero on error. ++ */ ++static int blk_pread_nonzeroes(BlockBackend *blk, hwaddr size, void *buf) ++{ ++ int ret; ++ int64_t bytes, offset = 0; ++ BlockDriverState *bs = blk_bs(blk); ++ ++ for (;;) { ++ bytes = MIN(size - offset, BDRV_REQUEST_MAX_BYTES); ++ if (bytes <= 0) { ++ return 0; ++ } ++ ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ if (!(ret & BDRV_BLOCK_ZERO)) { ++ ret = blk_pread(blk, offset, bytes, (uint8_t *) buf + offset, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ offset += bytes; ++ } ++} ++ ++/* ++ * Read the entire contents of @blk into @buf. ++ * @blk's contents must be @size bytes, and @size must be at most ++ * BDRV_REQUEST_MAX_BYTES. ++ * On success, return true. ++ * On failure, store an error through @errp and return false. ++ * ++ * This function not intended for actual block devices, which read on ++ * demand. It's for things like memory devices that (ab)use a block ++ * backend to provide persistence. ++ */ ++bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, ++ void *buf, hwaddr size, Error **errp) ++{ ++ int64_t blk_len; ++ int ret; ++ g_autofree char *dev_id = NULL; ++ ++ blk_len = blk_getlength(blk); ++ if (blk_len < 0) { ++ error_setg_errno(errp, -blk_len, ++ "can't get size of %s block backend", blk_name(blk)); ++ return false; ++ } ++ if (blk_len != size) { ++ dev_id = qdev_get_human_name(dev); ++ error_setg(errp, "%s device '%s' requires %" HWADDR_PRIu ++ " bytes, %s block backend provides %" PRIu64 " bytes", ++ object_get_typename(OBJECT(dev)), dev_id, size, ++ blk_name(blk), blk_len); ++ return false; ++ } ++ ++ /* ++ * We could loop for @size > BDRV_REQUEST_MAX_BYTES, but if we ++ * ever get to the point we want to read *gigabytes* here, we ++ * should probably rework the device to be more like an actual ++ * block device and read only on demand. ++ */ ++ assert(size <= BDRV_REQUEST_MAX_BYTES); ++ ret = blk_pread_nonzeroes(blk, size, buf); ++ if (ret < 0) { ++ dev_id = qdev_get_human_name(dev); ++ error_setg_errno(errp, -ret, "can't read %s block backend" ++ " for %s device '%s'", ++ blk_name(blk), object_get_typename(OBJECT(dev)), ++ dev_id); ++ return false; ++ } ++ return true; ++} ++ ++bool blkconf_blocksizes(BlockConf *conf, Error **errp) ++{ ++ BlockBackend *blk = conf->blk; ++ BlockSizes blocksizes; ++ BlockDriverState *bs; ++ bool use_blocksizes; ++ bool use_bs; ++ ++ switch (conf->backend_defaults) { ++ case ON_OFF_AUTO_AUTO: ++ use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes); ++ use_bs = false; ++ break; ++ ++ case ON_OFF_AUTO_ON: ++ use_blocksizes = !blk_probe_blocksizes(blk, &blocksizes); ++ bs = blk_bs(blk); ++ use_bs = bs; ++ break; ++ ++ case ON_OFF_AUTO_OFF: ++ use_blocksizes = false; ++ use_bs = false; ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ /* fill in detected values if they are not defined via qemu command line */ ++ if (!conf->physical_block_size) { ++ if (use_blocksizes) { ++ conf->physical_block_size = blocksizes.phys; ++ } else { ++ conf->physical_block_size = BDRV_SECTOR_SIZE; ++ } ++ } ++ if (!conf->logical_block_size) { ++ if (use_blocksizes) { ++ conf->logical_block_size = blocksizes.log; ++ } else { ++ conf->logical_block_size = BDRV_SECTOR_SIZE; ++ } ++ } ++ if (use_bs) { ++ if (!conf->opt_io_size) { ++ conf->opt_io_size = bs->bl.opt_transfer; ++ } ++ if (conf->discard_granularity == -1) { ++ if (bs->bl.pdiscard_alignment) { ++ conf->discard_granularity = bs->bl.pdiscard_alignment; ++ } else if (bs->bl.request_alignment != 1) { ++ conf->discard_granularity = bs->bl.request_alignment; ++ } ++ } ++ } ++ ++ if (conf->logical_block_size > conf->physical_block_size) { ++ error_setg(errp, ++ "logical_block_size > physical_block_size not supported"); ++ return false; ++ } ++ ++ if (!QEMU_IS_ALIGNED(conf->min_io_size, conf->logical_block_size)) { ++ error_setg(errp, ++ "min_io_size must be a multiple of logical_block_size"); ++ return false; ++ } ++ ++ /* ++ * all devices which support min_io_size (scsi and virtio-blk) expose it to ++ * the guest as a uint16_t in units of logical blocks ++ */ ++ if (conf->min_io_size / conf->logical_block_size > UINT16_MAX) { ++ error_setg(errp, "min_io_size must not exceed %u logical blocks", ++ UINT16_MAX); ++ return false; ++ } ++ ++ if (!QEMU_IS_ALIGNED(conf->opt_io_size, conf->logical_block_size)) { ++ error_setg(errp, ++ "opt_io_size must be a multiple of logical_block_size"); ++ return false; ++ } ++ ++ if (conf->discard_granularity != -1 && ++ !QEMU_IS_ALIGNED(conf->discard_granularity, ++ conf->logical_block_size)) { ++ error_setg(errp, "discard_granularity must be " ++ "a multiple of logical_block_size"); ++ return false; ++ } ++ ++ return true; ++} ++ ++bool blkconf_apply_backend_options(BlockConf *conf, bool readonly, ++ bool resizable, Error **errp) ++{ ++ BlockBackend *blk = conf->blk; ++ BlockdevOnError rerror, werror; ++ uint64_t perm, shared_perm; ++ bool wce; ++ int ret; ++ ++ perm = BLK_PERM_CONSISTENT_READ; ++ if (!readonly) { ++ perm |= BLK_PERM_WRITE; ++ } ++ ++ shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED; ++ if (resizable) { ++ shared_perm |= BLK_PERM_RESIZE; ++ } ++ if (conf->share_rw) { ++ shared_perm |= BLK_PERM_WRITE; ++ } ++ ++ ret = blk_set_perm(blk, perm, shared_perm, errp); ++ if (ret < 0) { ++ return false; ++ } ++ ++ switch (conf->wce) { ++ case ON_OFF_AUTO_ON: wce = true; break; ++ case ON_OFF_AUTO_OFF: wce = false; break; ++ case ON_OFF_AUTO_AUTO: wce = blk_enable_write_cache(blk); break; ++ default: ++ abort(); ++ } ++ ++ rerror = conf->rerror; ++ if (rerror == BLOCKDEV_ON_ERROR_AUTO) { ++ rerror = blk_get_on_error(blk, true); ++ } ++ ++ werror = conf->werror; ++ if (werror == BLOCKDEV_ON_ERROR_AUTO) { ++ werror = blk_get_on_error(blk, false); ++ } ++ ++ blk_set_enable_write_cache(blk, wce); ++ blk_set_on_error(blk, rerror, werror); ++ ++ block_acct_setup(blk_get_stats(blk), conf->account_invalid, ++ conf->account_failed); ++ return true; ++} ++ ++bool blkconf_geometry(BlockConf *conf, int *ptrans, ++ unsigned cyls_max, unsigned heads_max, unsigned secs_max, ++ Error **errp) ++{ ++ if (!conf->cyls && !conf->heads && !conf->secs) { ++ hd_geometry_guess(conf->blk, ++ &conf->cyls, &conf->heads, &conf->secs, ++ ptrans); ++ } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { ++ *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); ++ } ++ if (conf->cyls || conf->heads || conf->secs) { ++ if (conf->cyls < 1 || conf->cyls > cyls_max) { ++ error_setg(errp, "cyls must be between 1 and %u", cyls_max); ++ return false; ++ } ++ if (conf->heads < 1 || conf->heads > heads_max) { ++ error_setg(errp, "heads must be between 1 and %u", heads_max); ++ return false; ++ } ++ if (conf->secs < 1 || conf->secs > secs_max) { ++ error_setg(errp, "secs must be between 1 and %u", secs_max); ++ return false; ++ } ++ } ++ return true; ++} +diff --git a/qcow2/lib/hw/block/hd-geometry.c b/qcow2/lib/hw/block/hd-geometry.c +new file mode 100644 +index 00000000..2b0af443 +--- /dev/null ++++ b/qcow2/lib/hw/block/hd-geometry.c +@@ -0,0 +1,168 @@ ++/* ++ * Hard disk geometry utilities ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ * This file incorporates work covered by the following copyright and ++ * permission notice: ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "sysemu/block-backend.h" ++#include "qapi/qapi-types-block.h" ++#include "qemu/bswap.h" ++#include "hw/block/block.h" ++#include "trace.h" ++ ++struct partition { ++ uint8_t boot_ind; /* 0x80 - active */ ++ uint8_t head; /* starting head */ ++ uint8_t sector; /* starting sector */ ++ uint8_t cyl; /* starting cylinder */ ++ uint8_t sys_ind; /* What partition type */ ++ uint8_t end_head; /* end head */ ++ uint8_t end_sector; /* end sector */ ++ uint8_t end_cyl; /* end cylinder */ ++ uint32_t start_sect; /* starting sector counting from 0 */ ++ uint32_t nr_sects; /* nr of sectors in partition */ ++} QEMU_PACKED; ++ ++/* try to guess the disk logical geometry from the MS-DOS partition table. ++ Return 0 if OK, -1 if could not guess */ ++static int guess_disk_lchs(BlockBackend *blk, ++ int *pcylinders, int *pheads, int *psectors) ++{ ++ uint8_t buf[BDRV_SECTOR_SIZE]; ++ int i, heads, sectors, cylinders; ++ struct partition *p; ++ uint32_t nr_sects; ++ uint64_t nb_sectors; ++ ++ blk_get_geometry(blk, &nb_sectors); ++ ++ if (blk_pread(blk, 0, BDRV_SECTOR_SIZE, buf, 0) < 0) { ++ return -1; ++ } ++ /* test MS-DOS magic */ ++ if (buf[510] != 0x55 || buf[511] != 0xaa) { ++ return -1; ++ } ++ for (i = 0; i < 4; i++) { ++ p = ((struct partition *)(buf + 0x1be)) + i; ++ nr_sects = le32_to_cpu(p->nr_sects); ++ if (nr_sects && p->end_head) { ++ /* We make the assumption that the partition terminates on ++ a cylinder boundary */ ++ heads = p->end_head + 1; ++ sectors = p->end_sector & 63; ++ if (sectors == 0) { ++ continue; ++ } ++ cylinders = nb_sectors / (heads * sectors); ++ if (cylinders < 1 || cylinders > 16383) { ++ continue; ++ } ++ *pheads = heads; ++ *psectors = sectors; ++ *pcylinders = cylinders; ++ trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors); ++ return 0; ++ } ++ } ++ return -1; ++} ++ ++static void guess_chs_for_size(BlockBackend *blk, ++ uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs) ++{ ++ uint64_t nb_sectors; ++ int cylinders; ++ ++ blk_get_geometry(blk, &nb_sectors); ++ ++ cylinders = nb_sectors / (16 * 63); ++ if (cylinders > 16383) { ++ cylinders = 16383; ++ } else if (cylinders < 2) { ++ cylinders = 2; ++ } ++ *pcyls = cylinders; ++ *pheads = 16; ++ *psecs = 63; ++} ++ ++void hd_geometry_guess(BlockBackend *blk, ++ uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, ++ int *ptrans) ++{ ++ int cylinders, heads, secs, translation; ++ HDGeometry geo; ++ ++ /* Try to probe the backing device geometry, otherwise fallback ++ to the old logic. (as of 12/2014 probing only succeeds on DASDs) */ ++ if (blk_probe_geometry(blk, &geo) == 0) { ++ *pcyls = geo.cylinders; ++ *psecs = geo.sectors; ++ *pheads = geo.heads; ++ translation = BIOS_ATA_TRANSLATION_NONE; ++ } else if (guess_disk_lchs(blk, &cylinders, &heads, &secs) < 0) { ++ /* no LCHS guess: use a standard physical disk geometry */ ++ guess_chs_for_size(blk, pcyls, pheads, psecs); ++ translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs); ++ } else if (heads > 16) { ++ /* LCHS guess with heads > 16 means that a BIOS LBA ++ translation was active, so a standard physical disk ++ geometry is OK */ ++ guess_chs_for_size(blk, pcyls, pheads, psecs); ++ translation = *pcyls * *pheads <= 131072 ++ ? BIOS_ATA_TRANSLATION_LARGE ++ : BIOS_ATA_TRANSLATION_LBA; ++ } else { ++ /* LCHS guess with heads <= 16: use as physical geometry */ ++ *pcyls = cylinders; ++ *pheads = heads; ++ *psecs = secs; ++ /* disable any translation to be in sync with ++ the logical geometry */ ++ translation = BIOS_ATA_TRANSLATION_NONE; ++ } ++ if (ptrans) { ++ if (*ptrans == BIOS_ATA_TRANSLATION_AUTO) { ++ *ptrans = translation; ++ } else { ++ /* Defer to the translation specified by the user. */ ++ translation = *ptrans; ++ } ++ } ++ trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); ++} ++ ++int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs) ++{ ++ return cyls <= 1024 && heads <= 16 && secs <= 63 ++ ? BIOS_ATA_TRANSLATION_NONE ++ : BIOS_ATA_TRANSLATION_LBA; ++} +diff --git a/qcow2/lib/io/channel-file.c b/qcow2/lib/io/channel-file.c +new file mode 100644 +index 00000000..2ea8d083 +--- /dev/null ++++ b/qcow2/lib/io/channel-file.c +@@ -0,0 +1,324 @@ ++/* ++ * QEMU I/O channels files driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "io/channel-file.h" ++#include "io/channel-util.h" ++#include "io/channel-watch.h" ++#include "qapi/error.h" ++#include "qemu/module.h" ++#include "qemu/sockets.h" ++#include "trace.h" ++ ++QIOChannelFile * ++qio_channel_file_new_fd(int fd) ++{ ++ QIOChannelFile *ioc; ++ ++ ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); ++ ++ ioc->fd = fd; ++ ++ if (lseek(fd, 0, SEEK_CUR) != (off_t)-1) { ++ qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); ++ } ++ ++ trace_qio_channel_file_new_fd(ioc, fd); ++ ++ return ioc; ++} ++ ++QIOChannelFile * ++qio_channel_file_new_dupfd(int fd, Error **errp) ++{ ++ int newfd = dup(fd); ++ ++ if (newfd < 0) { ++ error_setg_errno(errp, errno, "Could not dup FD %d", fd); ++ return NULL; ++ } ++ ++ return qio_channel_file_new_fd(newfd); ++} ++ ++QIOChannelFile * ++qio_channel_file_new_path(const char *path, ++ int flags, ++ mode_t mode, ++ Error **errp) ++{ ++ QIOChannelFile *ioc; ++ ++ ioc = QIO_CHANNEL_FILE(object_new(TYPE_QIO_CHANNEL_FILE)); ++ ++ if (flags & O_CREAT) { ++ ioc->fd = qemu_create(path, flags & ~O_CREAT, mode, errp); ++ } else { ++ ioc->fd = qemu_open(path, flags, errp); ++ } ++ if (ioc->fd < 0) { ++ object_unref(OBJECT(ioc)); ++ return NULL; ++ } ++ ++ if (lseek(ioc->fd, 0, SEEK_CUR) != (off_t)-1) { ++ qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); ++ } ++ ++ trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd); ++ ++ return ioc; ++} ++ ++ ++static void qio_channel_file_init(Object *obj) ++{ ++ QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj); ++ ioc->fd = -1; ++} ++ ++static void qio_channel_file_finalize(Object *obj) ++{ ++ QIOChannelFile *ioc = QIO_CHANNEL_FILE(obj); ++ if (ioc->fd != -1) { ++ qemu_close(ioc->fd); ++ ioc->fd = -1; ++ } ++} ++ ++ ++static ssize_t qio_channel_file_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ssize_t ret; ++ ++ retry: ++ ret = readv(fioc->fd, iov, niov); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ ++ error_setg_errno(errp, errno, ++ "Unable to read from file"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++static ssize_t qio_channel_file_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ssize_t ret; ++ ++ retry: ++ ret = writev(fioc->fd, iov, niov); ++ if (ret <= 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ error_setg_errno(errp, errno, ++ "Unable to write to file"); ++ return -1; ++ } ++ return ret; ++} ++ ++#ifdef CONFIG_PREADV ++static ssize_t qio_channel_file_preadv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ off_t offset, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ssize_t ret; ++ ++ retry: ++ ret = preadv(fioc->fd, iov, niov, offset); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ ++ error_setg_errno(errp, errno, "Unable to read from file"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++static ssize_t qio_channel_file_pwritev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ off_t offset, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ssize_t ret; ++ ++ retry: ++ ret = pwritev(fioc->fd, iov, niov, offset); ++ if (ret <= 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ error_setg_errno(errp, errno, "Unable to write to file"); ++ return -1; ++ } ++ return ret; ++} ++#endif /* CONFIG_PREADV */ ++ ++static int qio_channel_file_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp) ++{ ++#ifdef WIN32 ++ /* not implemented */ ++ error_setg_errno(errp, errno, "Failed to set FD nonblocking"); ++ return -1; ++#else ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ++ if (!g_unix_set_fd_nonblocking(fioc->fd, !enabled, NULL)) { ++ error_setg_errno(errp, errno, "Failed to set FD nonblocking"); ++ return -1; ++ } ++ return 0; ++#endif ++} ++ ++ ++static off_t qio_channel_file_seek(QIOChannel *ioc, ++ off_t offset, ++ int whence, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ off_t ret; ++ ++ ret = lseek(fioc->fd, offset, whence); ++ if (ret == (off_t)-1) { ++ error_setg_errno(errp, errno, ++ "Unable to seek to offset %lld whence %d in file", ++ (long long int)offset, whence); ++ return -1; ++ } ++ return ret; ++} ++ ++ ++static int qio_channel_file_close(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ++ if (qemu_close(fioc->fd) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to close file"); ++ return -1; ++ } ++ fioc->fd = -1; ++ return 0; ++} ++ ++ ++static void qio_channel_file_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ ++ qio_channel_util_set_aio_fd_handler(fioc->fd, read_ctx, io_read, ++ fioc->fd, write_ctx, io_write, ++ opaque); ++} ++ ++static GSource *qio_channel_file_create_watch(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc); ++ return qio_channel_create_fd_watch(ioc, ++ fioc->fd, ++ condition); ++} ++ ++static void qio_channel_file_class_init(ObjectClass *klass, ++ void *class_data G_GNUC_UNUSED) ++{ ++ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); ++ ++ ioc_klass->io_writev = qio_channel_file_writev; ++ ioc_klass->io_readv = qio_channel_file_readv; ++ ioc_klass->io_set_blocking = qio_channel_file_set_blocking; ++#ifdef CONFIG_PREADV ++ ioc_klass->io_pwritev = qio_channel_file_pwritev; ++ ioc_klass->io_preadv = qio_channel_file_preadv; ++#endif ++ ioc_klass->io_seek = qio_channel_file_seek; ++ ioc_klass->io_close = qio_channel_file_close; ++ ioc_klass->io_create_watch = qio_channel_file_create_watch; ++ ioc_klass->io_set_aio_fd_handler = qio_channel_file_set_aio_fd_handler; ++} ++ ++static const TypeInfo qio_channel_file_info = { ++ .parent = TYPE_QIO_CHANNEL, ++ .name = TYPE_QIO_CHANNEL_FILE, ++ .instance_size = sizeof(QIOChannelFile), ++ .instance_init = qio_channel_file_init, ++ .instance_finalize = qio_channel_file_finalize, ++ .class_init = qio_channel_file_class_init, ++}; ++ ++static void qio_channel_file_register_types(void) ++{ ++ type_register_static(&qio_channel_file_info); ++} ++ ++type_init(qio_channel_file_register_types); +diff --git a/qcow2/lib/io/channel-socket.c b/qcow2/lib/io/channel-socket.c +new file mode 100644 +index 00000000..608bcf06 +--- /dev/null ++++ b/qcow2/lib/io/channel-socket.c +@@ -0,0 +1,985 @@ ++/* ++ * QEMU I/O channels sockets driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi/qapi-visit-sockets.h" ++#include "qemu/module.h" ++#include "io/channel-socket.h" ++#include "io/channel-util.h" ++#include "io/channel-watch.h" ++#include "trace.h" ++#include "qapi/clone-visitor.h" ++#ifdef CONFIG_LINUX ++#include ++#include ++ ++#if (defined(MSG_ZEROCOPY) && defined(SO_ZEROCOPY)) ++#define QEMU_MSG_ZEROCOPY ++#endif ++#endif ++ ++#define SOCKET_MAX_FDS 16 ++ ++SocketAddress * ++qio_channel_socket_get_local_address(QIOChannelSocket *ioc, ++ Error **errp) ++{ ++ return socket_sockaddr_to_address(&ioc->localAddr, ++ ioc->localAddrLen, ++ errp); ++} ++ ++SocketAddress * ++qio_channel_socket_get_remote_address(QIOChannelSocket *ioc, ++ Error **errp) ++{ ++ return socket_sockaddr_to_address(&ioc->remoteAddr, ++ ioc->remoteAddrLen, ++ errp); ++} ++ ++QIOChannelSocket * ++qio_channel_socket_new(void) ++{ ++ QIOChannelSocket *sioc; ++ QIOChannel *ioc; ++ ++ sioc = QIO_CHANNEL_SOCKET(object_new(TYPE_QIO_CHANNEL_SOCKET)); ++ sioc->fd = -1; ++ sioc->zero_copy_queued = 0; ++ sioc->zero_copy_sent = 0; ++ ++ ioc = QIO_CHANNEL(sioc); ++ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); ++ ++#ifdef WIN32 ++ ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL); ++#endif ++ ++ trace_qio_channel_socket_new(sioc); ++ ++ return sioc; ++} ++ ++ ++static int ++qio_channel_socket_set_fd(QIOChannelSocket *sioc, ++ int fd, ++ Error **errp) ++{ ++ if (sioc->fd != -1) { ++ error_setg(errp, "Socket is already open"); ++ return -1; ++ } ++ ++ sioc->fd = fd; ++ sioc->remoteAddrLen = sizeof(sioc->remoteAddr); ++ sioc->localAddrLen = sizeof(sioc->localAddr); ++ ++ ++ if (getpeername(fd, (struct sockaddr *)&sioc->remoteAddr, ++ &sioc->remoteAddrLen) < 0) { ++ if (errno == ENOTCONN) { ++ memset(&sioc->remoteAddr, 0, sizeof(sioc->remoteAddr)); ++ sioc->remoteAddrLen = sizeof(sioc->remoteAddr); ++ } else { ++ error_setg_errno(errp, errno, ++ "Unable to query remote socket address"); ++ goto error; ++ } ++ } ++ ++ if (getsockname(fd, (struct sockaddr *)&sioc->localAddr, ++ &sioc->localAddrLen) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to query local socket address"); ++ goto error; ++ } ++ ++#ifndef WIN32 ++ if (sioc->localAddr.ss_family == AF_UNIX) { ++ QIOChannel *ioc = QIO_CHANNEL(sioc); ++ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS); ++ } ++#endif /* WIN32 */ ++ ++ return 0; ++ ++ error: ++ sioc->fd = -1; /* Let the caller close FD on failure */ ++ return -1; ++} ++ ++QIOChannelSocket * ++qio_channel_socket_new_fd(int fd, ++ Error **errp) ++{ ++ QIOChannelSocket *ioc; ++ ++ ioc = qio_channel_socket_new(); ++ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { ++ object_unref(OBJECT(ioc)); ++ return NULL; ++ } ++ ++ trace_qio_channel_socket_new_fd(ioc, fd); ++ ++ return ioc; ++} ++ ++ ++int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ Error **errp) ++{ ++ int fd; ++ ++ trace_qio_channel_socket_connect_sync(ioc, addr); ++ fd = socket_connect(addr, errp); ++ if (fd < 0) { ++ trace_qio_channel_socket_connect_fail(ioc); ++ return -1; ++ } ++ ++ trace_qio_channel_socket_connect_complete(ioc, fd); ++ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { ++ close(fd); ++ return -1; ++ } ++ ++#ifdef QEMU_MSG_ZEROCOPY ++ int ret, v = 1; ++ ret = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &v, sizeof(v)); ++ if (ret == 0) { ++ /* Zero copy available on host */ ++ qio_channel_set_feature(QIO_CHANNEL(ioc), ++ QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY); ++ } ++#endif ++ ++ qio_channel_set_feature(QIO_CHANNEL(ioc), ++ QIO_CHANNEL_FEATURE_READ_MSG_PEEK); ++ ++ return 0; ++} ++ ++ ++static void qio_channel_socket_connect_worker(QIOTask *task, ++ gpointer opaque) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); ++ SocketAddress *addr = opaque; ++ Error *err = NULL; ++ ++ qio_channel_socket_connect_sync(ioc, addr, &err); ++ ++ qio_task_set_error(task, err); ++} ++ ++ ++void qio_channel_socket_connect_async(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ QIOTask *task = qio_task_new( ++ OBJECT(ioc), callback, opaque, destroy); ++ SocketAddress *addrCopy; ++ ++ addrCopy = QAPI_CLONE(SocketAddress, addr); ++ ++ /* socket_connect() does a non-blocking connect(), but it ++ * still blocks in DNS lookups, so we must use a thread */ ++ trace_qio_channel_socket_connect_async(ioc, addr); ++ qio_task_run_in_thread(task, ++ qio_channel_socket_connect_worker, ++ addrCopy, ++ (GDestroyNotify)qapi_free_SocketAddress, ++ context); ++} ++ ++ ++int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ int num, ++ Error **errp) ++{ ++ int fd; ++ ++ trace_qio_channel_socket_listen_sync(ioc, addr, num); ++ fd = socket_listen(addr, num, errp); ++ if (fd < 0) { ++ trace_qio_channel_socket_listen_fail(ioc); ++ return -1; ++ } ++ ++ trace_qio_channel_socket_listen_complete(ioc, fd); ++ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { ++ close(fd); ++ return -1; ++ } ++ qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_LISTEN); ++ ++ return 0; ++} ++ ++ ++struct QIOChannelListenWorkerData { ++ SocketAddress *addr; ++ int num; /* amount of expected connections */ ++}; ++ ++static void qio_channel_listen_worker_free(gpointer opaque) ++{ ++ struct QIOChannelListenWorkerData *data = opaque; ++ ++ qapi_free_SocketAddress(data->addr); ++ g_free(data); ++} ++ ++static void qio_channel_socket_listen_worker(QIOTask *task, ++ gpointer opaque) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); ++ struct QIOChannelListenWorkerData *data = opaque; ++ Error *err = NULL; ++ ++ qio_channel_socket_listen_sync(ioc, data->addr, data->num, &err); ++ ++ qio_task_set_error(task, err); ++} ++ ++ ++void qio_channel_socket_listen_async(QIOChannelSocket *ioc, ++ SocketAddress *addr, ++ int num, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ QIOTask *task = qio_task_new( ++ OBJECT(ioc), callback, opaque, destroy); ++ struct QIOChannelListenWorkerData *data; ++ ++ data = g_new0(struct QIOChannelListenWorkerData, 1); ++ data->addr = QAPI_CLONE(SocketAddress, addr); ++ data->num = num; ++ ++ /* socket_listen() blocks in DNS lookups, so we must use a thread */ ++ trace_qio_channel_socket_listen_async(ioc, addr, num); ++ qio_task_run_in_thread(task, ++ qio_channel_socket_listen_worker, ++ data, ++ qio_channel_listen_worker_free, ++ context); ++} ++ ++ ++int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, ++ SocketAddress *localAddr, ++ SocketAddress *remoteAddr, ++ Error **errp) ++{ ++ int fd; ++ ++ trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr); ++ fd = socket_dgram(remoteAddr, localAddr, errp); ++ if (fd < 0) { ++ trace_qio_channel_socket_dgram_fail(ioc); ++ return -1; ++ } ++ ++ trace_qio_channel_socket_dgram_complete(ioc, fd); ++ if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { ++ close(fd); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++struct QIOChannelSocketDGramWorkerData { ++ SocketAddress *localAddr; ++ SocketAddress *remoteAddr; ++}; ++ ++ ++static void qio_channel_socket_dgram_worker_free(gpointer opaque) ++{ ++ struct QIOChannelSocketDGramWorkerData *data = opaque; ++ qapi_free_SocketAddress(data->localAddr); ++ qapi_free_SocketAddress(data->remoteAddr); ++ g_free(data); ++} ++ ++static void qio_channel_socket_dgram_worker(QIOTask *task, ++ gpointer opaque) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); ++ struct QIOChannelSocketDGramWorkerData *data = opaque; ++ Error *err = NULL; ++ ++ /* socket_dgram() blocks in DNS lookups, so we must use a thread */ ++ qio_channel_socket_dgram_sync(ioc, data->localAddr, ++ data->remoteAddr, &err); ++ ++ qio_task_set_error(task, err); ++} ++ ++ ++void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, ++ SocketAddress *localAddr, ++ SocketAddress *remoteAddr, ++ QIOTaskFunc callback, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ QIOTask *task = qio_task_new( ++ OBJECT(ioc), callback, opaque, destroy); ++ struct QIOChannelSocketDGramWorkerData *data = g_new0( ++ struct QIOChannelSocketDGramWorkerData, 1); ++ ++ data->localAddr = QAPI_CLONE(SocketAddress, localAddr); ++ data->remoteAddr = QAPI_CLONE(SocketAddress, remoteAddr); ++ ++ trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); ++ qio_task_run_in_thread(task, ++ qio_channel_socket_dgram_worker, ++ data, ++ qio_channel_socket_dgram_worker_free, ++ context); ++} ++ ++ ++QIOChannelSocket * ++qio_channel_socket_accept(QIOChannelSocket *ioc, ++ Error **errp) ++{ ++ QIOChannelSocket *cioc; ++ ++ cioc = qio_channel_socket_new(); ++ cioc->remoteAddrLen = sizeof(ioc->remoteAddr); ++ cioc->localAddrLen = sizeof(ioc->localAddr); ++ ++ retry: ++ trace_qio_channel_socket_accept(ioc); ++ cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr, ++ &cioc->remoteAddrLen); ++ if (cioc->fd < 0) { ++ if (errno == EINTR) { ++ goto retry; ++ } ++ error_setg_errno(errp, errno, "Unable to accept connection"); ++ trace_qio_channel_socket_accept_fail(ioc); ++ goto error; ++ } ++ ++ if (getsockname(cioc->fd, (struct sockaddr *)&cioc->localAddr, ++ &cioc->localAddrLen) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to query local socket address"); ++ goto error; ++ } ++ ++#ifndef WIN32 ++ if (cioc->localAddr.ss_family == AF_UNIX) { ++ QIOChannel *ioc_local = QIO_CHANNEL(cioc); ++ qio_channel_set_feature(ioc_local, QIO_CHANNEL_FEATURE_FD_PASS); ++ } ++#endif /* WIN32 */ ++ ++ qio_channel_set_feature(QIO_CHANNEL(cioc), ++ QIO_CHANNEL_FEATURE_READ_MSG_PEEK); ++ ++ trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); ++ return cioc; ++ ++ error: ++ object_unref(OBJECT(cioc)); ++ return NULL; ++} ++ ++static void qio_channel_socket_init(Object *obj) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); ++ ioc->fd = -1; ++} ++ ++static void qio_channel_socket_finalize(Object *obj) ++{ ++ QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(obj); ++ ++ if (ioc->fd != -1) { ++ QIOChannel *ioc_local = QIO_CHANNEL(ioc); ++ if (qio_channel_has_feature(ioc_local, QIO_CHANNEL_FEATURE_LISTEN)) { ++ Error *err = NULL; ++ ++ socket_listen_cleanup(ioc->fd, &err); ++ if (err) { ++ error_report_err(err); ++ err = NULL; ++ } ++ } ++#ifdef WIN32 ++ qemu_socket_unselect(ioc->fd, NULL); ++#endif ++ close(ioc->fd); ++ ioc->fd = -1; ++ } ++} ++ ++ ++#ifndef WIN32 ++static void qio_channel_socket_copy_fds(struct msghdr *msg, ++ int **fds, size_t *nfds) ++{ ++ struct cmsghdr *cmsg; ++ ++ *nfds = 0; ++ *fds = NULL; ++ ++ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { ++ int fd_size, i; ++ int gotfds; ++ ++ if (cmsg->cmsg_len < CMSG_LEN(sizeof(int)) || ++ cmsg->cmsg_level != SOL_SOCKET || ++ cmsg->cmsg_type != SCM_RIGHTS) { ++ continue; ++ } ++ ++ fd_size = cmsg->cmsg_len - CMSG_LEN(0); ++ ++ if (!fd_size) { ++ continue; ++ } ++ ++ gotfds = fd_size / sizeof(int); ++ *fds = g_renew(int, *fds, *nfds + gotfds); ++ memcpy(*fds + *nfds, CMSG_DATA(cmsg), fd_size); ++ ++ for (i = 0; i < gotfds; i++) { ++ int fd = (*fds)[*nfds + i]; ++ if (fd < 0) { ++ continue; ++ } ++ ++ /* O_NONBLOCK is preserved across SCM_RIGHTS so reset it */ ++ qemu_socket_set_block(fd); ++ ++#ifndef MSG_CMSG_CLOEXEC ++ qemu_set_cloexec(fd); ++#endif ++ } ++ *nfds += gotfds; ++ } ++} ++ ++ ++static ssize_t qio_channel_socket_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ssize_t ret; ++ struct msghdr msg = { NULL, }; ++ char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)]; ++ int sflags = 0; ++ ++ memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)); ++ ++ msg.msg_iov = (struct iovec *)iov; ++ msg.msg_iovlen = niov; ++ if (fds && nfds) { ++ msg.msg_control = control; ++ msg.msg_controllen = sizeof(control); ++#ifdef MSG_CMSG_CLOEXEC ++ sflags |= MSG_CMSG_CLOEXEC; ++#endif ++ ++ } ++ ++ if (flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) { ++ sflags |= MSG_PEEK; ++ } ++ ++ retry: ++ ret = recvmsg(sioc->fd, &msg, sflags); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ if (errno == EINTR) { ++ goto retry; ++ } ++ ++ error_setg_errno(errp, errno, ++ "Unable to read from socket"); ++ return -1; ++ } ++ ++ if (fds && nfds) { ++ qio_channel_socket_copy_fds(&msg, fds, nfds); ++ } ++ ++ return ret; ++} ++ ++static ssize_t qio_channel_socket_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ssize_t ret; ++ struct msghdr msg = { NULL, }; ++ char control[CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)]; ++ size_t fdsize = sizeof(int) * nfds; ++ struct cmsghdr *cmsg; ++ int sflags = 0; ++ ++ memset(control, 0, CMSG_SPACE(sizeof(int) * SOCKET_MAX_FDS)); ++ ++ msg.msg_iov = (struct iovec *)iov; ++ msg.msg_iovlen = niov; ++ ++ if (nfds) { ++ if (nfds > SOCKET_MAX_FDS) { ++ error_setg_errno(errp, EINVAL, ++ "Only %d FDs can be sent, got %zu", ++ SOCKET_MAX_FDS, nfds); ++ return -1; ++ } ++ ++ msg.msg_control = control; ++ msg.msg_controllen = CMSG_SPACE(sizeof(int) * nfds); ++ ++ cmsg = CMSG_FIRSTHDR(&msg); ++ cmsg->cmsg_len = CMSG_LEN(fdsize); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_type = SCM_RIGHTS; ++ memcpy(CMSG_DATA(cmsg), fds, fdsize); ++ } ++ ++ if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { ++#ifdef QEMU_MSG_ZEROCOPY ++ sflags = MSG_ZEROCOPY; ++#else ++ /* ++ * We expect QIOChannel class entry point to have ++ * blocked this code path already ++ */ ++ g_assert_not_reached(); ++#endif ++ } ++ ++ retry: ++ ret = sendmsg(sioc->fd, &msg, sflags); ++ if (ret <= 0) { ++ switch (errno) { ++ case EAGAIN: ++ return QIO_CHANNEL_ERR_BLOCK; ++ case EINTR: ++ goto retry; ++ case ENOBUFS: ++ if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { ++ error_setg_errno(errp, errno, ++ "Process can't lock enough memory for using MSG_ZEROCOPY"); ++ return -1; ++ } ++ break; ++ } ++ ++ error_setg_errno(errp, errno, ++ "Unable to write to socket"); ++ return -1; ++ } ++ ++ if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { ++ sioc->zero_copy_queued++; ++ } ++ ++ return ret; ++} ++#else /* WIN32 */ ++static ssize_t qio_channel_socket_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ssize_t done = 0; ++ ssize_t i; ++ int sflags = 0; ++ ++ if (flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) { ++ sflags |= MSG_PEEK; ++ } ++ ++ for (i = 0; i < niov; i++) { ++ ssize_t ret; ++ retry: ++ ret = recv(sioc->fd, ++ iov[i].iov_base, ++ iov[i].iov_len, ++ sflags); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ if (done) { ++ return done; ++ } else { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ } else if (errno == EINTR) { ++ goto retry; ++ } else { ++ error_setg_errno(errp, errno, ++ "Unable to read from socket"); ++ return -1; ++ } ++ } ++ done += ret; ++ if (ret < iov[i].iov_len) { ++ return done; ++ } ++ } ++ ++ return done; ++} ++ ++static ssize_t qio_channel_socket_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ssize_t done = 0; ++ ssize_t i; ++ ++ for (i = 0; i < niov; i++) { ++ ssize_t ret; ++ retry: ++ ret = send(sioc->fd, ++ iov[i].iov_base, ++ iov[i].iov_len, ++ 0); ++ if (ret < 0) { ++ if (errno == EAGAIN) { ++ if (done) { ++ return done; ++ } else { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ } else if (errno == EINTR) { ++ goto retry; ++ } else { ++ error_setg_errno(errp, errno, ++ "Unable to write to socket"); ++ return -1; ++ } ++ } ++ done += ret; ++ if (ret < iov[i].iov_len) { ++ return done; ++ } ++ } ++ ++ return done; ++} ++#endif /* WIN32 */ ++ ++ ++#ifdef QEMU_MSG_ZEROCOPY ++static int qio_channel_socket_flush(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ struct msghdr msg = {}; ++ struct sock_extended_err *serr; ++ struct cmsghdr *cm; ++ char control[CMSG_SPACE(sizeof(*serr))]; ++ int received; ++ int ret; ++ ++ if (sioc->zero_copy_queued == sioc->zero_copy_sent) { ++ return 0; ++ } ++ ++ msg.msg_control = control; ++ msg.msg_controllen = sizeof(control); ++ memset(control, 0, sizeof(control)); ++ ++ ret = 1; ++ ++ while (sioc->zero_copy_sent < sioc->zero_copy_queued) { ++ received = recvmsg(sioc->fd, &msg, MSG_ERRQUEUE); ++ if (received < 0) { ++ switch (errno) { ++ case EAGAIN: ++ /* Nothing on errqueue, wait until something is available */ ++ qio_channel_wait(ioc, G_IO_ERR); ++ continue; ++ case EINTR: ++ continue; ++ default: ++ error_setg_errno(errp, errno, ++ "Unable to read errqueue"); ++ return -1; ++ } ++ } ++ ++ cm = CMSG_FIRSTHDR(&msg); ++ if (cm->cmsg_level != SOL_IP && cm->cmsg_type != IP_RECVERR && ++ cm->cmsg_level != SOL_IPV6 && cm->cmsg_type != IPV6_RECVERR) { ++ error_setg_errno(errp, EPROTOTYPE, ++ "Wrong cmsg in errqueue"); ++ return -1; ++ } ++ ++ serr = (void *) CMSG_DATA(cm); ++ if (serr->ee_errno != SO_EE_ORIGIN_NONE) { ++ error_setg_errno(errp, serr->ee_errno, ++ "Error on socket"); ++ return -1; ++ } ++ if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY) { ++ error_setg_errno(errp, serr->ee_origin, ++ "Error not from zero copy"); ++ return -1; ++ } ++ if (serr->ee_data < serr->ee_info) { ++ error_setg_errno(errp, serr->ee_origin, ++ "Wrong notification bounds"); ++ return -1; ++ } ++ ++ /* No errors, count successfully finished sendmsg()*/ ++ sioc->zero_copy_sent += serr->ee_data - serr->ee_info + 1; ++ ++ /* If any sendmsg() succeeded using zero copy, return 0 at the end */ ++ if (serr->ee_code != SO_EE_CODE_ZEROCOPY_COPIED) { ++ ret = 0; ++ } ++ } ++ ++ return ret; ++} ++ ++#endif /* QEMU_MSG_ZEROCOPY */ ++ ++static int ++qio_channel_socket_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ++ if (enabled) { ++ qemu_socket_set_block(sioc->fd); ++ } else { ++ qemu_socket_set_nonblock(sioc->fd); ++ } ++ return 0; ++} ++ ++ ++static void ++qio_channel_socket_set_delay(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ int v = enabled ? 0 : 1; ++ ++ setsockopt(sioc->fd, ++ IPPROTO_TCP, TCP_NODELAY, ++ &v, sizeof(v)); ++} ++ ++ ++static void ++qio_channel_socket_set_cork(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ int v = enabled ? 1 : 0; ++ ++ socket_set_cork(sioc->fd, v); ++} ++ ++static int ++qio_channel_socket_get_peerpid(QIOChannel *ioc, ++ unsigned int *pid, ++ Error **errp) ++{ ++#ifdef CONFIG_LINUX ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ Error *err = NULL; ++ socklen_t len = sizeof(struct ucred); ++ ++ struct ucred cred; ++ if (getsockopt(sioc->fd, ++ SOL_SOCKET, SO_PEERCRED, ++ &cred, &len) == -1) { ++ error_setg_errno(&err, errno, "Unable to get peer credentials"); ++ error_propagate(errp, err); ++ *pid = -1; ++ return -1; ++ } ++ *pid = (unsigned int)cred.pid; ++ return 0; ++#else ++ error_setg(errp, "Unsupported feature"); ++ *pid = -1; ++ return -1; ++#endif ++} ++ ++static int ++qio_channel_socket_close(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ int rc = 0; ++ Error *err = NULL; ++ ++ if (sioc->fd != -1) { ++#ifdef WIN32 ++ qemu_socket_unselect(sioc->fd, NULL); ++#endif ++ if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_LISTEN)) { ++ socket_listen_cleanup(sioc->fd, errp); ++ } ++ ++ if (close(sioc->fd) < 0) { ++ sioc->fd = -1; ++ error_setg_errno(&err, errno, "Unable to close socket"); ++ error_propagate(errp, err); ++ return -1; ++ } ++ sioc->fd = -1; ++ } ++ return rc; ++} ++ ++static int ++qio_channel_socket_shutdown(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ int sockhow; ++ ++ switch (how) { ++ case QIO_CHANNEL_SHUTDOWN_READ: ++ sockhow = SHUT_RD; ++ break; ++ case QIO_CHANNEL_SHUTDOWN_WRITE: ++ sockhow = SHUT_WR; ++ break; ++ case QIO_CHANNEL_SHUTDOWN_BOTH: ++ default: ++ sockhow = SHUT_RDWR; ++ break; ++ } ++ ++ if (shutdown(sioc->fd, sockhow) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to shutdown socket"); ++ return -1; ++ } ++ return 0; ++} ++ ++static void qio_channel_socket_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ ++ qio_channel_util_set_aio_fd_handler(sioc->fd, read_ctx, io_read, ++ sioc->fd, write_ctx, io_write, ++ opaque); ++} ++ ++static GSource *qio_channel_socket_create_watch(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); ++ return qio_channel_create_socket_watch(ioc, ++ sioc->fd, ++ condition); ++} ++ ++static void qio_channel_socket_class_init(ObjectClass *klass, ++ void *class_data G_GNUC_UNUSED) ++{ ++ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); ++ ++ ioc_klass->io_writev = qio_channel_socket_writev; ++ ioc_klass->io_readv = qio_channel_socket_readv; ++ ioc_klass->io_set_blocking = qio_channel_socket_set_blocking; ++ ioc_klass->io_close = qio_channel_socket_close; ++ ioc_klass->io_shutdown = qio_channel_socket_shutdown; ++ ioc_klass->io_set_cork = qio_channel_socket_set_cork; ++ ioc_klass->io_set_delay = qio_channel_socket_set_delay; ++ ioc_klass->io_create_watch = qio_channel_socket_create_watch; ++ ioc_klass->io_set_aio_fd_handler = qio_channel_socket_set_aio_fd_handler; ++#ifdef QEMU_MSG_ZEROCOPY ++ ioc_klass->io_flush = qio_channel_socket_flush; ++#endif ++ ioc_klass->io_peerpid = qio_channel_socket_get_peerpid; ++} ++ ++static const TypeInfo qio_channel_socket_info = { ++ .parent = TYPE_QIO_CHANNEL, ++ .name = TYPE_QIO_CHANNEL_SOCKET, ++ .instance_size = sizeof(QIOChannelSocket), ++ .instance_init = qio_channel_socket_init, ++ .instance_finalize = qio_channel_socket_finalize, ++ .class_init = qio_channel_socket_class_init, ++}; ++ ++static void qio_channel_socket_register_types(void) ++{ ++ type_register_static(&qio_channel_socket_info); ++} ++ ++type_init(qio_channel_socket_register_types); +diff --git a/qcow2/lib/io/channel-tls.c b/qcow2/lib/io/channel-tls.c +new file mode 100644 +index 00000000..aab630e5 +--- /dev/null ++++ b/qcow2/lib/io/channel-tls.c +@@ -0,0 +1,507 @@ ++/* ++ * QEMU I/O channels TLS driver ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/module.h" ++#include "io/channel-tls.h" ++#include "trace.h" ++#include "qemu/atomic.h" ++ ++ ++static ssize_t qio_channel_tls_write_handler(const char *buf, ++ size_t len, ++ void *opaque, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); ++ ssize_t ret; ++ ++ ret = qio_channel_write(tioc->master, buf, len, errp); ++ if (ret == QIO_CHANNEL_ERR_BLOCK) { ++ return QCRYPTO_TLS_SESSION_ERR_BLOCK; ++ } else if (ret < 0) { ++ return -1; ++ } ++ return ret; ++} ++ ++static ssize_t qio_channel_tls_read_handler(char *buf, ++ size_t len, ++ void *opaque, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); ++ ssize_t ret; ++ ++ ret = qio_channel_read(tioc->master, buf, len, errp); ++ if (ret == QIO_CHANNEL_ERR_BLOCK) { ++ return QCRYPTO_TLS_SESSION_ERR_BLOCK; ++ } else if (ret < 0) { ++ return -1; ++ } ++ return ret; ++} ++ ++ ++QIOChannelTLS * ++qio_channel_tls_new_server(QIOChannel *master, ++ QCryptoTLSCreds *creds, ++ const char *aclname, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc; ++ QIOChannel *ioc; ++ ++ tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); ++ ioc = QIO_CHANNEL(tioc); ++ ++ tioc->master = master; ++ ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; ++ if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { ++ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); ++ } ++ object_ref(OBJECT(master)); ++ ++ tioc->session = qcrypto_tls_session_new( ++ creds, ++ NULL, ++ aclname, ++ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, ++ errp); ++ if (!tioc->session) { ++ goto error; ++ } ++ ++ qcrypto_tls_session_set_callbacks( ++ tioc->session, ++ qio_channel_tls_write_handler, ++ qio_channel_tls_read_handler, ++ tioc); ++ ++ trace_qio_channel_tls_new_server(tioc, master, creds, aclname); ++ return tioc; ++ ++ error: ++ object_unref(OBJECT(tioc)); ++ return NULL; ++} ++ ++QIOChannelTLS * ++qio_channel_tls_new_client(QIOChannel *master, ++ QCryptoTLSCreds *creds, ++ const char *hostname, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc; ++ QIOChannel *ioc; ++ ++ tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); ++ ioc = QIO_CHANNEL(tioc); ++ ++ tioc->master = master; ++ ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; ++ if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { ++ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); ++ } ++ object_ref(OBJECT(master)); ++ ++ tioc->session = qcrypto_tls_session_new( ++ creds, ++ hostname, ++ NULL, ++ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, ++ errp); ++ if (!tioc->session) { ++ goto error; ++ } ++ ++ qcrypto_tls_session_set_callbacks( ++ tioc->session, ++ qio_channel_tls_write_handler, ++ qio_channel_tls_read_handler, ++ tioc); ++ ++ trace_qio_channel_tls_new_client(tioc, master, creds, hostname); ++ return tioc; ++ ++ error: ++ object_unref(OBJECT(tioc)); ++ return NULL; ++} ++ ++struct QIOChannelTLSData { ++ QIOTask *task; ++ GMainContext *context; ++}; ++typedef struct QIOChannelTLSData QIOChannelTLSData; ++ ++static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, ++ GIOCondition condition, ++ gpointer user_data); ++ ++static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, ++ QIOTask *task, ++ GMainContext *context) ++{ ++ Error *err = NULL; ++ QCryptoTLSSessionHandshakeStatus status; ++ ++ if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { ++ trace_qio_channel_tls_handshake_fail(ioc); ++ qio_task_set_error(task, err); ++ qio_task_complete(task); ++ return; ++ } ++ ++ status = qcrypto_tls_session_get_handshake_status(ioc->session); ++ if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { ++ trace_qio_channel_tls_handshake_complete(ioc); ++ if (qcrypto_tls_session_check_credentials(ioc->session, ++ &err) < 0) { ++ trace_qio_channel_tls_credentials_deny(ioc); ++ qio_task_set_error(task, err); ++ } else { ++ trace_qio_channel_tls_credentials_allow(ioc); ++ } ++ qio_task_complete(task); ++ } else { ++ GIOCondition condition; ++ QIOChannelTLSData *data = g_new0(typeof(*data), 1); ++ ++ data->task = task; ++ data->context = context; ++ ++ if (context) { ++ g_main_context_ref(context); ++ } ++ ++ if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) { ++ condition = G_IO_OUT; ++ } else { ++ condition = G_IO_IN; ++ } ++ ++ trace_qio_channel_tls_handshake_pending(ioc, status); ++ ioc->hs_ioc_tag = ++ qio_channel_add_watch_full(ioc->master, ++ condition, ++ qio_channel_tls_handshake_io, ++ data, ++ NULL, ++ context); ++ } ++} ++ ++ ++static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, ++ GIOCondition condition, ++ gpointer user_data) ++{ ++ QIOChannelTLSData *data = user_data; ++ QIOTask *task = data->task; ++ GMainContext *context = data->context; ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS( ++ qio_task_get_source(task)); ++ ++ tioc->hs_ioc_tag = 0; ++ g_free(data); ++ qio_channel_tls_handshake_task(tioc, task, context); ++ ++ if (context) { ++ g_main_context_unref(context); ++ } ++ ++ return FALSE; ++} ++ ++void qio_channel_tls_handshake(QIOChannelTLS *ioc, ++ QIOTaskFunc func, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ QIOTask *task; ++ ++ task = qio_task_new(OBJECT(ioc), ++ func, opaque, destroy); ++ ++ trace_qio_channel_tls_handshake_start(ioc); ++ qio_channel_tls_handshake_task(ioc, task, context); ++} ++ ++ ++static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) ++{ ++} ++ ++ ++static void qio_channel_tls_finalize(Object *obj) ++{ ++ QIOChannelTLS *ioc = QIO_CHANNEL_TLS(obj); ++ ++ object_unref(OBJECT(ioc->master)); ++ qcrypto_tls_session_free(ioc->session); ++} ++ ++ ++static ssize_t qio_channel_tls_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ size_t i; ++ ssize_t got = 0; ++ ++ for (i = 0 ; i < niov ; i++) { ++ ssize_t ret = qcrypto_tls_session_read( ++ tioc->session, ++ iov[i].iov_base, ++ iov[i].iov_len, ++ qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ, ++ errp); ++ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { ++ if (got) { ++ return got; ++ } else { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ } else if (ret < 0) { ++ return -1; ++ } ++ got += ret; ++ if (ret < iov[i].iov_len) { ++ break; ++ } ++ } ++ return got; ++} ++ ++ ++static ssize_t qio_channel_tls_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ size_t i; ++ ssize_t done = 0; ++ ++ for (i = 0 ; i < niov ; i++) { ++ ssize_t ret = qcrypto_tls_session_write(tioc->session, ++ iov[i].iov_base, ++ iov[i].iov_len, ++ errp); ++ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { ++ if (done) { ++ return done; ++ } else { ++ return QIO_CHANNEL_ERR_BLOCK; ++ } ++ } else if (ret < 0) { ++ return -1; ++ } ++ done += ret; ++ if (ret < iov[i].iov_len) { ++ break; ++ } ++ } ++ return done; ++} ++ ++static int qio_channel_tls_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ ++ return qio_channel_set_blocking(tioc->master, enabled, errp); ++} ++ ++static void qio_channel_tls_set_delay(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ ++ qio_channel_set_delay(tioc->master, enabled); ++} ++ ++static void qio_channel_tls_set_cork(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ ++ qio_channel_set_cork(tioc->master, enabled); ++} ++ ++static int qio_channel_tls_shutdown(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ ++ qatomic_or(&tioc->shutdown, how); ++ ++ return qio_channel_shutdown(tioc->master, how, errp); ++} ++ ++static int qio_channel_tls_close(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ ++ if (tioc->hs_ioc_tag) { ++ trace_qio_channel_tls_handshake_cancel(ioc); ++ g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); ++ } ++ ++ return qio_channel_close(tioc->master, errp); ++} ++ ++static void qio_channel_tls_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ ++ qio_channel_set_aio_fd_handler(tioc->master, read_ctx, io_read, ++ write_ctx, io_write, opaque); ++} ++ ++typedef struct QIOChannelTLSSource QIOChannelTLSSource; ++struct QIOChannelTLSSource { ++ GSource parent; ++ QIOChannelTLS *tioc; ++}; ++ ++static gboolean ++qio_channel_tls_source_check(GSource *source) ++{ ++ QIOChannelTLSSource *tsource = (QIOChannelTLSSource *)source; ++ ++ return qcrypto_tls_session_check_pending(tsource->tioc->session) > 0; ++} ++ ++static gboolean ++qio_channel_tls_source_prepare(GSource *source, gint *timeout) ++{ ++ *timeout = -1; ++ return qio_channel_tls_source_check(source); ++} ++ ++static gboolean ++qio_channel_tls_source_dispatch(GSource *source, GSourceFunc callback, ++ gpointer user_data) ++{ ++ return G_SOURCE_CONTINUE; ++} ++ ++static void ++qio_channel_tls_source_finalize(GSource *source) ++{ ++ QIOChannelTLSSource *tsource = (QIOChannelTLSSource *)source; ++ ++ object_unref(OBJECT(tsource->tioc)); ++} ++ ++static GSourceFuncs qio_channel_tls_source_funcs = { ++ qio_channel_tls_source_prepare, ++ qio_channel_tls_source_check, ++ qio_channel_tls_source_dispatch, ++ qio_channel_tls_source_finalize ++}; ++ ++static void ++qio_channel_tls_read_watch(QIOChannelTLS *tioc, GSource *source) ++{ ++ GSource *child; ++ QIOChannelTLSSource *tlssource; ++ ++ child = g_source_new(&qio_channel_tls_source_funcs, ++ sizeof(QIOChannelTLSSource)); ++ tlssource = (QIOChannelTLSSource *)child; ++ ++ tlssource->tioc = tioc; ++ object_ref(OBJECT(tioc)); ++ ++ g_source_add_child_source(source, child); ++ g_source_unref(child); ++} ++ ++static GSource *qio_channel_tls_create_watch(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); ++ GSource *source = qio_channel_create_watch(tioc->master, condition); ++ ++ if (condition & G_IO_IN) { ++ qio_channel_tls_read_watch(tioc, source); ++ } ++ ++ return source; ++} ++ ++QCryptoTLSSession * ++qio_channel_tls_get_session(QIOChannelTLS *ioc) ++{ ++ return ioc->session; ++} ++ ++static void qio_channel_tls_class_init(ObjectClass *klass, ++ void *class_data G_GNUC_UNUSED) ++{ ++ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); ++ ++ ioc_klass->io_writev = qio_channel_tls_writev; ++ ioc_klass->io_readv = qio_channel_tls_readv; ++ ioc_klass->io_set_blocking = qio_channel_tls_set_blocking; ++ ioc_klass->io_set_delay = qio_channel_tls_set_delay; ++ ioc_klass->io_set_cork = qio_channel_tls_set_cork; ++ ioc_klass->io_close = qio_channel_tls_close; ++ ioc_klass->io_shutdown = qio_channel_tls_shutdown; ++ ioc_klass->io_create_watch = qio_channel_tls_create_watch; ++ ioc_klass->io_set_aio_fd_handler = qio_channel_tls_set_aio_fd_handler; ++} ++ ++static const TypeInfo qio_channel_tls_info = { ++ .parent = TYPE_QIO_CHANNEL, ++ .name = TYPE_QIO_CHANNEL_TLS, ++ .instance_size = sizeof(QIOChannelTLS), ++ .instance_init = qio_channel_tls_init, ++ .instance_finalize = qio_channel_tls_finalize, ++ .class_init = qio_channel_tls_class_init, ++}; ++ ++static void qio_channel_tls_register_types(void) ++{ ++ type_register_static(&qio_channel_tls_info); ++} ++ ++type_init(qio_channel_tls_register_types); +diff --git a/qcow2/lib/io/channel-util.c b/qcow2/lib/io/channel-util.c +new file mode 100644 +index 00000000..4b340d46 +--- /dev/null ++++ b/qcow2/lib/io/channel-util.c +@@ -0,0 +1,62 @@ ++/* ++ * QEMU I/O channels utility APIs ++ * ++ * Copyright (c) 2016 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "io/channel-util.h" ++#include "io/channel-file.h" ++#include "io/channel-socket.h" ++ ++ ++QIOChannel *qio_channel_new_fd(int fd, ++ Error **errp) ++{ ++ QIOChannel *ioc; ++ ++ if (fd_is_socket(fd)) { ++ ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, errp)); ++ } else { ++ ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); ++ } ++ return ioc; ++} ++ ++ ++void qio_channel_util_set_aio_fd_handler(int read_fd, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ int write_fd, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ if (read_fd == write_fd && read_ctx == write_ctx) { ++ aio_set_fd_handler(read_ctx, read_fd, io_read, io_write, ++ NULL, NULL, opaque); ++ } else { ++ if (read_ctx) { ++ aio_set_fd_handler(read_ctx, read_fd, io_read, NULL, ++ NULL, NULL, opaque); ++ } ++ if (write_ctx) { ++ aio_set_fd_handler(write_ctx, write_fd, NULL, io_write, ++ NULL, NULL, opaque); ++ } ++ } ++} +diff --git a/qcow2/lib/io/channel-watch.c b/qcow2/lib/io/channel-watch.c +new file mode 100644 +index 00000000..64b486e3 +--- /dev/null ++++ b/qcow2/lib/io/channel-watch.c +@@ -0,0 +1,347 @@ ++/* ++ * QEMU I/O channels watch helper APIs ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "io/channel-watch.h" ++ ++typedef struct QIOChannelFDSource QIOChannelFDSource; ++struct QIOChannelFDSource { ++ GSource parent; ++ GPollFD fd; ++ QIOChannel *ioc; ++ GIOCondition condition; ++}; ++ ++ ++#ifdef CONFIG_WIN32 ++typedef struct QIOChannelSocketSource QIOChannelSocketSource; ++struct QIOChannelSocketSource { ++ GSource parent; ++ GPollFD fd; ++ QIOChannel *ioc; ++ SOCKET socket; ++ int revents; ++ GIOCondition condition; ++}; ++ ++#endif ++ ++ ++typedef struct QIOChannelFDPairSource QIOChannelFDPairSource; ++struct QIOChannelFDPairSource { ++ GSource parent; ++ GPollFD fdread; ++ GPollFD fdwrite; ++ QIOChannel *ioc; ++ GIOCondition condition; ++}; ++ ++ ++static gboolean ++qio_channel_fd_source_prepare(GSource *source G_GNUC_UNUSED, ++ gint *timeout) ++{ ++ *timeout = -1; ++ ++ return FALSE; ++} ++ ++ ++static gboolean ++qio_channel_fd_source_check(GSource *source) ++{ ++ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; ++ ++ return ssource->fd.revents & ssource->condition; ++} ++ ++ ++static gboolean ++qio_channel_fd_source_dispatch(GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ QIOChannelFunc func = (QIOChannelFunc)callback; ++ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; ++ ++ return (*func)(ssource->ioc, ++ ssource->fd.revents & ssource->condition, ++ user_data); ++} ++ ++ ++static void ++qio_channel_fd_source_finalize(GSource *source) ++{ ++ QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; ++ ++ object_unref(OBJECT(ssource->ioc)); ++} ++ ++ ++#ifdef CONFIG_WIN32 ++static gboolean ++qio_channel_socket_source_prepare(GSource *source G_GNUC_UNUSED, ++ gint *timeout) ++{ ++ *timeout = -1; ++ ++ return FALSE; ++} ++ ++ ++/* ++ * NB, this impl only works when the socket is in non-blocking ++ * mode on Win32 ++ */ ++static gboolean ++qio_channel_socket_source_check(GSource *source) ++{ ++ static struct timeval tv0; ++ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; ++ fd_set rfds, wfds, xfds; ++ ++ if (!ssource->condition) { ++ return 0; ++ } ++ ++ FD_ZERO(&rfds); ++ FD_ZERO(&wfds); ++ FD_ZERO(&xfds); ++ if (ssource->condition & G_IO_IN) { ++ FD_SET(ssource->socket, &rfds); ++ } ++ if (ssource->condition & G_IO_OUT) { ++ FD_SET(ssource->socket, &wfds); ++ } ++ if (ssource->condition & G_IO_PRI) { ++ FD_SET(ssource->socket, &xfds); ++ } ++ ssource->revents = 0; ++ if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) { ++ return 0; ++ } ++ ++ if (FD_ISSET(ssource->socket, &rfds)) { ++ ssource->revents |= G_IO_IN; ++ } ++ if (FD_ISSET(ssource->socket, &wfds)) { ++ ssource->revents |= G_IO_OUT; ++ } ++ if (FD_ISSET(ssource->socket, &xfds)) { ++ ssource->revents |= G_IO_PRI; ++ } ++ ++ return ssource->revents; ++} ++ ++ ++static gboolean ++qio_channel_socket_source_dispatch(GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ QIOChannelFunc func = (QIOChannelFunc)callback; ++ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; ++ ++ return (*func)(ssource->ioc, ssource->revents, user_data); ++} ++ ++ ++static void ++qio_channel_socket_source_finalize(GSource *source) ++{ ++ QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; ++ ++ object_unref(OBJECT(ssource->ioc)); ++} ++ ++ ++GSourceFuncs qio_channel_socket_source_funcs = { ++ qio_channel_socket_source_prepare, ++ qio_channel_socket_source_check, ++ qio_channel_socket_source_dispatch, ++ qio_channel_socket_source_finalize ++}; ++#endif ++ ++ ++static gboolean ++qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED, ++ gint *timeout) ++{ ++ *timeout = -1; ++ ++ return FALSE; ++} ++ ++ ++static gboolean ++qio_channel_fd_pair_source_check(GSource *source) ++{ ++ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; ++ GIOCondition poll_condition = ssource->fdread.revents | ++ ssource->fdwrite.revents; ++ ++ return poll_condition & ssource->condition; ++} ++ ++ ++static gboolean ++qio_channel_fd_pair_source_dispatch(GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ QIOChannelFunc func = (QIOChannelFunc)callback; ++ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; ++ GIOCondition poll_condition = ssource->fdread.revents | ++ ssource->fdwrite.revents; ++ ++ return (*func)(ssource->ioc, ++ poll_condition & ssource->condition, ++ user_data); ++} ++ ++ ++static void ++qio_channel_fd_pair_source_finalize(GSource *source) ++{ ++ QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; ++ ++ object_unref(OBJECT(ssource->ioc)); ++} ++ ++ ++GSourceFuncs qio_channel_fd_source_funcs = { ++ qio_channel_fd_source_prepare, ++ qio_channel_fd_source_check, ++ qio_channel_fd_source_dispatch, ++ qio_channel_fd_source_finalize ++}; ++ ++ ++GSourceFuncs qio_channel_fd_pair_source_funcs = { ++ qio_channel_fd_pair_source_prepare, ++ qio_channel_fd_pair_source_check, ++ qio_channel_fd_pair_source_dispatch, ++ qio_channel_fd_pair_source_finalize ++}; ++ ++ ++GSource *qio_channel_create_fd_watch(QIOChannel *ioc, ++ int fd, ++ GIOCondition condition) ++{ ++ GSource *source; ++ QIOChannelFDSource *ssource; ++ ++ source = g_source_new(&qio_channel_fd_source_funcs, ++ sizeof(QIOChannelFDSource)); ++ ssource = (QIOChannelFDSource *)source; ++ ++ ssource->ioc = ioc; ++ object_ref(OBJECT(ioc)); ++ ++ ssource->condition = condition; ++ ++#ifdef CONFIG_WIN32 ++ ssource->fd.fd = (gint64)_get_osfhandle(fd); ++#else ++ ssource->fd.fd = fd; ++#endif ++ ssource->fd.events = condition; ++ ++ g_source_add_poll(source, &ssource->fd); ++ ++ return source; ++} ++ ++#ifdef CONFIG_WIN32 ++GSource *qio_channel_create_socket_watch(QIOChannel *ioc, ++ int sockfd, ++ GIOCondition condition) ++{ ++ GSource *source; ++ QIOChannelSocketSource *ssource; ++ ++ qemu_socket_select(sockfd, ioc->event, ++ FD_READ | FD_ACCEPT | FD_CLOSE | ++ FD_CONNECT | FD_WRITE | FD_OOB, NULL); ++ ++ source = g_source_new(&qio_channel_socket_source_funcs, ++ sizeof(QIOChannelSocketSource)); ++ ssource = (QIOChannelSocketSource *)source; ++ ++ ssource->ioc = ioc; ++ object_ref(OBJECT(ioc)); ++ ++ ssource->condition = condition; ++ ssource->socket = _get_osfhandle(sockfd); ++ ssource->revents = 0; ++ ++ ssource->fd.fd = (gintptr)ioc->event; ++ ssource->fd.events = G_IO_IN; ++ ++ g_source_add_poll(source, &ssource->fd); ++ ++ return source; ++} ++#else ++GSource *qio_channel_create_socket_watch(QIOChannel *ioc, ++ int socket, ++ GIOCondition condition) ++{ ++ return qio_channel_create_fd_watch(ioc, socket, condition); ++} ++#endif ++ ++GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc, ++ int fdread, ++ int fdwrite, ++ GIOCondition condition) ++{ ++ GSource *source; ++ QIOChannelFDPairSource *ssource; ++ ++ source = g_source_new(&qio_channel_fd_pair_source_funcs, ++ sizeof(QIOChannelFDPairSource)); ++ ssource = (QIOChannelFDPairSource *)source; ++ ++ ssource->ioc = ioc; ++ object_ref(OBJECT(ioc)); ++ ++ ssource->condition = condition; ++ ++#ifdef CONFIG_WIN32 ++ ssource->fdread.fd = (gint64)_get_osfhandle(fdread); ++ ssource->fdwrite.fd = (gint64)_get_osfhandle(fdwrite); ++#else ++ ssource->fdread.fd = fdread; ++ ssource->fdwrite.fd = fdwrite; ++#endif ++ ++ ssource->fdread.events = condition & G_IO_IN; ++ ssource->fdwrite.events = condition & G_IO_OUT; ++ ++ g_source_add_poll(source, &ssource->fdread); ++ g_source_add_poll(source, &ssource->fdwrite); ++ ++ return source; ++} +diff --git a/qcow2/lib/io/channel.c b/qcow2/lib/io/channel.c +new file mode 100644 +index 00000000..e3f17c24 +--- /dev/null ++++ b/qcow2/lib/io/channel.c +@@ -0,0 +1,808 @@ ++/* ++ * QEMU I/O channels ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/aio-wait.h" ++#include "io/channel.h" ++#include "qapi/error.h" ++#include "qemu/main-loop.h" ++#include "qemu/module.h" ++#include "qemu/iov.h" ++ ++bool qio_channel_has_feature(QIOChannel *ioc, ++ QIOChannelFeature feature) ++{ ++ return ioc->features & (1 << feature); ++} ++ ++ ++void qio_channel_set_feature(QIOChannel *ioc, ++ QIOChannelFeature feature) ++{ ++ ioc->features |= (1 << feature); ++} ++ ++ ++void qio_channel_set_name(QIOChannel *ioc, ++ const char *name) ++{ ++ g_free(ioc->name); ++ ioc->name = g_strdup(name); ++} ++ ++ ++ssize_t qio_channel_readv_full(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, ++ size_t *nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if ((fds || nfds) && ++ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { ++ error_setg_errno(errp, EINVAL, ++ "Channel does not support file descriptor passing"); ++ return -1; ++ } ++ ++ if ((flags & QIO_CHANNEL_READ_FLAG_MSG_PEEK) && ++ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) { ++ error_setg_errno(errp, EINVAL, ++ "Channel does not support peek read"); ++ return -1; ++ } ++ ++ return klass->io_readv(ioc, iov, niov, fds, nfds, flags, errp); ++} ++ ++ ++ssize_t qio_channel_writev_full(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, ++ size_t nfds, ++ int flags, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (fds || nfds) { ++ if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { ++ error_setg_errno(errp, EINVAL, ++ "Channel does not support file descriptor passing"); ++ return -1; ++ } ++ if (flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) { ++ error_setg_errno(errp, EINVAL, ++ "Zero Copy does not support file descriptor passing"); ++ return -1; ++ } ++ } ++ ++ if ((flags & QIO_CHANNEL_WRITE_FLAG_ZERO_COPY) && ++ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) { ++ error_setg_errno(errp, EINVAL, ++ "Requested Zero Copy feature is not available"); ++ return -1; ++ } ++ ++ return klass->io_writev(ioc, iov, niov, fds, nfds, flags, errp); ++} ++ ++ ++int coroutine_mixed_fn qio_channel_readv_all_eof(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_readv_full_all_eof(ioc, iov, niov, NULL, NULL, errp); ++} ++ ++int coroutine_mixed_fn qio_channel_readv_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_readv_full_all(ioc, iov, niov, NULL, NULL, errp); ++} ++ ++int coroutine_mixed_fn qio_channel_readv_full_all_eof(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, size_t *nfds, ++ Error **errp) ++{ ++ int ret = -1; ++ struct iovec *local_iov = g_new(struct iovec, niov); ++ struct iovec *local_iov_head = local_iov; ++ unsigned int nlocal_iov = niov; ++ int **local_fds = fds; ++ size_t *local_nfds = nfds; ++ bool partial = false; ++ ++ if (nfds) { ++ *nfds = 0; ++ } ++ ++ if (fds) { ++ *fds = NULL; ++ } ++ ++ nlocal_iov = iov_copy(local_iov, nlocal_iov, ++ iov, niov, ++ 0, iov_size(iov, niov)); ++ ++ while ((nlocal_iov > 0) || local_fds) { ++ ssize_t len; ++ len = qio_channel_readv_full(ioc, local_iov, nlocal_iov, local_fds, ++ local_nfds, 0, errp); ++ if (len == QIO_CHANNEL_ERR_BLOCK) { ++ if (qemu_in_coroutine()) { ++ qio_channel_yield(ioc, G_IO_IN); ++ } else { ++ qio_channel_wait(ioc, G_IO_IN); ++ } ++ continue; ++ } ++ ++ if (len == 0) { ++ if (local_nfds && *local_nfds) { ++ /* ++ * Got some FDs, but no data yet. This isn't an EOF ++ * scenario (yet), so carry on to try to read data ++ * on next loop iteration ++ */ ++ goto next_iter; ++ } else if (!partial) { ++ /* No fds and no data - EOF before any data read */ ++ ret = 0; ++ goto cleanup; ++ } else { ++ len = -1; ++ error_setg(errp, ++ "Unexpected end-of-file before all data were read"); ++ /* Fallthrough into len < 0 handling */ ++ } ++ } ++ ++ if (len < 0) { ++ /* Close any FDs we previously received */ ++ if (nfds && fds) { ++ size_t i; ++ for (i = 0; i < (*nfds); i++) { ++ close((*fds)[i]); ++ } ++ g_free(*fds); ++ *fds = NULL; ++ *nfds = 0; ++ } ++ goto cleanup; ++ } ++ ++ if (nlocal_iov) { ++ iov_discard_front(&local_iov, &nlocal_iov, len); ++ } ++ ++next_iter: ++ partial = true; ++ local_fds = NULL; ++ local_nfds = NULL; ++ } ++ ++ ret = 1; ++ ++ cleanup: ++ g_free(local_iov_head); ++ return ret; ++} ++ ++int coroutine_mixed_fn qio_channel_readv_full_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int **fds, size_t *nfds, ++ Error **errp) ++{ ++ int ret = qio_channel_readv_full_all_eof(ioc, iov, niov, fds, nfds, errp); ++ ++ if (ret == 0) { ++ error_setg(errp, "Unexpected end-of-file before all data were read"); ++ return -1; ++ } ++ if (ret == 1) { ++ return 0; ++ } ++ ++ return ret; ++} ++ ++int coroutine_mixed_fn qio_channel_writev_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_writev_full_all(ioc, iov, niov, NULL, 0, 0, errp); ++} ++ ++int coroutine_mixed_fn qio_channel_writev_full_all(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ int *fds, size_t nfds, ++ int flags, Error **errp) ++{ ++ int ret = -1; ++ struct iovec *local_iov = g_new(struct iovec, niov); ++ struct iovec *local_iov_head = local_iov; ++ unsigned int nlocal_iov = niov; ++ ++ nlocal_iov = iov_copy(local_iov, nlocal_iov, ++ iov, niov, ++ 0, iov_size(iov, niov)); ++ ++ while (nlocal_iov > 0) { ++ ssize_t len; ++ ++ len = qio_channel_writev_full(ioc, local_iov, nlocal_iov, fds, ++ nfds, flags, errp); ++ ++ if (len == QIO_CHANNEL_ERR_BLOCK) { ++ if (qemu_in_coroutine()) { ++ qio_channel_yield(ioc, G_IO_OUT); ++ } else { ++ qio_channel_wait(ioc, G_IO_OUT); ++ } ++ continue; ++ } ++ if (len < 0) { ++ goto cleanup; ++ } ++ ++ iov_discard_front(&local_iov, &nlocal_iov, len); ++ ++ fds = NULL; ++ nfds = 0; ++ } ++ ++ ret = 0; ++ cleanup: ++ g_free(local_iov_head); ++ return ret; ++} ++ ++ssize_t qio_channel_readv(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_readv_full(ioc, iov, niov, NULL, NULL, 0, errp); ++} ++ ++ ++ssize_t qio_channel_writev(QIOChannel *ioc, ++ const struct iovec *iov, ++ size_t niov, ++ Error **errp) ++{ ++ return qio_channel_writev_full(ioc, iov, niov, NULL, 0, 0, errp); ++} ++ ++ ++ssize_t qio_channel_read(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = buf, .iov_len = buflen }; ++ return qio_channel_readv_full(ioc, &iov, 1, NULL, NULL, 0, errp); ++} ++ ++ ++ssize_t qio_channel_write(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; ++ return qio_channel_writev_full(ioc, &iov, 1, NULL, 0, 0, errp); ++} ++ ++ ++int coroutine_mixed_fn qio_channel_read_all_eof(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = buf, .iov_len = buflen }; ++ return qio_channel_readv_all_eof(ioc, &iov, 1, errp); ++} ++ ++ ++int coroutine_mixed_fn qio_channel_read_all(QIOChannel *ioc, ++ char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = buf, .iov_len = buflen }; ++ return qio_channel_readv_all(ioc, &iov, 1, errp); ++} ++ ++ ++int coroutine_mixed_fn qio_channel_write_all(QIOChannel *ioc, ++ const char *buf, ++ size_t buflen, ++ Error **errp) ++{ ++ struct iovec iov = { .iov_base = (char *)buf, .iov_len = buflen }; ++ return qio_channel_writev_all(ioc, &iov, 1, errp); ++} ++ ++ ++int qio_channel_set_blocking(QIOChannel *ioc, ++ bool enabled, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ return klass->io_set_blocking(ioc, enabled, errp); ++} ++ ++ ++void qio_channel_set_follow_coroutine_ctx(QIOChannel *ioc, bool enabled) ++{ ++ ioc->follow_coroutine_ctx = enabled; ++} ++ ++ ++int qio_channel_close(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ return klass->io_close(ioc, errp); ++} ++ ++ ++GSource *qio_channel_create_watch(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ GSource *ret = klass->io_create_watch(ioc, condition); ++ ++ if (ioc->name) { ++ g_source_set_name(ret, ioc->name); ++ } ++ ++ return ret; ++} ++ ++ ++void qio_channel_set_aio_fd_handler(QIOChannel *ioc, ++ AioContext *read_ctx, ++ IOHandler *io_read, ++ AioContext *write_ctx, ++ IOHandler *io_write, ++ void *opaque) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ klass->io_set_aio_fd_handler(ioc, read_ctx, io_read, write_ctx, io_write, ++ opaque); ++} ++ ++guint qio_channel_add_watch_full(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify, ++ GMainContext *context) ++{ ++ GSource *source; ++ guint id; ++ ++ source = qio_channel_create_watch(ioc, condition); ++ ++ g_source_set_callback(source, (GSourceFunc)func, user_data, notify); ++ ++ id = g_source_attach(source, context); ++ g_source_unref(source); ++ ++ return id; ++} ++ ++guint qio_channel_add_watch(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify) ++{ ++ return qio_channel_add_watch_full(ioc, condition, func, ++ user_data, notify, NULL); ++} ++ ++GSource *qio_channel_add_watch_source(QIOChannel *ioc, ++ GIOCondition condition, ++ QIOChannelFunc func, ++ gpointer user_data, ++ GDestroyNotify notify, ++ GMainContext *context) ++{ ++ GSource *source; ++ guint id; ++ ++ id = qio_channel_add_watch_full(ioc, condition, func, ++ user_data, notify, context); ++ source = g_main_context_find_source_by_id(context, id); ++ g_source_ref(source); ++ return source; ++} ++ ++ ++ssize_t qio_channel_pwritev(QIOChannel *ioc, const struct iovec *iov, ++ size_t niov, off_t offset, Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_pwritev) { ++ error_setg(errp, "Channel does not support pwritev"); ++ return -1; ++ } ++ ++ if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) { ++ error_setg_errno(errp, EINVAL, "Requested channel is not seekable"); ++ return -1; ++ } ++ ++ return klass->io_pwritev(ioc, iov, niov, offset, errp); ++} ++ ++ssize_t qio_channel_pwrite(QIOChannel *ioc, char *buf, size_t buflen, ++ off_t offset, Error **errp) ++{ ++ struct iovec iov = { ++ .iov_base = buf, ++ .iov_len = buflen ++ }; ++ ++ return qio_channel_pwritev(ioc, &iov, 1, offset, errp); ++} ++ ++ssize_t qio_channel_preadv(QIOChannel *ioc, const struct iovec *iov, ++ size_t niov, off_t offset, Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_preadv) { ++ error_setg(errp, "Channel does not support preadv"); ++ return -1; ++ } ++ ++ if (!qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_SEEKABLE)) { ++ error_setg_errno(errp, EINVAL, "Requested channel is not seekable"); ++ return -1; ++ } ++ ++ return klass->io_preadv(ioc, iov, niov, offset, errp); ++} ++ ++ssize_t qio_channel_pread(QIOChannel *ioc, char *buf, size_t buflen, ++ off_t offset, Error **errp) ++{ ++ struct iovec iov = { ++ .iov_base = buf, ++ .iov_len = buflen ++ }; ++ ++ return qio_channel_preadv(ioc, &iov, 1, offset, errp); ++} ++ ++int qio_channel_shutdown(QIOChannel *ioc, ++ QIOChannelShutdown how, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_shutdown) { ++ error_setg(errp, "Data path shutdown not supported"); ++ return -1; ++ } ++ ++ return klass->io_shutdown(ioc, how, errp); ++} ++ ++ ++void qio_channel_set_delay(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (klass->io_set_delay) { ++ klass->io_set_delay(ioc, enabled); ++ } ++} ++ ++ ++void qio_channel_set_cork(QIOChannel *ioc, ++ bool enabled) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (klass->io_set_cork) { ++ klass->io_set_cork(ioc, enabled); ++ } ++} ++ ++int qio_channel_get_peerpid(QIOChannel *ioc, ++ unsigned int *pid, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_peerpid) { ++ error_setg(errp, "Channel does not support peer pid"); ++ return -1; ++ } ++ klass->io_peerpid(ioc, pid, errp); ++ return 0; ++} ++ ++off_t qio_channel_io_seek(QIOChannel *ioc, ++ off_t offset, ++ int whence, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_seek) { ++ error_setg(errp, "Channel does not support random access"); ++ return -1; ++ } ++ ++ return klass->io_seek(ioc, offset, whence, errp); ++} ++ ++int qio_channel_flush(QIOChannel *ioc, ++ Error **errp) ++{ ++ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc); ++ ++ if (!klass->io_flush || ++ !qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY)) { ++ return 0; ++ } ++ ++ return klass->io_flush(ioc, errp); ++} ++ ++ ++static void qio_channel_restart_read(void *opaque) ++{ ++ QIOChannel *ioc = opaque; ++ Coroutine *co = qatomic_xchg(&ioc->read_coroutine, NULL); ++ ++ if (!co) { ++ return; ++ } ++ ++ /* Assert that aio_co_wake() reenters the coroutine directly */ ++ assert(qemu_get_current_aio_context() == ++ qemu_coroutine_get_aio_context(co)); ++ aio_co_wake(co); ++} ++ ++static void qio_channel_restart_write(void *opaque) ++{ ++ QIOChannel *ioc = opaque; ++ Coroutine *co = qatomic_xchg(&ioc->write_coroutine, NULL); ++ ++ if (!co) { ++ return; ++ } ++ ++ /* Assert that aio_co_wake() reenters the coroutine directly */ ++ assert(qemu_get_current_aio_context() == ++ qemu_coroutine_get_aio_context(co)); ++ aio_co_wake(co); ++} ++ ++static void coroutine_fn ++qio_channel_set_fd_handlers(QIOChannel *ioc, GIOCondition condition) ++{ ++ AioContext *ctx = ioc->follow_coroutine_ctx ? ++ qemu_coroutine_get_aio_context(qemu_coroutine_self()) : ++ iohandler_get_aio_context(); ++ AioContext *read_ctx = NULL; ++ IOHandler *io_read = NULL; ++ AioContext *write_ctx = NULL; ++ IOHandler *io_write = NULL; ++ ++ if (condition == G_IO_IN) { ++ ioc->read_coroutine = qemu_coroutine_self(); ++ ioc->read_ctx = ctx; ++ read_ctx = ctx; ++ io_read = qio_channel_restart_read; ++ ++ /* ++ * Thread safety: if the other coroutine is set and its AioContext ++ * matches ours, then there is mutual exclusion between read and write ++ * because they share a single thread and it's safe to set both read ++ * and write fd handlers here. If the AioContext does not match ours, ++ * then both threads may run in parallel but there is no shared state ++ * to worry about. ++ */ ++ if (ioc->write_coroutine && ioc->write_ctx == ctx) { ++ write_ctx = ctx; ++ io_write = qio_channel_restart_write; ++ } ++ } else if (condition == G_IO_OUT) { ++ ioc->write_coroutine = qemu_coroutine_self(); ++ ioc->write_ctx = ctx; ++ write_ctx = ctx; ++ io_write = qio_channel_restart_write; ++ if (ioc->read_coroutine && ioc->read_ctx == ctx) { ++ read_ctx = ctx; ++ io_read = qio_channel_restart_read; ++ } ++ } else { ++ abort(); ++ } ++ ++ qio_channel_set_aio_fd_handler(ioc, read_ctx, io_read, ++ write_ctx, io_write, ioc); ++} ++ ++static void coroutine_fn ++qio_channel_clear_fd_handlers(QIOChannel *ioc, GIOCondition condition) ++{ ++ AioContext *read_ctx = NULL; ++ IOHandler *io_read = NULL; ++ AioContext *write_ctx = NULL; ++ IOHandler *io_write = NULL; ++ AioContext *ctx; ++ ++ if (condition == G_IO_IN) { ++ ctx = ioc->read_ctx; ++ read_ctx = ctx; ++ io_read = NULL; ++ if (ioc->write_coroutine && ioc->write_ctx == ctx) { ++ write_ctx = ctx; ++ io_write = qio_channel_restart_write; ++ } ++ } else if (condition == G_IO_OUT) { ++ ctx = ioc->write_ctx; ++ write_ctx = ctx; ++ io_write = NULL; ++ if (ioc->read_coroutine && ioc->read_ctx == ctx) { ++ read_ctx = ctx; ++ io_read = qio_channel_restart_read; ++ } ++ } else { ++ abort(); ++ } ++ ++ qio_channel_set_aio_fd_handler(ioc, read_ctx, io_read, ++ write_ctx, io_write, ioc); ++} ++ ++void coroutine_fn qio_channel_yield(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ AioContext *ioc_ctx; ++ ++ assert(qemu_in_coroutine()); ++ ioc_ctx = qemu_coroutine_get_aio_context(qemu_coroutine_self()); ++ ++ if (condition == G_IO_IN) { ++ assert(!ioc->read_coroutine); ++ } else if (condition == G_IO_OUT) { ++ assert(!ioc->write_coroutine); ++ } else { ++ abort(); ++ } ++ qio_channel_set_fd_handlers(ioc, condition); ++ qemu_coroutine_yield(); ++ assert(in_aio_context_home_thread(ioc_ctx)); ++ ++ /* Allow interrupting the operation by reentering the coroutine other than ++ * through the aio_fd_handlers. */ ++ if (condition == G_IO_IN) { ++ assert(ioc->read_coroutine == NULL); ++ } else if (condition == G_IO_OUT) { ++ assert(ioc->write_coroutine == NULL); ++ } ++ qio_channel_clear_fd_handlers(ioc, condition); ++} ++ ++void qio_channel_wake_read(QIOChannel *ioc) ++{ ++ Coroutine *co = qatomic_xchg(&ioc->read_coroutine, NULL); ++ if (co) { ++ aio_co_wake(co); ++ } ++} ++ ++static gboolean qio_channel_wait_complete(QIOChannel *ioc, ++ GIOCondition condition, ++ gpointer opaque) ++{ ++ GMainLoop *loop = opaque; ++ ++ g_main_loop_quit(loop); ++ return FALSE; ++} ++ ++ ++void qio_channel_wait(QIOChannel *ioc, ++ GIOCondition condition) ++{ ++ GMainContext *ctxt = g_main_context_new(); ++ GMainLoop *loop = g_main_loop_new(ctxt, TRUE); ++ GSource *source; ++ ++ source = qio_channel_create_watch(ioc, condition); ++ ++ g_source_set_callback(source, ++ (GSourceFunc)qio_channel_wait_complete, ++ loop, ++ NULL); ++ ++ g_source_attach(source, ctxt); ++ ++ g_main_loop_run(loop); ++ ++ g_source_unref(source); ++ g_main_loop_unref(loop); ++ g_main_context_unref(ctxt); ++} ++ ++ ++static void qio_channel_finalize(Object *obj) ++{ ++ QIOChannel *ioc = QIO_CHANNEL(obj); ++ ++ /* Must not have coroutines in qio_channel_yield() */ ++ assert(!ioc->read_coroutine); ++ assert(!ioc->write_coroutine); ++ ++ g_free(ioc->name); ++ ++#ifdef _WIN32 ++ if (ioc->event) { ++ CloseHandle(ioc->event); ++ } ++#endif ++} ++ ++static const TypeInfo qio_channel_info = { ++ .parent = TYPE_OBJECT, ++ .name = TYPE_QIO_CHANNEL, ++ .instance_size = sizeof(QIOChannel), ++ .instance_finalize = qio_channel_finalize, ++ .abstract = true, ++ .class_size = sizeof(QIOChannelClass), ++}; ++ ++ ++static void qio_channel_register_types(void) ++{ ++ type_register_static(&qio_channel_info); ++} ++ ++ ++type_init(qio_channel_register_types); +diff --git a/qcow2/lib/io/task.c b/qcow2/lib/io/task.c +new file mode 100644 +index 00000000..451f26f8 +--- /dev/null ++++ b/qcow2/lib/io/task.c +@@ -0,0 +1,241 @@ ++/* ++ * QEMU I/O task ++ * ++ * Copyright (c) 2015 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "io/task.h" ++#include "qapi/error.h" ++#include "qemu/thread.h" ++#include "qom/object.h" ++#include "trace.h" ++ ++struct QIOTaskThreadData { ++ QIOTaskWorker worker; ++ gpointer opaque; ++ GDestroyNotify destroy; ++ GMainContext *context; ++ GSource *completion; ++}; ++ ++ ++struct QIOTask { ++ Object *source; ++ QIOTaskFunc func; ++ gpointer opaque; ++ GDestroyNotify destroy; ++ Error *err; ++ gpointer result; ++ GDestroyNotify destroyResult; ++ QemuMutex thread_lock; ++ QemuCond thread_cond; ++ struct QIOTaskThreadData *thread; ++}; ++ ++ ++QIOTask *qio_task_new(Object *source, ++ QIOTaskFunc func, ++ gpointer opaque, ++ GDestroyNotify destroy) ++{ ++ QIOTask *task; ++ ++ task = g_new0(QIOTask, 1); ++ ++ task->source = source; ++ object_ref(source); ++ task->func = func; ++ task->opaque = opaque; ++ task->destroy = destroy; ++ qemu_mutex_init(&task->thread_lock); ++ qemu_cond_init(&task->thread_cond); ++ ++ trace_qio_task_new(task, source, func, opaque); ++ ++ return task; ++} ++ ++static void qio_task_free(QIOTask *task) ++{ ++ qemu_mutex_lock(&task->thread_lock); ++ if (task->thread) { ++ if (task->thread->destroy) { ++ task->thread->destroy(task->thread->opaque); ++ } ++ ++ if (task->thread->context) { ++ g_main_context_unref(task->thread->context); ++ } ++ ++ g_free(task->thread); ++ } ++ ++ if (task->destroy) { ++ task->destroy(task->opaque); ++ } ++ if (task->destroyResult) { ++ task->destroyResult(task->result); ++ } ++ if (task->err) { ++ error_free(task->err); ++ } ++ object_unref(task->source); ++ ++ qemu_mutex_unlock(&task->thread_lock); ++ qemu_mutex_destroy(&task->thread_lock); ++ qemu_cond_destroy(&task->thread_cond); ++ ++ g_free(task); ++} ++ ++ ++static gboolean qio_task_thread_result(gpointer opaque) ++{ ++ QIOTask *task = opaque; ++ ++ trace_qio_task_thread_result(task); ++ qio_task_complete(task); ++ ++ return FALSE; ++} ++ ++ ++static gpointer qio_task_thread_worker(gpointer opaque) ++{ ++ QIOTask *task = opaque; ++ ++ trace_qio_task_thread_run(task); ++ ++ task->thread->worker(task, task->thread->opaque); ++ ++ /* We're running in the background thread, and must only ++ * ever report the task results in the main event loop ++ * thread. So we schedule an idle callback to report ++ * the worker results ++ */ ++ trace_qio_task_thread_exit(task); ++ ++ qemu_mutex_lock(&task->thread_lock); ++ ++ task->thread->completion = g_idle_source_new(); ++ g_source_set_callback(task->thread->completion, ++ qio_task_thread_result, task, NULL); ++ g_source_attach(task->thread->completion, ++ task->thread->context); ++ g_source_unref(task->thread->completion); ++ trace_qio_task_thread_source_attach(task, task->thread->completion); ++ ++ qemu_cond_signal(&task->thread_cond); ++ qemu_mutex_unlock(&task->thread_lock); ++ ++ return NULL; ++} ++ ++ ++void qio_task_run_in_thread(QIOTask *task, ++ QIOTaskWorker worker, ++ gpointer opaque, ++ GDestroyNotify destroy, ++ GMainContext *context) ++{ ++ struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1); ++ QemuThread thread; ++ ++ if (context) { ++ g_main_context_ref(context); ++ } ++ ++ data->worker = worker; ++ data->opaque = opaque; ++ data->destroy = destroy; ++ data->context = context; ++ ++ task->thread = data; ++ ++ trace_qio_task_thread_start(task, worker, opaque); ++ qemu_thread_create(&thread, ++ "io-task-worker", ++ qio_task_thread_worker, ++ task, ++ QEMU_THREAD_DETACHED); ++} ++ ++ ++void qio_task_wait_thread(QIOTask *task) ++{ ++ qemu_mutex_lock(&task->thread_lock); ++ g_assert(task->thread != NULL); ++ while (task->thread->completion == NULL) { ++ qemu_cond_wait(&task->thread_cond, &task->thread_lock); ++ } ++ ++ trace_qio_task_thread_source_cancel(task, task->thread->completion); ++ g_source_destroy(task->thread->completion); ++ qemu_mutex_unlock(&task->thread_lock); ++ ++ qio_task_thread_result(task); ++} ++ ++ ++void qio_task_complete(QIOTask *task) ++{ ++ task->func(task, task->opaque); ++ trace_qio_task_complete(task); ++ qio_task_free(task); ++} ++ ++ ++void qio_task_set_error(QIOTask *task, ++ Error *err) ++{ ++ error_propagate(&task->err, err); ++} ++ ++ ++bool qio_task_propagate_error(QIOTask *task, ++ Error **errp) ++{ ++ if (task->err) { ++ error_propagate(errp, task->err); ++ task->err = NULL; ++ return true; ++ } ++ ++ return false; ++} ++ ++ ++void qio_task_set_result_pointer(QIOTask *task, ++ gpointer result, ++ GDestroyNotify destroy) ++{ ++ task->result = result; ++ task->destroyResult = destroy; ++} ++ ++ ++gpointer qio_task_get_result_pointer(QIOTask *task) ++{ ++ return task->result; ++} ++ ++ ++Object *qio_task_get_source(QIOTask *task) ++{ ++ return task->source; ++} +diff --git a/qcow2/lib/job-qmp.c b/qcow2/lib/job-qmp.c +new file mode 100644 +index 00000000..9e26fa89 +--- /dev/null ++++ b/qcow2/lib/job-qmp.c +@@ -0,0 +1,188 @@ ++/* ++ * QMP interface for background jobs ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/job.h" ++#include "qapi/qapi-commands-job.h" ++#include "qapi/error.h" ++#include "trace/trace-root.h" ++ ++/* ++ * Get a job using its ID. Called with job_mutex held. ++ */ ++static Job *find_job_locked(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ job = job_get_locked(id); ++ if (!job) { ++ error_setg(errp, "Job not found"); ++ return NULL; ++ } ++ ++ return job; ++} ++ ++void qmp_job_cancel(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_cancel(job); ++ job_user_cancel_locked(job, true, errp); ++} ++ ++void qmp_job_pause(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_pause(job); ++ job_user_pause_locked(job, errp); ++} ++ ++void qmp_job_resume(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_resume(job); ++ job_user_resume_locked(job, errp); ++} ++ ++void qmp_job_complete(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_complete(job); ++ job_complete_locked(job, errp); ++} ++ ++void qmp_job_finalize(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_finalize(job); ++ job_ref_locked(job); ++ job_finalize_locked(job, errp); ++ ++ job_unref_locked(job); ++} ++ ++void qmp_job_dismiss(const char *id, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ job = find_job_locked(id, errp); ++ ++ if (!job) { ++ return; ++ } ++ ++ trace_qmp_job_dismiss(job); ++ job_dismiss_locked(&job, errp); ++} ++ ++/* Called with job_mutex held. */ ++static JobInfo *job_query_single_locked(Job *job, Error **errp) ++{ ++ JobInfo *info; ++ uint64_t progress_current; ++ uint64_t progress_total; ++ ++ assert(!job_is_internal(job)); ++ progress_get_snapshot(&job->progress, &progress_current, ++ &progress_total); ++ ++ info = g_new(JobInfo, 1); ++ *info = (JobInfo) { ++ .id = g_strdup(job->id), ++ .type = job_type(job), ++ .status = job->status, ++ .current_progress = progress_current, ++ .total_progress = progress_total, ++ .error = job->err ? ++ g_strdup(error_get_pretty(job->err)) : NULL, ++ }; ++ ++ return info; ++} ++ ++JobInfoList *qmp_query_jobs(Error **errp) ++{ ++ JobInfoList *head = NULL, **tail = &head; ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ ++ for (job = job_next_locked(NULL); job; job = job_next_locked(job)) { ++ JobInfo *value; ++ ++ if (job_is_internal(job)) { ++ continue; ++ } ++ value = job_query_single_locked(job, errp); ++ if (!value) { ++ qapi_free_JobInfoList(head); ++ return NULL; ++ } ++ QAPI_LIST_APPEND(tail, value); ++ } ++ ++ return head; ++} +diff --git a/qcow2/lib/job.c b/qcow2/lib/job.c +new file mode 100644 +index 00000000..660ce22c +--- /dev/null ++++ b/qcow2/lib/job.c +@@ -0,0 +1,1264 @@ ++/* ++ * Background jobs (long-running operations) ++ * ++ * Copyright (c) 2011 IBM Corp. ++ * Copyright (c) 2012, 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/job.h" ++#include "qemu/id.h" ++#include "qemu/main-loop.h" ++#include "block/aio-wait.h" ++#include "trace/trace-root.h" ++#include "qapi/qapi-events-job.h" ++ ++/* ++ * The job API is composed of two categories of functions. ++ * ++ * The first includes functions used by the monitor. The monitor is ++ * peculiar in that it accesses the job list with job_get, and ++ * therefore needs consistency across job_get and the actual operation ++ * (e.g. job_user_cancel). To achieve this consistency, the caller ++ * calls job_lock/job_unlock itself around the whole operation. ++ * ++ * ++ * The second includes functions used by the job drivers and sometimes ++ * by the core block layer. These delegate the locking to the callee instead. ++ */ ++ ++/* ++ * job_mutex protects the jobs list, but also makes the ++ * struct job fields thread-safe. ++ */ ++QemuMutex job_mutex; ++ ++/* Protected by job_mutex */ ++static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); ++ ++/* Job State Transition Table */ ++bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = { ++ /* U, C, R, P, Y, S, W, D, X, E, N */ ++ /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++ /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, ++ /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, ++ /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, ++ /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, ++ /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, ++ /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, ++ /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, ++ /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, ++ /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, ++ /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, ++}; ++ ++bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { ++ /* U, C, R, P, Y, S, W, D, X, E, N */ ++ [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, ++ [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, ++ [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, ++ [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, ++ [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, ++ [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, ++ [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, ++ [JOB_VERB_CHANGE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, ++}; ++ ++/* Transactional group of jobs */ ++struct JobTxn { ++ ++ /* Is this txn being cancelled? */ ++ bool aborting; ++ ++ /* List of jobs */ ++ QLIST_HEAD(, Job) jobs; ++ ++ /* Reference count */ ++ int refcnt; ++}; ++ ++void job_lock(void) ++{ ++ qemu_mutex_lock(&job_mutex); ++} ++ ++void job_unlock(void) ++{ ++ qemu_mutex_unlock(&job_mutex); ++} ++ ++static void __attribute__((__constructor__)) job_init(void) ++{ ++ qemu_mutex_init(&job_mutex); ++} ++ ++JobTxn *job_txn_new(void) ++{ ++ JobTxn *txn = g_new0(JobTxn, 1); ++ QLIST_INIT(&txn->jobs); ++ txn->refcnt = 1; ++ return txn; ++} ++ ++/* Called with job_mutex held. */ ++static void job_txn_ref_locked(JobTxn *txn) ++{ ++ txn->refcnt++; ++} ++ ++void job_txn_unref_locked(JobTxn *txn) ++{ ++ if (txn && --txn->refcnt == 0) { ++ g_free(txn); ++ } ++} ++ ++void job_txn_unref(JobTxn *txn) ++{ ++ JOB_LOCK_GUARD(); ++ job_txn_unref_locked(txn); ++} ++ ++/** ++ * @txn: The transaction (may be NULL) ++ * @job: Job to add to the transaction ++ * ++ * Add @job to the transaction. The @job must not already be in a transaction. ++ * The caller must call either job_txn_unref() or job_completed() to release ++ * the reference that is automatically grabbed here. ++ * ++ * If @txn is NULL, the function does nothing. ++ * ++ * Called with job_mutex held. ++ */ ++static void job_txn_add_job_locked(JobTxn *txn, Job *job) ++{ ++ if (!txn) { ++ return; ++ } ++ ++ assert(!job->txn); ++ job->txn = txn; ++ ++ QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); ++ job_txn_ref_locked(txn); ++} ++ ++/* Called with job_mutex held. */ ++static void job_txn_del_job_locked(Job *job) ++{ ++ if (job->txn) { ++ QLIST_REMOVE(job, txn_list); ++ job_txn_unref_locked(job->txn); ++ job->txn = NULL; ++ } ++} ++ ++/* Called with job_mutex held, but releases it temporarily. */ ++static int job_txn_apply_locked(Job *job, int fn(Job *)) ++{ ++ Job *other_job, *next; ++ JobTxn *txn = job->txn; ++ int rc = 0; ++ ++ /* ++ * Similar to job_completed_txn_abort, we take each job's lock before ++ * applying fn, but since we assume that outer_ctx is held by the caller, ++ * we need to release it here to avoid holding the lock twice - which would ++ * break AIO_WAIT_WHILE from within fn. ++ */ ++ job_ref_locked(job); ++ ++ QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) { ++ rc = fn(other_job); ++ if (rc) { ++ break; ++ } ++ } ++ ++ job_unref_locked(job); ++ return rc; ++} ++ ++bool job_is_internal(Job *job) ++{ ++ return (job->id == NULL); ++} ++ ++/* Called with job_mutex held. */ ++static void job_state_transition_locked(Job *job, JobStatus s1) ++{ ++ JobStatus s0 = job->status; ++ assert(s1 >= 0 && s1 < JOB_STATUS__MAX); ++ trace_job_state_transition(job, job->ret, ++ JobSTT[s0][s1] ? "allowed" : "disallowed", ++ JobStatus_str(s0), JobStatus_str(s1)); ++ assert(JobSTT[s0][s1]); ++ job->status = s1; ++ ++ if (!job_is_internal(job) && s1 != s0) { ++ qapi_event_send_job_status_change(job->id, job->status); ++ } ++} ++ ++int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) ++{ ++ JobStatus s0 = job->status; ++ assert(verb >= 0 && verb < JOB_VERB__MAX); ++ trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), ++ JobVerbTable[verb][s0] ? "allowed" : "prohibited"); ++ if (JobVerbTable[verb][s0]) { ++ return 0; ++ } ++ error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", ++ job->id, JobStatus_str(s0), JobVerb_str(verb)); ++ return -EPERM; ++} ++ ++JobType job_type(const Job *job) ++{ ++ return job->driver->job_type; ++} ++ ++const char *job_type_str(const Job *job) ++{ ++ return JobType_str(job_type(job)); ++} ++ ++bool job_is_cancelled_locked(Job *job) ++{ ++ /* force_cancel may be true only if cancelled is true, too */ ++ assert(job->cancelled || !job->force_cancel); ++ return job->force_cancel; ++} ++ ++bool job_is_cancelled(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_is_cancelled_locked(job); ++} ++ ++/* Called with job_mutex held. */ ++static bool job_cancel_requested_locked(Job *job) ++{ ++ return job->cancelled; ++} ++ ++bool job_cancel_requested(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_cancel_requested_locked(job); ++} ++ ++bool job_is_ready_locked(Job *job) ++{ ++ switch (job->status) { ++ case JOB_STATUS_UNDEFINED: ++ case JOB_STATUS_CREATED: ++ case JOB_STATUS_RUNNING: ++ case JOB_STATUS_PAUSED: ++ case JOB_STATUS_WAITING: ++ case JOB_STATUS_PENDING: ++ case JOB_STATUS_ABORTING: ++ case JOB_STATUS_CONCLUDED: ++ case JOB_STATUS_NULL: ++ return false; ++ case JOB_STATUS_READY: ++ case JOB_STATUS_STANDBY: ++ return true; ++ default: ++ g_assert_not_reached(); ++ } ++ return false; ++} ++ ++bool job_is_ready(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_is_ready_locked(job); ++} ++ ++bool job_is_completed_locked(Job *job) ++{ ++ switch (job->status) { ++ case JOB_STATUS_UNDEFINED: ++ case JOB_STATUS_CREATED: ++ case JOB_STATUS_RUNNING: ++ case JOB_STATUS_PAUSED: ++ case JOB_STATUS_READY: ++ case JOB_STATUS_STANDBY: ++ return false; ++ case JOB_STATUS_WAITING: ++ case JOB_STATUS_PENDING: ++ case JOB_STATUS_ABORTING: ++ case JOB_STATUS_CONCLUDED: ++ case JOB_STATUS_NULL: ++ return true; ++ default: ++ g_assert_not_reached(); ++ } ++ return false; ++} ++ ++static bool job_is_completed(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_is_completed_locked(job); ++} ++ ++static bool job_started_locked(Job *job) ++{ ++ return job->co; ++} ++ ++/* Called with job_mutex held. */ ++static bool job_should_pause_locked(Job *job) ++{ ++ return job->pause_count > 0; ++} ++ ++Job *job_next_locked(Job *job) ++{ ++ if (!job) { ++ return QLIST_FIRST(&jobs); ++ } ++ return QLIST_NEXT(job, job_list); ++} ++ ++Job *job_next(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ return job_next_locked(job); ++} ++ ++Job *job_get_locked(const char *id) ++{ ++ Job *job; ++ ++ QLIST_FOREACH(job, &jobs, job_list) { ++ if (job->id && !strcmp(id, job->id)) { ++ return job; ++ } ++ } ++ ++ return NULL; ++} ++ ++void job_set_aio_context(Job *job, AioContext *ctx) ++{ ++ /* protect against read in job_finish_sync_locked and job_start */ ++ GLOBAL_STATE_CODE(); ++ /* protect against read in job_do_yield_locked */ ++ JOB_LOCK_GUARD(); ++ /* ensure the job is quiescent while the AioContext is changed */ ++ assert(job->paused || job_is_completed_locked(job)); ++ job->aio_context = ctx; ++} ++ ++/* Called with job_mutex *not* held. */ ++static void job_sleep_timer_cb(void *opaque) ++{ ++ Job *job = opaque; ++ ++ job_enter(job); ++} ++ ++void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, ++ AioContext *ctx, int flags, BlockCompletionFunc *cb, ++ void *opaque, Error **errp) ++{ ++ Job *job; ++ ++ JOB_LOCK_GUARD(); ++ ++ if (job_id) { ++ if (flags & JOB_INTERNAL) { ++ error_setg(errp, "Cannot specify job ID for internal job"); ++ return NULL; ++ } ++ if (!id_wellformed(job_id)) { ++ error_setg(errp, "Invalid job ID '%s'", job_id); ++ return NULL; ++ } ++ if (job_get_locked(job_id)) { ++ error_setg(errp, "Job ID '%s' already in use", job_id); ++ return NULL; ++ } ++ } else if (!(flags & JOB_INTERNAL)) { ++ error_setg(errp, "An explicit job ID is required"); ++ return NULL; ++ } ++ ++ job = g_malloc0(driver->instance_size); ++ job->driver = driver; ++ job->id = g_strdup(job_id); ++ job->refcnt = 1; ++ job->aio_context = ctx; ++ job->busy = false; ++ job->paused = true; ++ job->pause_count = 1; ++ job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); ++ job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); ++ job->cb = cb; ++ job->opaque = opaque; ++ ++ progress_init(&job->progress); ++ ++ notifier_list_init(&job->on_finalize_cancelled); ++ notifier_list_init(&job->on_finalize_completed); ++ notifier_list_init(&job->on_pending); ++ notifier_list_init(&job->on_ready); ++ notifier_list_init(&job->on_idle); ++ ++ job_state_transition_locked(job, JOB_STATUS_CREATED); ++ aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, ++ QEMU_CLOCK_REALTIME, SCALE_NS, ++ job_sleep_timer_cb, job); ++ ++ QLIST_INSERT_HEAD(&jobs, job, job_list); ++ ++ /* Single jobs are modeled as single-job transactions for sake of ++ * consolidating the job management logic */ ++ if (!txn) { ++ txn = job_txn_new(); ++ job_txn_add_job_locked(txn, job); ++ job_txn_unref_locked(txn); ++ } else { ++ job_txn_add_job_locked(txn, job); ++ } ++ ++ return job; ++} ++ ++void job_ref_locked(Job *job) ++{ ++ ++job->refcnt; ++} ++ ++void job_unref_locked(Job *job) ++{ ++ GLOBAL_STATE_CODE(); ++ ++ if (--job->refcnt == 0) { ++ assert(job->status == JOB_STATUS_NULL); ++ assert(!timer_pending(&job->sleep_timer)); ++ assert(!job->txn); ++ ++ if (job->driver->free) { ++ job_unlock(); ++ job->driver->free(job); ++ job_lock(); ++ } ++ ++ QLIST_REMOVE(job, job_list); ++ ++ progress_destroy(&job->progress); ++ error_free(job->err); ++ g_free(job->id); ++ g_free(job); ++ } ++} ++ ++void job_progress_update(Job *job, uint64_t done) ++{ ++ progress_work_done(&job->progress, done); ++} ++ ++void job_progress_set_remaining(Job *job, uint64_t remaining) ++{ ++ progress_set_remaining(&job->progress, remaining); ++} ++ ++void job_progress_increase_remaining(Job *job, uint64_t delta) ++{ ++ progress_increase_remaining(&job->progress, delta); ++} ++ ++/** ++ * To be called when a cancelled job is finalised. ++ * Called with job_mutex held. ++ */ ++static void job_event_cancelled_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_finalize_cancelled, job); ++} ++ ++/** ++ * To be called when a successfully completed job is finalised. ++ * Called with job_mutex held. ++ */ ++static void job_event_completed_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_finalize_completed, job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_event_pending_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_pending, job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_event_ready_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_ready, job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_event_idle_locked(Job *job) ++{ ++ notifier_list_notify(&job->on_idle, job); ++} ++ ++void job_enter_cond_locked(Job *job, bool(*fn)(Job *job)) ++{ ++ if (!job_started_locked(job)) { ++ return; ++ } ++ if (job->deferred_to_main_loop) { ++ return; ++ } ++ ++ if (job->busy) { ++ return; ++ } ++ ++ if (fn && !fn(job)) { ++ return; ++ } ++ ++ assert(!job->deferred_to_main_loop); ++ timer_del(&job->sleep_timer); ++ job->busy = true; ++ job_unlock(); ++ aio_co_wake(job->co); ++ job_lock(); ++} ++ ++void job_enter(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_enter_cond_locked(job, NULL); ++} ++ ++/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. ++ * Reentering the job coroutine with job_enter() before the timer has expired ++ * is allowed and cancels the timer. ++ * ++ * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be ++ * called explicitly. ++ * ++ * Called with job_mutex held, but releases it temporarily. ++ */ ++static void coroutine_fn job_do_yield_locked(Job *job, uint64_t ns) ++{ ++ AioContext *next_aio_context; ++ ++ if (ns != -1) { ++ timer_mod(&job->sleep_timer, ns); ++ } ++ job->busy = false; ++ job_event_idle_locked(job); ++ job_unlock(); ++ qemu_coroutine_yield(); ++ job_lock(); ++ ++ next_aio_context = job->aio_context; ++ /* ++ * Coroutine has resumed, but in the meanwhile the job AioContext ++ * 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) { ++ job_unlock(); ++ aio_co_reschedule_self(next_aio_context); ++ job_lock(); ++ next_aio_context = job->aio_context; ++ } ++ ++ /* Set by job_enter_cond_locked() before re-entering the coroutine. */ ++ assert(job->busy); ++} ++ ++/* Called with job_mutex held, but releases it temporarily. */ ++static void coroutine_fn job_pause_point_locked(Job *job) ++{ ++ assert(job && job_started_locked(job)); ++ ++ if (!job_should_pause_locked(job)) { ++ return; ++ } ++ if (job_is_cancelled_locked(job)) { ++ return; ++ } ++ ++ if (job->driver->pause) { ++ job_unlock(); ++ job->driver->pause(job); ++ job_lock(); ++ } ++ ++ if (job_should_pause_locked(job) && !job_is_cancelled_locked(job)) { ++ JobStatus status = job->status; ++ job_state_transition_locked(job, status == JOB_STATUS_READY ++ ? JOB_STATUS_STANDBY ++ : JOB_STATUS_PAUSED); ++ job->paused = true; ++ job_do_yield_locked(job, -1); ++ job->paused = false; ++ job_state_transition_locked(job, status); ++ } ++ ++ if (job->driver->resume) { ++ job_unlock(); ++ job->driver->resume(job); ++ job_lock(); ++ } ++} ++ ++void coroutine_fn job_pause_point(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_pause_point_locked(job); ++} ++ ++void coroutine_fn job_yield(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ assert(job->busy); ++ ++ /* Check cancellation *before* setting busy = false, too! */ ++ if (job_is_cancelled_locked(job)) { ++ return; ++ } ++ ++ if (!job_should_pause_locked(job)) { ++ job_do_yield_locked(job, -1); ++ } ++ ++ job_pause_point_locked(job); ++} ++ ++void coroutine_fn job_sleep_ns(Job *job, int64_t ns) ++{ ++ JOB_LOCK_GUARD(); ++ assert(job->busy); ++ ++ /* Check cancellation *before* setting busy = false, too! */ ++ if (job_is_cancelled_locked(job)) { ++ return; ++ } ++ ++ if (!job_should_pause_locked(job)) { ++ job_do_yield_locked(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); ++ } ++ ++ job_pause_point_locked(job); ++} ++ ++/* Assumes the job_mutex is held */ ++static bool job_timer_not_pending_locked(Job *job) ++{ ++ return !timer_pending(&job->sleep_timer); ++} ++ ++void job_pause_locked(Job *job) ++{ ++ job->pause_count++; ++ if (!job->paused) { ++ job_enter_cond_locked(job, NULL); ++ } ++} ++ ++void job_pause(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_pause_locked(job); ++} ++ ++void job_resume_locked(Job *job) ++{ ++ assert(job->pause_count > 0); ++ job->pause_count--; ++ if (job->pause_count) { ++ return; ++ } ++ ++ /* kick only if no timer is pending */ ++ job_enter_cond_locked(job, job_timer_not_pending_locked); ++} ++ ++void job_resume(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_resume_locked(job); ++} ++ ++void job_user_pause_locked(Job *job, Error **errp) ++{ ++ if (job_apply_verb_locked(job, JOB_VERB_PAUSE, errp)) { ++ return; ++ } ++ if (job->user_paused) { ++ error_setg(errp, "Job is already paused"); ++ return; ++ } ++ job->user_paused = true; ++ job_pause_locked(job); ++} ++ ++bool job_user_paused_locked(Job *job) ++{ ++ return job->user_paused; ++} ++ ++void job_user_resume_locked(Job *job, Error **errp) ++{ ++ assert(job); ++ GLOBAL_STATE_CODE(); ++ if (!job->user_paused || job->pause_count <= 0) { ++ error_setg(errp, "Can't resume a job that was not paused"); ++ return; ++ } ++ if (job_apply_verb_locked(job, JOB_VERB_RESUME, errp)) { ++ return; ++ } ++ if (job->driver->user_resume) { ++ job_unlock(); ++ job->driver->user_resume(job); ++ job_lock(); ++ } ++ job->user_paused = false; ++ job_resume_locked(job); ++} ++ ++/* Called with job_mutex held, but releases it temporarily. */ ++static void job_do_dismiss_locked(Job *job) ++{ ++ assert(job); ++ job->busy = false; ++ job->paused = false; ++ job->deferred_to_main_loop = true; ++ ++ job_txn_del_job_locked(job); ++ ++ job_state_transition_locked(job, JOB_STATUS_NULL); ++ job_unref_locked(job); ++} ++ ++void job_dismiss_locked(Job **jobptr, Error **errp) ++{ ++ Job *job = *jobptr; ++ /* similarly to _complete, this is QMP-interface only. */ ++ assert(job->id); ++ if (job_apply_verb_locked(job, JOB_VERB_DISMISS, errp)) { ++ return; ++ } ++ ++ job_do_dismiss_locked(job); ++ *jobptr = NULL; ++} ++ ++void job_early_fail(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ assert(job->status == JOB_STATUS_CREATED); ++ job_do_dismiss_locked(job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_conclude_locked(Job *job) ++{ ++ job_state_transition_locked(job, JOB_STATUS_CONCLUDED); ++ if (job->auto_dismiss || !job_started_locked(job)) { ++ job_do_dismiss_locked(job); ++ } ++} ++ ++/* Called with job_mutex held. */ ++static void job_update_rc_locked(Job *job) ++{ ++ if (!job->ret && job_is_cancelled_locked(job)) { ++ job->ret = -ECANCELED; ++ } ++ if (job->ret) { ++ if (!job->err) { ++ error_setg(&job->err, "%s", strerror(-job->ret)); ++ } ++ job_state_transition_locked(job, JOB_STATUS_ABORTING); ++ } ++} ++ ++static void job_commit(Job *job) ++{ ++ assert(!job->ret); ++ GLOBAL_STATE_CODE(); ++ if (job->driver->commit) { ++ job->driver->commit(job); ++ } ++} ++ ++static void job_abort(Job *job) ++{ ++ assert(job->ret); ++ GLOBAL_STATE_CODE(); ++ if (job->driver->abort) { ++ job->driver->abort(job); ++ } ++} ++ ++static void job_clean(Job *job) ++{ ++ GLOBAL_STATE_CODE(); ++ if (job->driver->clean) { ++ job->driver->clean(job); ++ } ++} ++ ++/* ++ * Called with job_mutex held, but releases it temporarily. ++ */ ++static int job_finalize_single_locked(Job *job) ++{ ++ int job_ret; ++ ++ assert(job_is_completed_locked(job)); ++ ++ /* Ensure abort is called for late-transactional failures */ ++ job_update_rc_locked(job); ++ ++ job_ret = job->ret; ++ job_unlock(); ++ ++ if (!job_ret) { ++ job_commit(job); ++ } else { ++ job_abort(job); ++ } ++ job_clean(job); ++ ++ if (job->cb) { ++ job->cb(job->opaque, job_ret); ++ } ++ ++ job_lock(); ++ ++ /* Emit events only if we actually started */ ++ if (job_started_locked(job)) { ++ if (job_is_cancelled_locked(job)) { ++ job_event_cancelled_locked(job); ++ } else { ++ job_event_completed_locked(job); ++ } ++ } ++ ++ job_txn_del_job_locked(job); ++ job_conclude_locked(job); ++ return 0; ++} ++ ++/* ++ * Called with job_mutex held, but releases it temporarily. ++ */ ++static void job_cancel_async_locked(Job *job, bool force) ++{ ++ GLOBAL_STATE_CODE(); ++ if (job->driver->cancel) { ++ job_unlock(); ++ force = job->driver->cancel(job, force); ++ job_lock(); ++ } else { ++ /* No .cancel() means the job will behave as if force-cancelled */ ++ force = true; ++ } ++ ++ if (job->user_paused) { ++ /* Do not call job_enter here, the caller will handle it. */ ++ if (job->driver->user_resume) { ++ job_unlock(); ++ job->driver->user_resume(job); ++ job_lock(); ++ } ++ job->user_paused = false; ++ assert(job->pause_count > 0); ++ job->pause_count--; ++ } ++ ++ /* ++ * Ignore soft cancel requests after the job is already done ++ * (We will still invoke job->driver->cancel() above, but if the ++ * job driver supports soft cancelling and the job is done, that ++ * should be a no-op, too. We still call it so it can override ++ * @force.) ++ */ ++ if (force || !job->deferred_to_main_loop) { ++ job->cancelled = true; ++ /* To prevent 'force == false' overriding a previous 'force == true' */ ++ job->force_cancel |= force; ++ } ++} ++ ++/* ++ * Called with job_mutex held, but releases it temporarily. ++ */ ++static void job_completed_txn_abort_locked(Job *job) ++{ ++ JobTxn *txn = job->txn; ++ Job *other_job; ++ ++ if (txn->aborting) { ++ /* ++ * We are cancelled by another job, which will handle everything. ++ */ ++ return; ++ } ++ txn->aborting = true; ++ job_txn_ref_locked(txn); ++ ++ job_ref_locked(job); ++ ++ /* Other jobs are effectively cancelled by us, set the status for ++ * them; this job, however, may or may not be cancelled, depending ++ * on the caller, so leave it. */ ++ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { ++ if (other_job != job) { ++ /* ++ * This is a transaction: If one job failed, no result will matter. ++ * Therefore, pass force=true to terminate all other jobs as quickly ++ * as possible. ++ */ ++ job_cancel_async_locked(other_job, true); ++ } ++ } ++ while (!QLIST_EMPTY(&txn->jobs)) { ++ other_job = QLIST_FIRST(&txn->jobs); ++ if (!job_is_completed_locked(other_job)) { ++ assert(job_cancel_requested_locked(other_job)); ++ job_finish_sync_locked(other_job, NULL, NULL); ++ } ++ job_finalize_single_locked(other_job); ++ } ++ ++ job_unref_locked(job); ++ job_txn_unref_locked(txn); ++} ++ ++/* Called with job_mutex held, but releases it temporarily */ ++static int job_prepare_locked(Job *job) ++{ ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ ++ if (job->ret == 0 && job->driver->prepare) { ++ job_unlock(); ++ ret = job->driver->prepare(job); ++ job_lock(); ++ job->ret = ret; ++ job_update_rc_locked(job); ++ } ++ ++ return job->ret; ++} ++ ++/* Called with job_mutex held */ ++static int job_needs_finalize_locked(Job *job) ++{ ++ return !job->auto_finalize; ++} ++ ++/* Called with job_mutex held */ ++static void job_do_finalize_locked(Job *job) ++{ ++ int rc; ++ assert(job && job->txn); ++ ++ /* prepare the transaction to complete */ ++ rc = job_txn_apply_locked(job, job_prepare_locked); ++ if (rc) { ++ job_completed_txn_abort_locked(job); ++ } else { ++ job_txn_apply_locked(job, job_finalize_single_locked); ++ } ++} ++ ++void job_finalize_locked(Job *job, Error **errp) ++{ ++ assert(job && job->id); ++ if (job_apply_verb_locked(job, JOB_VERB_FINALIZE, errp)) { ++ return; ++ } ++ job_do_finalize_locked(job); ++} ++ ++/* Called with job_mutex held. */ ++static int job_transition_to_pending_locked(Job *job) ++{ ++ job_state_transition_locked(job, JOB_STATUS_PENDING); ++ if (!job->auto_finalize) { ++ job_event_pending_locked(job); ++ } ++ return 0; ++} ++ ++void job_transition_to_ready(Job *job) ++{ ++ JOB_LOCK_GUARD(); ++ job_state_transition_locked(job, JOB_STATUS_READY); ++ job_event_ready_locked(job); ++} ++ ++/* Called with job_mutex held. */ ++static void job_completed_txn_success_locked(Job *job) ++{ ++ JobTxn *txn = job->txn; ++ Job *other_job; ++ ++ job_state_transition_locked(job, JOB_STATUS_WAITING); ++ ++ /* ++ * Successful completion, see if there are other running jobs in this ++ * txn. ++ */ ++ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { ++ if (!job_is_completed_locked(other_job)) { ++ return; ++ } ++ assert(other_job->ret == 0); ++ } ++ ++ job_txn_apply_locked(job, job_transition_to_pending_locked); ++ ++ /* If no jobs need manual finalization, automatically do so */ ++ if (job_txn_apply_locked(job, job_needs_finalize_locked) == 0) { ++ job_do_finalize_locked(job); ++ } ++} ++ ++/* Called with job_mutex held. */ ++static void job_completed_locked(Job *job) ++{ ++ assert(job && job->txn && !job_is_completed_locked(job)); ++ ++ job_update_rc_locked(job); ++ trace_job_completed(job, job->ret); ++ if (job->ret) { ++ job_completed_txn_abort_locked(job); ++ } else { ++ job_completed_txn_success_locked(job); ++ } ++} ++ ++/** ++ * Useful only as a type shim for aio_bh_schedule_oneshot. ++ * Called with job_mutex *not* held. ++ */ ++static void job_exit(void *opaque) ++{ ++ Job *job = (Job *)opaque; ++ JOB_LOCK_GUARD(); ++ job_ref_locked(job); ++ ++ /* This is a lie, we're not quiescent, but still doing the completion ++ * callbacks. However, completion callbacks tend to involve operations that ++ * drain block nodes, and if .drained_poll still returned true, we would ++ * deadlock. */ ++ job->busy = false; ++ job_event_idle_locked(job); ++ ++ job_completed_locked(job); ++ job_unref_locked(job); ++} ++ ++/** ++ * All jobs must allow a pause point before entering their job proper. This ++ * ensures that jobs can be paused prior to being started, then resumed later. ++ */ ++static void coroutine_fn job_co_entry(void *opaque) ++{ ++ Job *job = opaque; ++ int ret; ++ ++ assert(job && job->driver && job->driver->run); ++ WITH_JOB_LOCK_GUARD() { ++ assert(job->aio_context == qemu_get_current_aio_context()); ++ job_pause_point_locked(job); ++ } ++ ret = job->driver->run(job, &job->err); ++ WITH_JOB_LOCK_GUARD() { ++ job->ret = ret; ++ job->deferred_to_main_loop = true; ++ job->busy = true; ++ } ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); ++} ++ ++void job_start(Job *job) ++{ ++ assert(qemu_in_main_thread()); ++ ++ WITH_JOB_LOCK_GUARD() { ++ assert(job && !job_started_locked(job) && job->paused && ++ job->driver && job->driver->run); ++ job->co = qemu_coroutine_create(job_co_entry, job); ++ job->pause_count--; ++ job->busy = true; ++ job->paused = false; ++ job_state_transition_locked(job, JOB_STATUS_RUNNING); ++ } ++ aio_co_enter(job->aio_context, job->co); ++} ++ ++void job_cancel_locked(Job *job, bool force) ++{ ++ if (job->status == JOB_STATUS_CONCLUDED) { ++ job_do_dismiss_locked(job); ++ return; ++ } ++ job_cancel_async_locked(job, force); ++ if (!job_started_locked(job)) { ++ job_completed_locked(job); ++ } else if (job->deferred_to_main_loop) { ++ /* ++ * job_cancel_async() ignores soft-cancel requests for jobs ++ * that are already done (i.e. deferred to the main loop). We ++ * have to check again whether the job is really cancelled. ++ * (job_cancel_requested() and job_is_cancelled() are equivalent ++ * here, because job_cancel_async() will make soft-cancel ++ * requests no-ops when deferred_to_main_loop is true. We ++ * choose to call job_is_cancelled() to show that we invoke ++ * job_completed_txn_abort() only for force-cancelled jobs.) ++ */ ++ if (job_is_cancelled_locked(job)) { ++ job_completed_txn_abort_locked(job); ++ } ++ } else { ++ job_enter_cond_locked(job, NULL); ++ } ++} ++ ++void job_user_cancel_locked(Job *job, bool force, Error **errp) ++{ ++ if (job_apply_verb_locked(job, JOB_VERB_CANCEL, errp)) { ++ return; ++ } ++ job_cancel_locked(job, force); ++} ++ ++/* A wrapper around job_cancel_locked() taking an Error ** parameter so it may ++ * be used with job_finish_sync_locked() without the need for (rather nasty) ++ * function pointer casts there. ++ * ++ * Called with job_mutex held. ++ */ ++static void job_cancel_err_locked(Job *job, Error **errp) ++{ ++ job_cancel_locked(job, false); ++} ++ ++/** ++ * Same as job_cancel_err(), but force-cancel. ++ * Called with job_mutex held. ++ */ ++static void job_force_cancel_err_locked(Job *job, Error **errp) ++{ ++ job_cancel_locked(job, true); ++} ++ ++int job_cancel_sync_locked(Job *job, bool force) ++{ ++ if (force) { ++ return job_finish_sync_locked(job, &job_force_cancel_err_locked, NULL); ++ } else { ++ return job_finish_sync_locked(job, &job_cancel_err_locked, NULL); ++ } ++} ++ ++int job_cancel_sync(Job *job, bool force) ++{ ++ JOB_LOCK_GUARD(); ++ return job_cancel_sync_locked(job, force); ++} ++ ++void job_cancel_sync_all(void) ++{ ++ Job *job; ++ JOB_LOCK_GUARD(); ++ ++ while ((job = job_next_locked(NULL))) { ++ job_cancel_sync_locked(job, true); ++ } ++} ++ ++int job_complete_sync_locked(Job *job, Error **errp) ++{ ++ return job_finish_sync_locked(job, job_complete_locked, errp); ++} ++ ++void job_complete_locked(Job *job, Error **errp) ++{ ++ /* Should not be reachable via external interface for internal jobs */ ++ assert(job->id); ++ GLOBAL_STATE_CODE(); ++ if (job_apply_verb_locked(job, JOB_VERB_COMPLETE, errp)) { ++ return; ++ } ++ if (job_cancel_requested_locked(job) || !job->driver->complete) { ++ error_setg(errp, "The active block job '%s' cannot be completed", ++ job->id); ++ return; ++ } ++ ++ job_unlock(); ++ job->driver->complete(job, errp); ++ job_lock(); ++} ++ ++int job_finish_sync_locked(Job *job, ++ void (*finish)(Job *, Error **errp), ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ int ret; ++ GLOBAL_STATE_CODE(); ++ ++ job_ref_locked(job); ++ ++ if (finish) { ++ finish(job, &local_err); ++ } ++ if (local_err) { ++ error_propagate(errp, local_err); ++ job_unref_locked(job); ++ return -EBUSY; ++ } ++ ++ job_unlock(); ++ AIO_WAIT_WHILE_UNLOCKED(job->aio_context, ++ (job_enter(job), !job_is_completed(job))); ++ job_lock(); ++ ++ ret = (job_is_cancelled_locked(job) && job->ret == 0) ++ ? -ECANCELED : job->ret; ++ job_unref_locked(job); ++ return ret; ++} +diff --git a/qcow2/lib/nbd/client-connection.c b/qcow2/lib/nbd/client-connection.c +new file mode 100644 +index 00000000..f9da67c8 +--- /dev/null ++++ b/qcow2/lib/nbd/client-connection.c +@@ -0,0 +1,422 @@ ++/* ++ * QEMU Block driver for NBD ++ * ++ * Copyright (c) 2021 Virtuozzo International GmbH. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "trace.h" ++ ++#include "block/nbd.h" ++ ++#include "qapi/qapi-visit-sockets.h" ++#include "qapi/clone-visitor.h" ++#include "qemu/coroutine.h" ++ ++struct NBDClientConnection { ++ /* Initialization constants, never change */ ++ SocketAddress *saddr; /* address to connect to */ ++ QCryptoTLSCreds *tlscreds; ++ char *tlshostname; ++ NBDExportInfo initial_info; ++ bool do_negotiation; ++ bool do_retry; ++ ++ QemuMutex mutex; ++ ++ NBDExportInfo updated_info; ++ /* ++ * @sioc represents a successful result. While thread is running, @sioc is ++ * used only by thread and not protected by mutex. When thread is not ++ * running, @sioc is stolen by nbd_co_establish_connection() under mutex. ++ */ ++ QIOChannelSocket *sioc; ++ QIOChannel *ioc; ++ /* ++ * @err represents previous attempt. It may be copied by ++ * nbd_co_establish_connection() when it reports failure. ++ */ ++ Error *err; ++ ++ /* All further fields are accessed only under mutex */ ++ bool running; /* thread is running now */ ++ bool detached; /* thread is detached and should cleanup the state */ ++ ++ /* ++ * wait_co: if non-NULL, which coroutine to wake in ++ * nbd_co_establish_connection() after yield() ++ */ ++ Coroutine *wait_co; ++}; ++ ++/* ++ * The function isn't protected by any mutex, only call it when the client ++ * connection attempt has not yet started. ++ */ ++void nbd_client_connection_enable_retry(NBDClientConnection *conn) ++{ ++ conn->do_retry = true; ++} ++ ++NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, ++ bool do_negotiation, ++ const char *export_name, ++ const char *x_dirty_bitmap, ++ QCryptoTLSCreds *tlscreds, ++ const char *tlshostname) ++{ ++ NBDClientConnection *conn = g_new(NBDClientConnection, 1); ++ ++ object_ref(OBJECT(tlscreds)); ++ *conn = (NBDClientConnection) { ++ .saddr = QAPI_CLONE(SocketAddress, saddr), ++ .tlscreds = tlscreds, ++ .tlshostname = g_strdup(tlshostname), ++ .do_negotiation = do_negotiation, ++ ++ .initial_info.request_sizes = true, ++ .initial_info.mode = NBD_MODE_EXTENDED, ++ .initial_info.base_allocation = true, ++ .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap), ++ .initial_info.name = g_strdup(export_name ?: "") ++ }; ++ ++ qemu_mutex_init(&conn->mutex); ++ ++ return conn; ++} ++ ++static void nbd_client_connection_do_free(NBDClientConnection *conn) ++{ ++ if (conn->sioc) { ++ qio_channel_close(QIO_CHANNEL(conn->sioc), NULL); ++ object_unref(OBJECT(conn->sioc)); ++ } ++ error_free(conn->err); ++ qapi_free_SocketAddress(conn->saddr); ++ g_free(conn->tlshostname); ++ object_unref(OBJECT(conn->tlscreds)); ++ g_free(conn->initial_info.x_dirty_bitmap); ++ g_free(conn->initial_info.name); ++ g_free(conn); ++} ++ ++/* ++ * Connect to @addr and do NBD negotiation if @info is not null. If @tlscreds ++ * are given @outioc is returned. @outioc is provided only on success. The call ++ * may be cancelled from other thread by simply qio_channel_shutdown(sioc). ++ */ ++static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, ++ NBDExportInfo *info, QCryptoTLSCreds *tlscreds, ++ const char *tlshostname, ++ QIOChannel **outioc, Error **errp) ++{ ++ int ret; ++ ++ if (outioc) { ++ *outioc = NULL; ++ } ++ ++ ret = qio_channel_socket_connect_sync(sioc, addr, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ qio_channel_set_delay(QIO_CHANNEL(sioc), false); ++ ++ if (!info) { ++ return 0; ++ } ++ ++ ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, tlshostname, ++ outioc, info, errp); ++ if (ret < 0) { ++ /* ++ * nbd_receive_negotiate() may setup tls ioc and return it even on ++ * failure path. In this case we should use it instead of original ++ * channel. ++ */ ++ if (outioc && *outioc) { ++ qio_channel_close(*outioc, NULL); ++ object_unref(OBJECT(*outioc)); ++ *outioc = NULL; ++ } else { ++ qio_channel_close(QIO_CHANNEL(sioc), NULL); ++ } ++ ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void *connect_thread_func(void *opaque) ++{ ++ NBDClientConnection *conn = opaque; ++ int ret; ++ bool do_free; ++ uint64_t timeout = 1; ++ uint64_t max_timeout = 16; ++ ++ qemu_mutex_lock(&conn->mutex); ++ while (!conn->detached) { ++ Error *local_err = NULL; ++ ++ assert(!conn->sioc); ++ conn->sioc = qio_channel_socket_new(); ++ ++ qemu_mutex_unlock(&conn->mutex); ++ ++ conn->updated_info = conn->initial_info; ++ ++ ret = nbd_connect(conn->sioc, conn->saddr, ++ conn->do_negotiation ? &conn->updated_info : NULL, ++ conn->tlscreds, conn->tlshostname, ++ &conn->ioc, &local_err); ++ ++ /* ++ * conn->updated_info will finally be returned to the user. Clear the ++ * pointers to our internally allocated strings, which are IN parameters ++ * of nbd_receive_negotiate() and therefore nbd_connect(). Caller ++ * shouldn't be interested in these fields. ++ */ ++ conn->updated_info.x_dirty_bitmap = NULL; ++ conn->updated_info.name = NULL; ++ ++ qemu_mutex_lock(&conn->mutex); ++ ++ error_free(conn->err); ++ conn->err = NULL; ++ error_propagate(&conn->err, local_err); ++ ++ if (ret < 0) { ++ object_unref(OBJECT(conn->sioc)); ++ conn->sioc = NULL; ++ if (conn->do_retry && !conn->detached) { ++ trace_nbd_connect_thread_sleep(timeout); ++ qemu_mutex_unlock(&conn->mutex); ++ ++ sleep(timeout); ++ if (timeout < max_timeout) { ++ timeout *= 2; ++ } ++ ++ qemu_mutex_lock(&conn->mutex); ++ continue; ++ } ++ } ++ ++ break; ++ } ++ ++ /* mutex is locked */ ++ ++ assert(conn->running); ++ conn->running = false; ++ if (conn->wait_co) { ++ aio_co_wake(conn->wait_co); ++ conn->wait_co = NULL; ++ } ++ do_free = conn->detached; ++ ++ qemu_mutex_unlock(&conn->mutex); ++ ++ if (do_free) { ++ nbd_client_connection_do_free(conn); ++ } ++ ++ return NULL; ++} ++ ++void nbd_client_connection_release(NBDClientConnection *conn) ++{ ++ bool do_free = false; ++ ++ if (!conn) { ++ return; ++ } ++ ++ WITH_QEMU_LOCK_GUARD(&conn->mutex) { ++ assert(!conn->detached); ++ if (conn->running) { ++ conn->detached = true; ++ } else { ++ do_free = true; ++ } ++ if (conn->sioc) { ++ qio_channel_shutdown(QIO_CHANNEL(conn->sioc), ++ QIO_CHANNEL_SHUTDOWN_BOTH, NULL); ++ } ++ } ++ ++ if (do_free) { ++ nbd_client_connection_do_free(conn); ++ } ++} ++ ++/* ++ * Get a new connection in context of @conn: ++ * if the thread is running, wait for completion ++ * if the thread already succeeded in the background, and user didn't get the ++ * result, just return it now ++ * otherwise the thread is not running, so start a thread and wait for ++ * completion ++ * ++ * If @blocking is false, don't wait for the thread, return immediately. ++ * ++ * If @info is not NULL, also do nbd-negotiation after successful connection. ++ * In this case info is used only as out parameter, and is fully initialized by ++ * nbd_co_establish_connection(). "IN" fields of info as well as related only to ++ * nbd_receive_export_list() would be zero (see description of NBDExportInfo in ++ * include/block/nbd.h). ++ */ ++QIOChannel *coroutine_fn ++nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info, ++ bool blocking, Error **errp) ++{ ++ QemuThread thread; ++ ++ if (conn->do_negotiation) { ++ assert(info); ++ } ++ ++ WITH_QEMU_LOCK_GUARD(&conn->mutex) { ++ /* ++ * Don't call nbd_co_establish_connection() in several coroutines in ++ * parallel. Only one call at once is supported. ++ */ ++ assert(!conn->wait_co); ++ ++ if (!conn->running) { ++ if (conn->sioc) { ++ /* Previous attempt finally succeeded in background */ ++ if (conn->do_negotiation) { ++ memcpy(info, &conn->updated_info, sizeof(*info)); ++ if (conn->ioc) { ++ /* TLS channel now has own reference to parent */ ++ object_unref(OBJECT(conn->sioc)); ++ conn->sioc = NULL; ++ ++ return g_steal_pointer(&conn->ioc); ++ } ++ } ++ ++ assert(!conn->ioc); ++ ++ return QIO_CHANNEL(g_steal_pointer(&conn->sioc)); ++ } ++ ++ conn->running = true; ++ qemu_thread_create(&thread, "nbd-connect", ++ connect_thread_func, conn, QEMU_THREAD_DETACHED); ++ } ++ ++ if (!blocking) { ++ if (conn->err) { ++ error_propagate(errp, error_copy(conn->err)); ++ } else { ++ error_setg(errp, "No connection at the moment"); ++ } ++ ++ return NULL; ++ } ++ ++ conn->wait_co = qemu_coroutine_self(); ++ } ++ ++ /* ++ * We are going to wait for connect-thread finish, but ++ * nbd_co_establish_connection_cancel() can interrupt. ++ */ ++ qemu_coroutine_yield(); ++ ++ WITH_QEMU_LOCK_GUARD(&conn->mutex) { ++ if (conn->running) { ++ /* ++ * The connection attempt was canceled and the coroutine resumed ++ * before the connection thread finished its job. Report the ++ * attempt as failed, but leave the connection thread running, ++ * to reuse it for the next connection attempt. ++ */ ++ if (conn->err) { ++ error_propagate(errp, error_copy(conn->err)); ++ } else { ++ /* ++ * The only possible case here is cancelling by open_timer ++ * during nbd_open(). So, the error message is for that case. ++ * If we have more use cases, we can refactor ++ * nbd_co_establish_connection_cancel() to take an additional ++ * parameter cancel_reason, that would be passed than to the ++ * caller of cancelled nbd_co_establish_connection(). ++ */ ++ error_setg(errp, "Connection attempt cancelled by timeout"); ++ } ++ ++ return NULL; ++ } else { ++ /* Thread finished. There must be either error or sioc */ ++ assert(!conn->err != !conn->sioc); ++ ++ if (conn->err) { ++ error_propagate(errp, error_copy(conn->err)); ++ return NULL; ++ } ++ ++ if (conn->do_negotiation) { ++ memcpy(info, &conn->updated_info, sizeof(*info)); ++ if (conn->ioc) { ++ /* TLS channel now has own reference to parent */ ++ object_unref(OBJECT(conn->sioc)); ++ conn->sioc = NULL; ++ ++ return g_steal_pointer(&conn->ioc); ++ } ++ } ++ ++ assert(!conn->ioc); ++ ++ return QIO_CHANNEL(g_steal_pointer(&conn->sioc)); ++ } ++ } ++ ++ abort(); /* unreachable */ ++} ++ ++/* ++ * nbd_co_establish_connection_cancel ++ * Cancel nbd_co_establish_connection() asynchronously. ++ * ++ * Note that this function neither directly stops the thread nor closes the ++ * socket, but rather safely wakes nbd_co_establish_connection() which is ++ * sleeping in yield() ++ */ ++void nbd_co_establish_connection_cancel(NBDClientConnection *conn) ++{ ++ Coroutine *wait_co; ++ ++ WITH_QEMU_LOCK_GUARD(&conn->mutex) { ++ wait_co = g_steal_pointer(&conn->wait_co); ++ } ++ ++ if (wait_co) { ++ aio_co_wake(wait_co); ++ } ++} +diff --git a/qcow2/lib/nbd/client.c b/qcow2/lib/nbd/client.c +new file mode 100644 +index 00000000..c89c7504 +--- /dev/null ++++ b/qcow2/lib/nbd/client.c +@@ -0,0 +1,1589 @@ ++/* ++ * Copyright Red Hat ++ * Copyright (C) 2005 Anthony Liguori ++ * ++ * Network Block Device Client Side ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; under version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/queue.h" ++#include "trace.h" ++#include "nbd-internal.h" ++#include "qemu/cutils.h" ++ ++/* Definitions for opaque data types */ ++ ++static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); ++ ++/* That's all folks */ ++ ++/* Basic flow for negotiation ++ ++ Server Client ++ Negotiate ++ ++ or ++ ++ Server Client ++ Negotiate #1 ++ Option ++ Negotiate #2 ++ ++ ---- ++ ++ followed by ++ ++ Server Client ++ Request ++ Response ++ Request ++ Response ++ ... ++ ... ++ Request (type == 2) ++ ++*/ ++ ++/* Send an option request. ++ * ++ * The request is for option @opt, with @data containing @len bytes of ++ * additional payload for the request (@len may be -1 to treat @data as ++ * a C string; and @data may be NULL if @len is 0). ++ * Return 0 if successful, -1 with errp set if it is impossible to ++ * continue. */ ++static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, ++ uint32_t len, const char *data, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ NBDOption req; ++ QEMU_BUILD_BUG_ON(sizeof(req) != 16); ++ ++ if (len == -1) { ++ req.length = len = strlen(data); ++ } ++ trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); ++ ++ stq_be_p(&req.magic, NBD_OPTS_MAGIC); ++ stl_be_p(&req.option, opt); ++ stl_be_p(&req.length, len); ++ ++ if (nbd_write(ioc, &req, sizeof(req), errp) < 0) { ++ error_prepend(errp, "Failed to send option request header: "); ++ return -1; ++ } ++ ++ if (len && nbd_write(ioc, (char *) data, len, errp) < 0) { ++ error_prepend(errp, "Failed to send option request data: "); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++/* Send NBD_OPT_ABORT as a courtesy to let the server know that we are ++ * not going to attempt further negotiation. */ ++static void nbd_send_opt_abort(QIOChannel *ioc) ++{ ++ /* Technically, a compliant server is supposed to reply to us; but ++ * older servers disconnected instead. At any rate, we're allowed ++ * to disconnect without waiting for the server reply, so we don't ++ * even care if the request makes it to the server, let alone ++ * waiting around for whether the server replies. */ ++ nbd_send_option_request(ioc, NBD_OPT_ABORT, 0, NULL, NULL); ++} ++ ++ ++/* Receive the header of an option reply, which should match the given ++ * opt. Read through the length field, but NOT the length bytes of ++ * payload. Return 0 if successful, -1 with errp set if it is ++ * impossible to continue. */ ++static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, ++ NBDOptionReply *reply, Error **errp) ++{ ++ QEMU_BUILD_BUG_ON(sizeof(*reply) != 20); ++ if (nbd_read(ioc, reply, sizeof(*reply), "option reply", errp) < 0) { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ reply->magic = be64_to_cpu(reply->magic); ++ reply->option = be32_to_cpu(reply->option); ++ reply->type = be32_to_cpu(reply->type); ++ reply->length = be32_to_cpu(reply->length); ++ ++ trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), ++ reply->type, nbd_rep_lookup(reply->type), ++ reply->length); ++ ++ if (reply->magic != NBD_REP_MAGIC) { ++ error_setg(errp, "Unexpected option reply magic"); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (reply->option != opt) { ++ error_setg(errp, "Unexpected option type %u (%s), expected %u (%s)", ++ reply->option, nbd_opt_lookup(reply->option), ++ opt, nbd_opt_lookup(opt)); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ return 0; ++} ++ ++/* ++ * If reply represents success, return 1 without further action. If ++ * reply represents an error, consume the optional payload of the ++ * packet on ioc. Then return 0 for unsupported (so the client can ++ * fall back to other approaches), where @strict determines if only ++ * ERR_UNSUP or all errors fit that category, or -1 with errp set for ++ * other errors. ++ */ ++static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, ++ bool strict, Error **errp) ++{ ++ ERRP_GUARD(); ++ g_autofree char *msg = NULL; ++ ++ if (!(reply->type & (1 << 31))) { ++ return 1; ++ } ++ ++ if (reply->length) { ++ if (reply->length > NBD_MAX_BUFFER_SIZE) { ++ error_setg(errp, "server error %" PRIu32 ++ " (%s) message is too long", ++ reply->type, nbd_rep_lookup(reply->type)); ++ goto err; ++ } ++ msg = g_malloc(reply->length + 1); ++ if (nbd_read(ioc, msg, reply->length, NULL, errp) < 0) { ++ error_prepend(errp, "Failed to read option error %" PRIu32 ++ " (%s) message: ", ++ reply->type, nbd_rep_lookup(reply->type)); ++ goto err; ++ } ++ msg[reply->length] = '\0'; ++ trace_nbd_server_error_msg(reply->type, ++ nbd_reply_type_lookup(reply->type), msg); ++ } ++ ++ if (reply->type == NBD_REP_ERR_UNSUP || !strict) { ++ trace_nbd_reply_err_ignored(reply->option, ++ nbd_opt_lookup(reply->option), ++ reply->type, nbd_rep_lookup(reply->type)); ++ return 0; ++ } ++ ++ switch (reply->type) { ++ case NBD_REP_ERR_POLICY: ++ error_setg(errp, "Denied by server for option %" PRIu32 " (%s)", ++ reply->option, nbd_opt_lookup(reply->option)); ++ break; ++ ++ case NBD_REP_ERR_INVALID: ++ error_setg(errp, "Invalid parameters for option %" PRIu32 " (%s)", ++ reply->option, nbd_opt_lookup(reply->option)); ++ break; ++ ++ case NBD_REP_ERR_PLATFORM: ++ error_setg(errp, "Server lacks support for option %" PRIu32 " (%s)", ++ reply->option, nbd_opt_lookup(reply->option)); ++ break; ++ ++ case NBD_REP_ERR_TLS_REQD: ++ error_setg(errp, "TLS negotiation required before option %" PRIu32 ++ " (%s)", reply->option, nbd_opt_lookup(reply->option)); ++ error_append_hint(errp, "Did you forget a valid tls-creds?\n"); ++ break; ++ ++ case NBD_REP_ERR_UNKNOWN: ++ error_setg(errp, "Requested export not available"); ++ break; ++ ++ case NBD_REP_ERR_SHUTDOWN: ++ error_setg(errp, "Server shutting down before option %" PRIu32 " (%s)", ++ reply->option, nbd_opt_lookup(reply->option)); ++ break; ++ ++ case NBD_REP_ERR_BLOCK_SIZE_REQD: ++ error_setg(errp, "Server requires INFO_BLOCK_SIZE for option %" PRIu32 ++ " (%s)", reply->option, nbd_opt_lookup(reply->option)); ++ break; ++ ++ default: ++ error_setg(errp, "Unknown error code when asking for option %" PRIu32 ++ " (%s)", reply->option, nbd_opt_lookup(reply->option)); ++ break; ++ } ++ ++ if (msg) { ++ error_append_hint(errp, "server reported: %s\n", msg); ++ } ++ ++ err: ++ nbd_send_opt_abort(ioc); ++ return -1; ++} ++ ++/* nbd_receive_list: ++ * Process another portion of the NBD_OPT_LIST reply, populating any ++ * name received into *@name. If @description is non-NULL, and the ++ * server provided a description, that is also populated. The caller ++ * must eventually call g_free() on success. ++ * Returns 1 if name and description were set and iteration must continue, ++ * 0 if iteration is complete (including if OPT_LIST unsupported), ++ * -1 with @errp set if an unrecoverable error occurred. ++ */ ++static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, ++ Error **errp) ++{ ++ NBDOptionReply reply; ++ uint32_t len; ++ uint32_t namelen; ++ g_autofree char *local_name = NULL; ++ g_autofree char *local_desc = NULL; ++ int error; ++ ++ if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) { ++ return -1; ++ } ++ error = nbd_handle_reply_err(ioc, &reply, true, errp); ++ if (error <= 0) { ++ return error; ++ } ++ len = reply.length; ++ ++ if (reply.type == NBD_REP_ACK) { ++ if (len != 0) { ++ error_setg(errp, "length too long for option end"); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ return 0; ++ } else if (reply.type != NBD_REP_SERVER) { ++ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)", ++ reply.type, nbd_rep_lookup(reply.type), ++ NBD_REP_SERVER, nbd_rep_lookup(NBD_REP_SERVER)); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ ++ if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) { ++ error_setg(errp, "incorrect option length %" PRIu32, len); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (nbd_read32(ioc, &namelen, "option name length", errp) < 0) { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ len -= sizeof(namelen); ++ if (len < namelen || namelen > NBD_MAX_STRING_SIZE) { ++ error_setg(errp, "incorrect name length in server's list response"); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ ++ local_name = g_malloc(namelen + 1); ++ if (nbd_read(ioc, local_name, namelen, "export name", errp) < 0) { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ local_name[namelen] = '\0'; ++ len -= namelen; ++ if (len) { ++ if (len > NBD_MAX_STRING_SIZE) { ++ error_setg(errp, "incorrect description length in server's " ++ "list response"); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ local_desc = g_malloc(len + 1); ++ if (nbd_read(ioc, local_desc, len, "export description", errp) < 0) { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ local_desc[len] = '\0'; ++ } ++ ++ trace_nbd_receive_list(local_name, local_desc ?: ""); ++ *name = g_steal_pointer(&local_name); ++ if (description) { ++ *description = g_steal_pointer(&local_desc); ++ } ++ return 1; ++} ++ ++ ++/* ++ * nbd_opt_info_or_go: ++ * Send option for NBD_OPT_INFO or NBD_OPT_GO and parse the reply. ++ * Returns -1 if the option proves the export @info->name cannot be ++ * used, 0 if the option is unsupported (fall back to NBD_OPT_LIST and ++ * NBD_OPT_EXPORT_NAME in that case), and > 0 if the export is good to ++ * go (with the rest of @info populated). ++ */ ++static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, ++ NBDExportInfo *info, Error **errp) ++{ ++ ERRP_GUARD(); ++ NBDOptionReply reply; ++ uint32_t len = strlen(info->name); ++ uint16_t type; ++ int error; ++ char *buf; ++ ++ /* The protocol requires that the server send NBD_INFO_EXPORT with ++ * a non-zero flags (at least NBD_FLAG_HAS_FLAGS must be set); so ++ * flags still 0 is a witness of a broken server. */ ++ info->flags = 0; ++ ++ assert(opt == NBD_OPT_GO || opt == NBD_OPT_INFO); ++ trace_nbd_opt_info_go_start(nbd_opt_lookup(opt), info->name); ++ buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); ++ stl_be_p(buf, len); ++ memcpy(buf + 4, info->name, len); ++ /* At most one request, everything else up to server */ ++ stw_be_p(buf + 4 + len, info->request_sizes); ++ if (info->request_sizes) { ++ stw_be_p(buf + 4 + len + 2, NBD_INFO_BLOCK_SIZE); ++ } ++ error = nbd_send_option_request(ioc, opt, ++ 4 + len + 2 + 2 * info->request_sizes, ++ buf, errp); ++ g_free(buf); ++ if (error < 0) { ++ return -1; ++ } ++ ++ while (1) { ++ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { ++ return -1; ++ } ++ error = nbd_handle_reply_err(ioc, &reply, true, errp); ++ if (error <= 0) { ++ return error; ++ } ++ len = reply.length; ++ ++ if (reply.type == NBD_REP_ACK) { ++ /* ++ * Server is done sending info, and moved into transmission ++ * phase for NBD_OPT_GO, but make sure it sent flags ++ */ ++ if (len) { ++ error_setg(errp, "server sent invalid NBD_REP_ACK"); ++ return -1; ++ } ++ if (!info->flags) { ++ error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); ++ return -1; ++ } ++ trace_nbd_opt_info_go_success(nbd_opt_lookup(opt)); ++ return 1; ++ } ++ if (reply.type != NBD_REP_INFO) { ++ error_setg(errp, "unexpected reply type %u (%s), expected %u (%s)", ++ reply.type, nbd_rep_lookup(reply.type), ++ NBD_REP_INFO, nbd_rep_lookup(NBD_REP_INFO)); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (len < sizeof(type)) { ++ error_setg(errp, "NBD_REP_INFO length %" PRIu32 " is too short", ++ len); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (nbd_read16(ioc, &type, "info type", errp) < 0) { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ len -= sizeof(type); ++ switch (type) { ++ case NBD_INFO_EXPORT: ++ if (len != sizeof(info->size) + sizeof(info->flags)) { ++ error_setg(errp, "remaining export info len %" PRIu32 ++ " is unexpected size", len); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (nbd_read64(ioc, &info->size, "info size", errp) < 0) { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (nbd_read16(ioc, &info->flags, "info flags", errp) < 0) { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (info->min_block && ++ !QEMU_IS_ALIGNED(info->size, info->min_block)) { ++ error_setg(errp, "export size %" PRIu64 " is not multiple of " ++ "minimum block size %" PRIu32, info->size, ++ info->min_block); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ trace_nbd_receive_negotiate_size_flags(info->size, info->flags); ++ break; ++ ++ case NBD_INFO_BLOCK_SIZE: ++ if (len != sizeof(info->min_block) * 3) { ++ error_setg(errp, "remaining export info len %" PRIu32 ++ " is unexpected size", len); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (nbd_read32(ioc, &info->min_block, "info minimum block size", ++ errp) < 0) { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (!is_power_of_2(info->min_block)) { ++ error_setg(errp, "server minimum block size %" PRIu32 ++ " is not a power of two", info->min_block); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (nbd_read32(ioc, &info->opt_block, "info preferred block size", ++ errp) < 0) ++ { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (!is_power_of_2(info->opt_block) || ++ info->opt_block < info->min_block) { ++ error_setg(errp, "server preferred block size %" PRIu32 ++ " is not valid", info->opt_block); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (nbd_read32(ioc, &info->max_block, "info maximum block size", ++ errp) < 0) ++ { ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ if (info->max_block < info->min_block) { ++ error_setg(errp, "server maximum block size %" PRIu32 ++ " is not valid", info->max_block); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ trace_nbd_opt_info_block_size(info->min_block, info->opt_block, ++ info->max_block); ++ break; ++ ++ default: ++ /* ++ * Not worth the bother to check if NBD_INFO_NAME or ++ * NBD_INFO_DESCRIPTION exceed NBD_MAX_STRING_SIZE. ++ */ ++ trace_nbd_opt_info_unknown(type, nbd_info_lookup(type)); ++ if (nbd_drop(ioc, len, errp) < 0) { ++ error_prepend(errp, "Failed to read info payload: "); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ break; ++ } ++ } ++} ++ ++/* Return -1 on failure, 0 if wantname is an available export. */ ++static int nbd_receive_query_exports(QIOChannel *ioc, ++ const char *wantname, ++ Error **errp) ++{ ++ bool list_empty = true; ++ bool found_export = false; ++ ++ trace_nbd_receive_query_exports_start(wantname); ++ if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) { ++ return -1; ++ } ++ ++ while (1) { ++ char *name; ++ int ret = nbd_receive_list(ioc, &name, NULL, errp); ++ ++ if (ret < 0) { ++ /* Server gave unexpected reply */ ++ return -1; ++ } else if (ret == 0) { ++ /* Done iterating. */ ++ if (list_empty) { ++ /* ++ * We don't have enough context to tell a server that ++ * sent an empty list apart from a server that does ++ * not support the list command; but as this function ++ * is just used to trigger a nicer error message ++ * before trying NBD_OPT_EXPORT_NAME, assume the ++ * export is available. ++ */ ++ return 0; ++ } else if (!found_export) { ++ error_setg(errp, "No export with name '%s' available", ++ wantname); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ trace_nbd_receive_query_exports_success(wantname); ++ return 0; ++ } ++ list_empty = false; ++ if (!strcmp(name, wantname)) { ++ found_export = true; ++ } ++ g_free(name); ++ } ++} ++ ++/* ++ * nbd_request_simple_option: Send an option request, and parse the reply. ++ * @strict controls whether ERR_UNSUP or all errors produce 0 status. ++ * return 1 for successful negotiation, ++ * 0 if operation is unsupported, ++ * -1 with errp set for any other error ++ */ ++static int nbd_request_simple_option(QIOChannel *ioc, int opt, bool strict, ++ Error **errp) ++{ ++ NBDOptionReply reply; ++ int error; ++ ++ if (nbd_send_option_request(ioc, opt, 0, NULL, errp) < 0) { ++ return -1; ++ } ++ ++ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { ++ return -1; ++ } ++ error = nbd_handle_reply_err(ioc, &reply, strict, errp); ++ if (error <= 0) { ++ return error; ++ } ++ ++ if (reply.type != NBD_REP_ACK) { ++ error_setg(errp, "Server answered option %d (%s) with unexpected " ++ "reply %" PRIu32 " (%s)", opt, nbd_opt_lookup(opt), ++ reply.type, nbd_rep_lookup(reply.type)); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ ++ if (reply.length != 0) { ++ error_setg(errp, "Option %d ('%s') response length is %" PRIu32 ++ " (it should be zero)", opt, nbd_opt_lookup(opt), ++ reply.length); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ ++ return 1; ++} ++ ++/* Callback to learn when QIO TLS upgrade is complete */ ++struct NBDTLSClientHandshakeData { ++ bool complete; ++ Error *error; ++ GMainLoop *loop; ++}; ++ ++static void nbd_client_tls_handshake(QIOTask *task, void *opaque) ++{ ++ struct NBDTLSClientHandshakeData *data = opaque; ++ ++ qio_task_propagate_error(task, &data->error); ++ data->complete = true; ++ if (data->loop) { ++ g_main_loop_quit(data->loop); ++ } ++} ++ ++static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, ++ QCryptoTLSCreds *tlscreds, ++ const char *hostname, Error **errp) ++{ ++ int ret; ++ QIOChannelTLS *tioc; ++ struct NBDTLSClientHandshakeData data = { 0 }; ++ ++ ret = nbd_request_simple_option(ioc, NBD_OPT_STARTTLS, true, errp); ++ if (ret <= 0) { ++ if (ret == 0) { ++ error_setg(errp, "Server don't support STARTTLS option"); ++ nbd_send_opt_abort(ioc); ++ } ++ return NULL; ++ } ++ ++ trace_nbd_receive_starttls_new_client(); ++ tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp); ++ if (!tioc) { ++ return NULL; ++ } ++ qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls"); ++ trace_nbd_receive_starttls_tls_handshake(); ++ qio_channel_tls_handshake(tioc, ++ nbd_client_tls_handshake, ++ &data, ++ NULL, ++ NULL); ++ ++ if (!data.complete) { ++ data.loop = g_main_loop_new(g_main_context_default(), FALSE); ++ g_main_loop_run(data.loop); ++ assert(data.complete); ++ g_main_loop_unref(data.loop); ++ } ++ ++ if (data.error) { ++ error_propagate(errp, data.error); ++ object_unref(OBJECT(tioc)); ++ return NULL; ++ } ++ ++ return QIO_CHANNEL(tioc); ++} ++ ++/* ++ * nbd_send_meta_query: ++ * Send 0 or 1 set/list meta context queries. ++ * Return 0 on success, -1 with errp set for any error ++ */ ++static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt, ++ const char *export, const char *query, ++ Error **errp) ++{ ++ int ret; ++ uint32_t export_len; ++ uint32_t queries = !!query; ++ uint32_t query_len = 0; ++ uint32_t data_len; ++ char *data; ++ char *p; ++ ++ assert(strnlen(export, NBD_MAX_STRING_SIZE + 1) <= NBD_MAX_STRING_SIZE); ++ export_len = strlen(export); ++ data_len = sizeof(export_len) + export_len + sizeof(queries); ++ if (query) { ++ assert(strnlen(query, NBD_MAX_STRING_SIZE + 1) <= NBD_MAX_STRING_SIZE); ++ query_len = strlen(query); ++ data_len += sizeof(query_len) + query_len; ++ } else { ++ assert(opt == NBD_OPT_LIST_META_CONTEXT); ++ } ++ p = data = g_malloc(data_len); ++ ++ trace_nbd_opt_meta_request(nbd_opt_lookup(opt), query ?: "(all)", export); ++ stl_be_p(p, export_len); ++ memcpy(p += sizeof(export_len), export, export_len); ++ stl_be_p(p += export_len, queries); ++ if (query) { ++ stl_be_p(p += sizeof(queries), query_len); ++ memcpy(p += sizeof(query_len), query, query_len); ++ } ++ ++ ret = nbd_send_option_request(ioc, opt, data_len, data, errp); ++ g_free(data); ++ return ret; ++} ++ ++/* ++ * nbd_receive_one_meta_context: ++ * Called in a loop to receive and trace one set/list meta context reply. ++ * Pass non-NULL @name or @id to collect results back to the caller, which ++ * must eventually call g_free(). ++ * return 1 if name is set and iteration must continue, ++ * 0 if iteration is complete (including if option is unsupported), ++ * -1 with errp set for any error ++ */ ++static int nbd_receive_one_meta_context(QIOChannel *ioc, ++ uint32_t opt, ++ char **name, ++ uint32_t *id, ++ Error **errp) ++{ ++ int ret; ++ NBDOptionReply reply; ++ char *local_name = NULL; ++ uint32_t local_id; ++ ++ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { ++ return -1; ++ } ++ ++ ret = nbd_handle_reply_err(ioc, &reply, false, errp); ++ if (ret <= 0) { ++ return ret; ++ } ++ ++ if (reply.type == NBD_REP_ACK) { ++ if (reply.length != 0) { ++ error_setg(errp, "Unexpected length to ACK response"); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ return 0; ++ } else if (reply.type != NBD_REP_META_CONTEXT) { ++ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)", ++ reply.type, nbd_rep_lookup(reply.type), ++ NBD_REP_META_CONTEXT, nbd_rep_lookup(NBD_REP_META_CONTEXT)); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ ++ if (reply.length <= sizeof(local_id) || ++ reply.length > NBD_MAX_BUFFER_SIZE) { ++ error_setg(errp, "Failed to negotiate meta context, server " ++ "answered with unexpected length %" PRIu32, ++ reply.length); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ ++ if (nbd_read32(ioc, &local_id, "context id", errp) < 0) { ++ return -1; ++ } ++ ++ reply.length -= sizeof(local_id); ++ local_name = g_malloc(reply.length + 1); ++ if (nbd_read(ioc, local_name, reply.length, "context name", errp) < 0) { ++ g_free(local_name); ++ return -1; ++ } ++ local_name[reply.length] = '\0'; ++ trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id); ++ ++ if (name) { ++ *name = local_name; ++ } else { ++ g_free(local_name); ++ } ++ if (id) { ++ *id = local_id; ++ } ++ return 1; ++} ++ ++/* ++ * nbd_negotiate_simple_meta_context: ++ * Request the server to set the meta context for export @info->name ++ * using @info->x_dirty_bitmap with a fallback to "base:allocation", ++ * setting @info->context_id to the resulting id. Fail if the server ++ * responds with more than one context or with a context different ++ * than the query. ++ * return 1 for successful negotiation, ++ * 0 if operation is unsupported, ++ * -1 with errp set for any other error ++ */ ++static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, ++ NBDExportInfo *info, ++ Error **errp) ++{ ++ /* ++ * TODO: Removing the x_dirty_bitmap hack will mean refactoring ++ * this function to request and store ids for multiple contexts ++ * (both base:allocation and a dirty bitmap), at which point this ++ * function should lose the term _simple. ++ */ ++ int ret; ++ const char *context = info->x_dirty_bitmap ?: "base:allocation"; ++ bool received = false; ++ char *name = NULL; ++ ++ if (nbd_send_meta_query(ioc, NBD_OPT_SET_META_CONTEXT, ++ info->name, context, errp) < 0) { ++ return -1; ++ } ++ ++ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT, ++ &name, &info->context_id, errp); ++ if (ret < 0) { ++ return -1; ++ } ++ if (ret == 1) { ++ if (strcmp(context, name)) { ++ error_setg(errp, "Failed to negotiate meta context '%s', server " ++ "answered with different context '%s'", context, ++ name); ++ g_free(name); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ g_free(name); ++ received = true; ++ ++ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT, ++ NULL, NULL, errp); ++ if (ret < 0) { ++ return -1; ++ } ++ } ++ if (ret != 0) { ++ error_setg(errp, "Server answered with more than one context"); ++ nbd_send_opt_abort(ioc); ++ return -1; ++ } ++ return received; ++} ++ ++/* ++ * nbd_list_meta_contexts: ++ * Request the server to list all meta contexts for export @info->name. ++ * return 0 if list is complete (even if empty), ++ * -1 with errp set for any error ++ */ ++static int nbd_list_meta_contexts(QIOChannel *ioc, ++ NBDExportInfo *info, ++ Error **errp) ++{ ++ int ret; ++ int seen_any = false; ++ int seen_qemu = false; ++ ++ if (nbd_send_meta_query(ioc, NBD_OPT_LIST_META_CONTEXT, ++ info->name, NULL, errp) < 0) { ++ return -1; ++ } ++ ++ while (1) { ++ char *context; ++ ++ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_LIST_META_CONTEXT, ++ &context, NULL, errp); ++ if (ret == 0 && seen_any && !seen_qemu) { ++ /* ++ * Work around qemu 3.0 bug: the server forgot to send ++ * "qemu:" replies to 0 queries. If we saw at least one ++ * reply (probably base:allocation), but none of them were ++ * qemu:, then run a more specific query to make sure. ++ */ ++ seen_qemu = true; ++ if (nbd_send_meta_query(ioc, NBD_OPT_LIST_META_CONTEXT, ++ info->name, "qemu:", errp) < 0) { ++ return -1; ++ } ++ continue; ++ } ++ if (ret <= 0) { ++ return ret; ++ } ++ seen_any = true; ++ seen_qemu |= strstart(context, "qemu:", NULL); ++ info->contexts = g_renew(char *, info->contexts, ++info->n_contexts); ++ info->contexts[info->n_contexts - 1] = context; ++ } ++} ++ ++/* ++ * nbd_start_negotiate: ++ * Start the handshake to the server. After a positive return, the server ++ * is ready to accept additional NBD_OPT requests. ++ * Returns: negative errno: failure talking to server ++ * non-negative: enum NBDMode describing server abilities ++ */ ++static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, QIOChannel **outioc, ++ NBDMode max_mode, bool *zeroes, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ uint64_t magic; ++ ++ trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); ++ ++ if (zeroes) { ++ *zeroes = true; ++ } ++ if (outioc) { ++ *outioc = NULL; ++ } ++ if (tlscreds && !outioc) { ++ error_setg(errp, "Output I/O channel required for TLS"); ++ return -EINVAL; ++ } ++ ++ if (nbd_read64(ioc, &magic, "initial magic", errp) < 0) { ++ return -EINVAL; ++ } ++ trace_nbd_receive_negotiate_magic(magic); ++ ++ if (magic != NBD_INIT_MAGIC) { ++ error_setg(errp, "Bad initial magic received: 0x%" PRIx64, magic); ++ return -EINVAL; ++ } ++ ++ if (nbd_read64(ioc, &magic, "server magic", errp) < 0) { ++ return -EINVAL; ++ } ++ trace_nbd_receive_negotiate_magic(magic); ++ ++ if (magic == NBD_OPTS_MAGIC) { ++ uint32_t clientflags = 0; ++ uint16_t globalflags; ++ bool fixedNewStyle = false; ++ ++ if (nbd_read16(ioc, &globalflags, "server flags", errp) < 0) { ++ return -EINVAL; ++ } ++ trace_nbd_receive_negotiate_server_flags(globalflags); ++ if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { ++ fixedNewStyle = true; ++ clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; ++ } ++ if (globalflags & NBD_FLAG_NO_ZEROES) { ++ if (zeroes) { ++ *zeroes = false; ++ } ++ clientflags |= NBD_FLAG_C_NO_ZEROES; ++ } ++ /* client requested flags */ ++ clientflags = cpu_to_be32(clientflags); ++ if (nbd_write(ioc, &clientflags, sizeof(clientflags), errp) < 0) { ++ error_prepend(errp, "Failed to send clientflags field: "); ++ return -EINVAL; ++ } ++ if (tlscreds) { ++ if (fixedNewStyle) { ++ *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp); ++ if (!*outioc) { ++ return -EINVAL; ++ } ++ ioc = *outioc; ++ } else { ++ error_setg(errp, "Server does not support STARTTLS"); ++ return -EINVAL; ++ } ++ } ++ if (fixedNewStyle) { ++ int result = 0; ++ ++ if (max_mode >= NBD_MODE_EXTENDED) { ++ result = nbd_request_simple_option(ioc, ++ NBD_OPT_EXTENDED_HEADERS, ++ false, errp); ++ if (result) { ++ return result < 0 ? -EINVAL : NBD_MODE_EXTENDED; ++ } ++ } ++ if (max_mode >= NBD_MODE_STRUCTURED) { ++ result = nbd_request_simple_option(ioc, ++ NBD_OPT_STRUCTURED_REPLY, ++ false, errp); ++ if (result) { ++ return result < 0 ? -EINVAL : NBD_MODE_STRUCTURED; ++ } ++ } ++ return NBD_MODE_SIMPLE; ++ } else { ++ return NBD_MODE_EXPORT_NAME; ++ } ++ } else if (magic == NBD_CLIENT_MAGIC) { ++ if (tlscreds) { ++ error_setg(errp, "Server does not support STARTTLS"); ++ return -EINVAL; ++ } ++ return NBD_MODE_OLDSTYLE; ++ } else { ++ error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic); ++ return -EINVAL; ++ } ++} ++ ++/* ++ * nbd_negotiate_finish_oldstyle: ++ * Populate @info with the size and export flags from an oldstyle server, ++ * but does not consume 124 bytes of reserved zero padding. ++ * Returns 0 on success, -1 with @errp set on failure ++ */ ++static int nbd_negotiate_finish_oldstyle(QIOChannel *ioc, NBDExportInfo *info, ++ Error **errp) ++{ ++ uint32_t oldflags; ++ ++ if (nbd_read64(ioc, &info->size, "export length", errp) < 0) { ++ return -EINVAL; ++ } ++ ++ if (nbd_read32(ioc, &oldflags, "export flags", errp) < 0) { ++ return -EINVAL; ++ } ++ if (oldflags & ~0xffff) { ++ error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); ++ return -EINVAL; ++ } ++ info->flags = oldflags; ++ return 0; ++} ++ ++/* ++ * nbd_receive_negotiate: ++ * Connect to server, complete negotiation, and move into transmission phase. ++ * Returns: negative errno: failure talking to server ++ * 0: server is connected ++ */ ++int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, QIOChannel **outioc, ++ NBDExportInfo *info, Error **errp) ++{ ++ ERRP_GUARD(); ++ int result; ++ bool zeroes; ++ bool base_allocation = info->base_allocation; ++ ++ assert(info->name && strlen(info->name) <= NBD_MAX_STRING_SIZE); ++ trace_nbd_receive_negotiate_name(info->name); ++ ++ result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, ++ info->mode, &zeroes, errp); ++ if (result < 0) { ++ return result; ++ } ++ ++ info->mode = result; ++ info->base_allocation = false; ++ if (tlscreds && *outioc) { ++ ioc = *outioc; ++ } ++ ++ switch (info->mode) { ++ case NBD_MODE_EXTENDED: ++ case NBD_MODE_STRUCTURED: ++ if (base_allocation) { ++ result = nbd_negotiate_simple_meta_context(ioc, info, errp); ++ if (result < 0) { ++ return -EINVAL; ++ } ++ info->base_allocation = result == 1; ++ } ++ /* fall through */ ++ case NBD_MODE_SIMPLE: ++ /* Try NBD_OPT_GO first - if it works, we are done (it ++ * also gives us a good message if the server requires ++ * TLS). If it is not available, fall back to ++ * NBD_OPT_LIST for nicer error messages about a missing ++ * export, then use NBD_OPT_EXPORT_NAME. */ ++ result = nbd_opt_info_or_go(ioc, NBD_OPT_GO, info, errp); ++ if (result < 0) { ++ return -EINVAL; ++ } ++ if (result > 0) { ++ return 0; ++ } ++ /* Check our desired export is present in the ++ * server export list. Since NBD_OPT_EXPORT_NAME ++ * cannot return an error message, running this ++ * query gives us better error reporting if the ++ * export name is not available. ++ */ ++ if (nbd_receive_query_exports(ioc, info->name, errp) < 0) { ++ return -EINVAL; ++ } ++ /* fall through */ ++ case NBD_MODE_EXPORT_NAME: ++ /* write the export name request */ ++ if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, info->name, ++ errp) < 0) { ++ return -EINVAL; ++ } ++ ++ /* Read the response */ ++ if (nbd_read64(ioc, &info->size, "export length", errp) < 0) { ++ return -EINVAL; ++ } ++ ++ if (nbd_read16(ioc, &info->flags, "export flags", errp) < 0) { ++ return -EINVAL; ++ } ++ break; ++ case NBD_MODE_OLDSTYLE: ++ if (*info->name) { ++ error_setg(errp, "Server does not support non-empty export names"); ++ return -EINVAL; ++ } ++ if (nbd_negotiate_finish_oldstyle(ioc, info, errp) < 0) { ++ return -EINVAL; ++ } ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ trace_nbd_receive_negotiate_size_flags(info->size, info->flags); ++ if (zeroes && nbd_drop(ioc, 124, errp) < 0) { ++ error_prepend(errp, "Failed to read reserved block: "); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* Clean up result of nbd_receive_export_list */ ++void nbd_free_export_list(NBDExportInfo *info, int count) ++{ ++ int i, j; ++ ++ if (!info) { ++ return; ++ } ++ ++ for (i = 0; i < count; i++) { ++ g_free(info[i].name); ++ g_free(info[i].description); ++ for (j = 0; j < info[i].n_contexts; j++) { ++ g_free(info[i].contexts[j]); ++ } ++ g_free(info[i].contexts); ++ } ++ g_free(info); ++} ++ ++/* ++ * nbd_receive_export_list: ++ * Query details about a server's exports, then disconnect without ++ * going into transmission phase. Return a count of the exports listed ++ * in @info by the server, or -1 on error. Caller must free @info using ++ * nbd_free_export_list(). ++ */ ++int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, ++ const char *hostname, NBDExportInfo **info, ++ Error **errp) ++{ ++ int result; ++ int count = 0; ++ int i; ++ int rc; ++ int ret = -1; ++ NBDExportInfo *array = NULL; ++ QIOChannel *sioc = NULL; ++ ++ *info = NULL; ++ result = nbd_start_negotiate(ioc, tlscreds, hostname, &sioc, ++ NBD_MODE_EXTENDED, NULL, errp); ++ if (tlscreds && sioc) { ++ ioc = sioc; ++ } ++ if (result < 0) { ++ goto out; ++ } ++ ++ switch ((NBDMode)result) { ++ case NBD_MODE_SIMPLE: ++ case NBD_MODE_STRUCTURED: ++ case NBD_MODE_EXTENDED: ++ /* newstyle - use NBD_OPT_LIST to populate array, then try ++ * NBD_OPT_INFO on each array member. If structured replies ++ * are enabled, also try NBD_OPT_LIST_META_CONTEXT. */ ++ if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) { ++ goto out; ++ } ++ while (1) { ++ char *name; ++ char *desc; ++ ++ rc = nbd_receive_list(ioc, &name, &desc, errp); ++ if (rc < 0) { ++ goto out; ++ } else if (rc == 0) { ++ break; ++ } ++ array = g_renew(NBDExportInfo, array, ++count); ++ memset(&array[count - 1], 0, sizeof(*array)); ++ array[count - 1].name = name; ++ array[count - 1].description = desc; ++ array[count - 1].mode = result; ++ } ++ ++ for (i = 0; i < count; i++) { ++ array[i].request_sizes = true; ++ rc = nbd_opt_info_or_go(ioc, NBD_OPT_INFO, &array[i], errp); ++ if (rc < 0) { ++ goto out; ++ } else if (rc == 0) { ++ /* ++ * Pointless to try rest of loop. If OPT_INFO doesn't work, ++ * it's unlikely that meta contexts work either ++ */ ++ break; ++ } ++ ++ if (result >= NBD_MODE_STRUCTURED && ++ nbd_list_meta_contexts(ioc, &array[i], errp) < 0) { ++ goto out; ++ } ++ } ++ ++ /* Send NBD_OPT_ABORT as a courtesy before hanging up */ ++ nbd_send_opt_abort(ioc); ++ break; ++ case NBD_MODE_EXPORT_NAME: ++ error_setg(errp, "Server does not support export lists"); ++ /* We can't even send NBD_OPT_ABORT, so merely hang up */ ++ goto out; ++ case NBD_MODE_OLDSTYLE: ++ /* Lone export name is implied, but we can parse length and flags */ ++ array = g_new0(NBDExportInfo, 1); ++ array->name = g_strdup(""); ++ array->mode = NBD_MODE_OLDSTYLE; ++ count = 1; ++ ++ if (nbd_negotiate_finish_oldstyle(ioc, array, errp) < 0) { ++ goto out; ++ } ++ ++ /* Send NBD_CMD_DISC as a courtesy to the server, but ignore all ++ * errors now that we have the information we wanted. */ ++ if (nbd_drop(ioc, 124, NULL) == 0) { ++ NBDRequest request = { .type = NBD_CMD_DISC, .mode = result }; ++ ++ nbd_send_request(ioc, &request); ++ } ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ *info = array; ++ array = NULL; ++ ret = count; ++ ++ out: ++ qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); ++ qio_channel_close(ioc, NULL); ++ object_unref(OBJECT(sioc)); ++ nbd_free_export_list(array, count); ++ return ret; ++} ++ ++#ifdef __linux__ ++int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, ++ Error **errp) ++{ ++ unsigned long sector_size = MAX(BDRV_SECTOR_SIZE, info->min_block); ++ unsigned long sectors = info->size / sector_size; ++ ++ /* FIXME: Once the kernel module is patched to honor block sizes, ++ * and to advertise that fact to user space, we should update the ++ * hand-off to the kernel to use any block sizes we learned. */ ++ assert(!info->request_sizes); ++ if (info->size / sector_size != sectors) { ++ error_setg(errp, "Export size %" PRIu64 " too large for 32-bit kernel", ++ info->size); ++ return -E2BIG; ++ } ++ ++ trace_nbd_init_set_socket(); ++ ++ if (ioctl(fd, NBD_SET_SOCK, (unsigned long) sioc->fd) < 0) { ++ int serrno = errno; ++ error_setg(errp, "Failed to set NBD socket"); ++ return -serrno; ++ } ++ ++ trace_nbd_init_set_block_size(sector_size); ++ ++ if (ioctl(fd, NBD_SET_BLKSIZE, sector_size) < 0) { ++ int serrno = errno; ++ error_setg(errp, "Failed setting NBD block size"); ++ return -serrno; ++ } ++ ++ trace_nbd_init_set_size(sectors); ++ if (info->size % sector_size) { ++ trace_nbd_init_trailing_bytes(info->size % sector_size); ++ } ++ ++ if (ioctl(fd, NBD_SET_SIZE_BLOCKS, sectors) < 0) { ++ int serrno = errno; ++ error_setg(errp, "Failed setting size (in blocks)"); ++ return -serrno; ++ } ++ ++ if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) info->flags) < 0) { ++ if (errno == ENOTTY) { ++ int read_only = (info->flags & NBD_FLAG_READ_ONLY) != 0; ++ trace_nbd_init_set_readonly(); ++ ++ if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { ++ int serrno = errno; ++ error_setg(errp, "Failed setting read-only attribute"); ++ return -serrno; ++ } ++ } else { ++ int serrno = errno; ++ error_setg(errp, "Failed setting flags"); ++ return -serrno; ++ } ++ } ++ ++ trace_nbd_init_finish(); ++ ++ return 0; ++} ++ ++int nbd_client(int fd) ++{ ++ int ret; ++ int serrno; ++ ++ trace_nbd_client_loop(); ++ ++ ret = ioctl(fd, NBD_DO_IT); ++ if (ret < 0 && errno == EPIPE) { ++ /* NBD_DO_IT normally returns EPIPE when someone has disconnected ++ * the socket via NBD_DISCONNECT. We do not want to return 1 in ++ * that case. ++ */ ++ ret = 0; ++ } ++ serrno = errno; ++ ++ trace_nbd_client_loop_ret(ret, strerror(serrno)); ++ ++ trace_nbd_client_clear_queue(); ++ ioctl(fd, NBD_CLEAR_QUE); ++ ++ trace_nbd_client_clear_socket(); ++ ioctl(fd, NBD_CLEAR_SOCK); ++ ++ errno = serrno; ++ return ret; ++} ++ ++int nbd_disconnect(int fd) ++{ ++ ioctl(fd, NBD_CLEAR_QUE); ++ ioctl(fd, NBD_DISCONNECT); ++ ioctl(fd, NBD_CLEAR_SOCK); ++ return 0; ++} ++ ++#endif /* __linux__ */ ++ ++int nbd_send_request(QIOChannel *ioc, NBDRequest *request) ++{ ++ uint8_t buf[NBD_EXTENDED_REQUEST_SIZE]; ++ size_t len; ++ ++ trace_nbd_send_request(request->from, request->len, request->cookie, ++ request->flags, request->type, ++ nbd_cmd_lookup(request->type)); ++ ++ stw_be_p(buf + 4, request->flags); ++ stw_be_p(buf + 6, request->type); ++ stq_be_p(buf + 8, request->cookie); ++ stq_be_p(buf + 16, request->from); ++ if (request->mode >= NBD_MODE_EXTENDED) { ++ stl_be_p(buf, NBD_EXTENDED_REQUEST_MAGIC); ++ stq_be_p(buf + 24, request->len); ++ len = NBD_EXTENDED_REQUEST_SIZE; ++ } else { ++ assert(request->len <= UINT32_MAX); ++ stl_be_p(buf, NBD_REQUEST_MAGIC); ++ stl_be_p(buf + 24, request->len); ++ len = NBD_REQUEST_SIZE; ++ } ++ ++ return nbd_write(ioc, buf, len, NULL); ++} ++ ++/* nbd_receive_simple_reply ++ * Read simple reply except magic field (which should be already read). ++ * Payload is not read (payload is possible for CMD_READ, but here we even ++ * don't know whether it take place or not). ++ */ ++static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply, ++ Error **errp) ++{ ++ int ret; ++ ++ assert(reply->magic == NBD_SIMPLE_REPLY_MAGIC); ++ ++ ret = nbd_read(ioc, (uint8_t *)reply + sizeof(reply->magic), ++ sizeof(*reply) - sizeof(reply->magic), "reply", errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ reply->error = be32_to_cpu(reply->error); ++ reply->cookie = be64_to_cpu(reply->cookie); ++ ++ return 0; ++} ++ ++/* nbd_receive_reply_chunk_header ++ * Read structured reply chunk except magic field (which should be already ++ * read). Normalize into the compact form. ++ * Payload is not read. ++ */ ++static int nbd_receive_reply_chunk_header(QIOChannel *ioc, NBDReply *chunk, ++ Error **errp) ++{ ++ int ret; ++ size_t len; ++ uint64_t payload_len; ++ ++ if (chunk->magic == NBD_STRUCTURED_REPLY_MAGIC) { ++ len = sizeof(chunk->structured); ++ } else { ++ assert(chunk->magic == NBD_EXTENDED_REPLY_MAGIC); ++ len = sizeof(chunk->extended); ++ } ++ ++ ret = nbd_read(ioc, (uint8_t *)chunk + sizeof(chunk->magic), ++ len - sizeof(chunk->magic), "structured chunk", ++ errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* flags, type, and cookie occupy same space between forms */ ++ chunk->structured.flags = be16_to_cpu(chunk->structured.flags); ++ chunk->structured.type = be16_to_cpu(chunk->structured.type); ++ chunk->structured.cookie = be64_to_cpu(chunk->structured.cookie); ++ ++ /* ++ * Because we use BLOCK_STATUS with REQ_ONE, and cap READ requests ++ * at 32M, no valid server should send us payload larger than ++ * this. Even if we stopped using REQ_ONE, sane servers will cap ++ * the number of extents they return for block status. ++ */ ++ if (chunk->magic == NBD_STRUCTURED_REPLY_MAGIC) { ++ payload_len = be32_to_cpu(chunk->structured.length); ++ } else { ++ /* For now, we are ignoring the extended header offset. */ ++ payload_len = be64_to_cpu(chunk->extended.length); ++ chunk->magic = NBD_STRUCTURED_REPLY_MAGIC; ++ } ++ if (payload_len > NBD_MAX_BUFFER_SIZE + sizeof(NBDStructuredReadData)) { ++ error_setg(errp, "server chunk %" PRIu32 " (%s) payload is too long", ++ chunk->structured.type, ++ nbd_rep_lookup(chunk->structured.type)); ++ return -EINVAL; ++ } ++ chunk->structured.length = payload_len; ++ ++ return 0; ++} ++ ++/* nbd_read_eof ++ * Tries to read @size bytes from @ioc. ++ * Returns 1 on success ++ * 0 on eof, when no data was read (errp is not set) ++ * negative errno on failure (errp is set) ++ */ ++static inline int coroutine_fn ++nbd_read_eof(BlockDriverState *bs, QIOChannel *ioc, void *buffer, size_t size, ++ Error **errp) ++{ ++ bool partial = false; ++ ++ assert(size); ++ while (size > 0) { ++ struct iovec iov = { .iov_base = buffer, .iov_len = size }; ++ ssize_t len; ++ ++ len = qio_channel_readv(ioc, &iov, 1, errp); ++ if (len == QIO_CHANNEL_ERR_BLOCK) { ++ qio_channel_yield(ioc, G_IO_IN); ++ continue; ++ } else if (len < 0) { ++ return -EIO; ++ } else if (len == 0) { ++ if (partial) { ++ error_setg(errp, ++ "Unexpected end-of-file before all bytes were read"); ++ return -EIO; ++ } else { ++ return 0; ++ } ++ } ++ ++ partial = true; ++ size -= len; ++ buffer = (uint8_t*) buffer + len; ++ } ++ return 1; ++} ++ ++/* nbd_receive_reply ++ * ++ * Wait for a new reply. If this yields, the coroutine must be able to be ++ * safely reentered for nbd_client_attach_aio_context(). @mode determines ++ * which reply magic we are expecting, although this normalizes the result ++ * so that the caller only has to work with compact headers. ++ * ++ * Returns 1 on success ++ * 0 on eof, when no data was read ++ * negative errno on failure ++ */ ++int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, ++ NBDReply *reply, NBDMode mode, Error **errp) ++{ ++ int ret; ++ const char *type; ++ uint32_t expected; ++ ++ ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp); ++ if (ret <= 0) { ++ return ret; ++ } ++ ++ reply->magic = be32_to_cpu(reply->magic); ++ ++ /* Diagnose but accept wrong-width header */ ++ switch (reply->magic) { ++ case NBD_SIMPLE_REPLY_MAGIC: ++ if (mode >= NBD_MODE_EXTENDED) { ++ trace_nbd_receive_wrong_header(reply->magic, ++ nbd_mode_lookup(mode)); ++ } ++ ret = nbd_receive_simple_reply(ioc, &reply->simple, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ trace_nbd_receive_simple_reply(reply->simple.error, ++ nbd_err_lookup(reply->simple.error), ++ reply->cookie); ++ break; ++ case NBD_STRUCTURED_REPLY_MAGIC: ++ case NBD_EXTENDED_REPLY_MAGIC: ++ expected = mode >= NBD_MODE_EXTENDED ? NBD_EXTENDED_REPLY_MAGIC ++ : NBD_STRUCTURED_REPLY_MAGIC; ++ if (reply->magic != expected) { ++ trace_nbd_receive_wrong_header(reply->magic, ++ nbd_mode_lookup(mode)); ++ } ++ ret = nbd_receive_reply_chunk_header(ioc, reply, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ type = nbd_reply_type_lookup(reply->structured.type); ++ trace_nbd_receive_reply_chunk_header(reply->structured.flags, ++ reply->structured.type, type, ++ reply->structured.cookie, ++ reply->structured.length); ++ break; ++ default: ++ trace_nbd_receive_wrong_header(reply->magic, nbd_mode_lookup(mode)); ++ error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", reply->magic); ++ return -EINVAL; ++ } ++ ++ return 1; ++} ++ +diff --git a/qcow2/lib/nbd/common.c b/qcow2/lib/nbd/common.c +new file mode 100644 +index 00000000..589a748c +--- /dev/null ++++ b/qcow2/lib/nbd/common.c +@@ -0,0 +1,266 @@ ++/* ++ * Copyright (C) 2005 Anthony Liguori ++ * ++ * Network Block Device Common Code ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; under version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "trace.h" ++#include "nbd-internal.h" ++ ++/* Discard length bytes from channel. Return -errno on failure and 0 on ++ * success */ ++int nbd_drop(QIOChannel *ioc, size_t size, Error **errp) ++{ ++ ssize_t ret = 0; ++ char small[1024]; ++ char *buffer; ++ ++ buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size)); ++ while (size > 0) { ++ ssize_t count = MIN(65536, size); ++ ret = nbd_read(ioc, buffer, MIN(65536, size), NULL, errp); ++ ++ if (ret < 0) { ++ goto cleanup; ++ } ++ size -= count; ++ } ++ ++ cleanup: ++ if (buffer != small) { ++ g_free(buffer); ++ } ++ return ret; ++} ++ ++ ++const char *nbd_opt_lookup(uint32_t opt) ++{ ++ switch (opt) { ++ case NBD_OPT_EXPORT_NAME: ++ return "export name"; ++ case NBD_OPT_ABORT: ++ return "abort"; ++ case NBD_OPT_LIST: ++ return "list"; ++ case NBD_OPT_STARTTLS: ++ return "starttls"; ++ case NBD_OPT_INFO: ++ return "info"; ++ case NBD_OPT_GO: ++ return "go"; ++ case NBD_OPT_STRUCTURED_REPLY: ++ return "structured reply"; ++ case NBD_OPT_LIST_META_CONTEXT: ++ return "list meta context"; ++ case NBD_OPT_SET_META_CONTEXT: ++ return "set meta context"; ++ case NBD_OPT_EXTENDED_HEADERS: ++ return "extended headers"; ++ default: ++ return ""; ++ } ++} ++ ++ ++const char *nbd_rep_lookup(uint32_t rep) ++{ ++ switch (rep) { ++ case NBD_REP_ACK: ++ return "ack"; ++ case NBD_REP_SERVER: ++ return "server"; ++ case NBD_REP_INFO: ++ return "info"; ++ case NBD_REP_META_CONTEXT: ++ return "meta context"; ++ case NBD_REP_ERR_UNSUP: ++ return "unsupported"; ++ case NBD_REP_ERR_POLICY: ++ return "denied by policy"; ++ case NBD_REP_ERR_INVALID: ++ return "invalid"; ++ case NBD_REP_ERR_PLATFORM: ++ return "platform lacks support"; ++ case NBD_REP_ERR_TLS_REQD: ++ return "TLS required"; ++ case NBD_REP_ERR_UNKNOWN: ++ return "export unknown"; ++ case NBD_REP_ERR_SHUTDOWN: ++ return "server shutting down"; ++ case NBD_REP_ERR_BLOCK_SIZE_REQD: ++ return "block size required"; ++ case NBD_REP_ERR_TOO_BIG: ++ return "option payload too big"; ++ case NBD_REP_ERR_EXT_HEADER_REQD: ++ return "extended headers required"; ++ default: ++ return ""; ++ } ++} ++ ++ ++const char *nbd_info_lookup(uint16_t info) ++{ ++ switch (info) { ++ case NBD_INFO_EXPORT: ++ return "export"; ++ case NBD_INFO_NAME: ++ return "name"; ++ case NBD_INFO_DESCRIPTION: ++ return "description"; ++ case NBD_INFO_BLOCK_SIZE: ++ return "block size"; ++ default: ++ return ""; ++ } ++} ++ ++ ++const char *nbd_cmd_lookup(uint16_t cmd) ++{ ++ switch (cmd) { ++ case NBD_CMD_READ: ++ return "read"; ++ case NBD_CMD_WRITE: ++ return "write"; ++ case NBD_CMD_DISC: ++ return "disconnect"; ++ case NBD_CMD_FLUSH: ++ return "flush"; ++ case NBD_CMD_TRIM: ++ return "trim"; ++ case NBD_CMD_CACHE: ++ return "cache"; ++ case NBD_CMD_WRITE_ZEROES: ++ return "write zeroes"; ++ case NBD_CMD_BLOCK_STATUS: ++ return "block status"; ++ default: ++ return ""; ++ } ++} ++ ++ ++const char *nbd_reply_type_lookup(uint16_t type) ++{ ++ switch (type) { ++ case NBD_REPLY_TYPE_NONE: ++ return "none"; ++ case NBD_REPLY_TYPE_OFFSET_DATA: ++ return "data"; ++ case NBD_REPLY_TYPE_OFFSET_HOLE: ++ return "hole"; ++ case NBD_REPLY_TYPE_BLOCK_STATUS: ++ return "block status (32-bit)"; ++ case NBD_REPLY_TYPE_BLOCK_STATUS_EXT: ++ return "block status (64-bit)"; ++ case NBD_REPLY_TYPE_ERROR: ++ return "generic error"; ++ case NBD_REPLY_TYPE_ERROR_OFFSET: ++ return "error at offset"; ++ default: ++ if (type & (1 << 15)) { ++ return ""; ++ } ++ return ""; ++ } ++} ++ ++ ++const char *nbd_err_lookup(int err) ++{ ++ switch (err) { ++ case NBD_SUCCESS: ++ return "success"; ++ case NBD_EPERM: ++ return "EPERM"; ++ case NBD_EIO: ++ return "EIO"; ++ case NBD_ENOMEM: ++ return "ENOMEM"; ++ case NBD_EINVAL: ++ return "EINVAL"; ++ case NBD_ENOSPC: ++ return "ENOSPC"; ++ case NBD_EOVERFLOW: ++ return "EOVERFLOW"; ++ case NBD_ENOTSUP: ++ return "ENOTSUP"; ++ case NBD_ESHUTDOWN: ++ return "ESHUTDOWN"; ++ default: ++ return ""; ++ } ++} ++ ++ ++int nbd_errno_to_system_errno(int err) ++{ ++ int ret; ++ switch (err) { ++ case NBD_SUCCESS: ++ ret = 0; ++ break; ++ case NBD_EPERM: ++ ret = EPERM; ++ break; ++ case NBD_EIO: ++ ret = EIO; ++ break; ++ case NBD_ENOMEM: ++ ret = ENOMEM; ++ break; ++ case NBD_ENOSPC: ++ ret = ENOSPC; ++ break; ++ case NBD_EOVERFLOW: ++ ret = EOVERFLOW; ++ break; ++ case NBD_ENOTSUP: ++ ret = ENOTSUP; ++ break; ++ case NBD_ESHUTDOWN: ++ ret = ESHUTDOWN; ++ break; ++ default: ++ trace_nbd_unknown_error(err); ++ /* fallthrough */ ++ case NBD_EINVAL: ++ ret = EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++ ++const char *nbd_mode_lookup(NBDMode mode) ++{ ++ switch (mode) { ++ case NBD_MODE_OLDSTYLE: ++ return "oldstyle"; ++ case NBD_MODE_EXPORT_NAME: ++ return "export name only"; ++ case NBD_MODE_SIMPLE: ++ return "simple headers"; ++ case NBD_MODE_STRUCTURED: ++ return "structured replies"; ++ case NBD_MODE_EXTENDED: ++ return "extended headers"; ++ default: ++ return ""; ++ } ++} +diff --git a/qcow2/lib/nbd/nbd-internal.h b/qcow2/lib/nbd/nbd-internal.h +new file mode 100644 +index 00000000..91895106 +--- /dev/null ++++ b/qcow2/lib/nbd/nbd-internal.h +@@ -0,0 +1,77 @@ ++/* ++ * NBD Internal Declarations ++ * ++ * Copyright Red Hat ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef NBD_INTERNAL_H ++#define NBD_INTERNAL_H ++#include "block/nbd.h" ++#include "sysemu/block-backend.h" ++#include "io/channel-tls.h" ++ ++#include "qemu/iov.h" ++ ++#ifndef _WIN32 ++#include ++#endif ++#ifdef HAVE_SYS_IOCCOM_H ++#include ++#endif ++ ++#ifdef __linux__ ++#include ++#endif ++ ++#include "qemu/bswap.h" ++ ++/* This is all part of the "official" NBD API. ++ * ++ * The most up-to-date documentation is available at: ++ * https://github.com/yoe/nbd/blob/master/doc/proto.md ++ */ ++ ++/* Size of all compact NBD_CMD_*, without payload */ ++#define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4) ++/* Size of all extended NBD_CMD_*, without payload */ ++#define NBD_EXTENDED_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 8) ++ ++/* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */ ++#define NBD_REPLY_SIZE (4 + 4 + 8) ++/* Size of reply to NBD_OPT_EXPORT_NAME */ ++#define NBD_REPLY_EXPORT_NAME_SIZE (8 + 2 + 124) ++/* Size of oldstyle negotiation */ ++#define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124) ++ ++#define NBD_INIT_MAGIC 0x4e42444d41474943LL /* ASCII "NBDMAGIC" */ ++#define NBD_OPTS_MAGIC 0x49484156454F5054LL /* ASCII "IHAVEOPT" */ ++#define NBD_CLIENT_MAGIC 0x0000420281861253LL ++#define NBD_REP_MAGIC 0x0003e889045565a9LL ++ ++#define NBD_SET_SOCK _IO(0xab, 0) ++#define NBD_SET_BLKSIZE _IO(0xab, 1) ++#define NBD_SET_SIZE _IO(0xab, 2) ++#define NBD_DO_IT _IO(0xab, 3) ++#define NBD_CLEAR_SOCK _IO(0xab, 4) ++#define NBD_CLEAR_QUE _IO(0xab, 5) ++#define NBD_PRINT_DEBUG _IO(0xab, 6) ++#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) ++#define NBD_DISCONNECT _IO(0xab, 8) ++#define NBD_SET_TIMEOUT _IO(0xab, 9) ++#define NBD_SET_FLAGS _IO(0xab, 10) ++ ++/* nbd_write ++ * Writes @size bytes to @ioc. Returns 0 on success. ++ */ ++static inline int nbd_write(QIOChannel *ioc, const void *buffer, size_t size, ++ Error **errp) ++{ ++ return qio_channel_write_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; ++} ++ ++int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); ++ ++#endif +diff --git a/qcow2/lib/qapi/qapi-clone-visitor.c b/qcow2/lib/qapi/qapi-clone-visitor.c +new file mode 100644 +index 00000000..bbf95369 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-clone-visitor.c +@@ -0,0 +1,182 @@ ++/* ++ * Copy one QAPI object to another ++ * ++ * Copyright (C) 2016 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/clone-visitor.h" ++#include "qapi/visitor-impl.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qnull.h" ++ ++struct QapiCloneVisitor { ++ Visitor visitor; ++ size_t depth; ++}; ++ ++static QapiCloneVisitor *to_qcv(Visitor *v) ++{ ++ return container_of(v, QapiCloneVisitor, visitor); ++} ++ ++static bool qapi_clone_start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ if (!obj) { ++ assert(qcv->depth); ++ /* Only possible when visiting an alternate's object ++ * branch. Nothing further to do here, since the earlier ++ * visit_start_alternate() already copied memory. */ ++ return true; ++ } ++ ++ *obj = g_memdup(*obj, size); ++ qcv->depth++; ++ return true; ++} ++ ++static void qapi_clone_end(Visitor *v, void **obj) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ if (obj) { ++ qcv->depth--; ++ } ++} ++ ++static bool qapi_clone_start_list(Visitor *v, const char *name, ++ GenericList **listp, size_t size, ++ Error **errp) ++{ ++ return qapi_clone_start_struct(v, name, (void **)listp, size, errp); ++} ++ ++static GenericList *qapi_clone_next_list(Visitor *v, GenericList *tail, ++ size_t size) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Unshare the tail of the list cloned by g_memdup() */ ++ tail->next = g_memdup(tail->next, size); ++ return tail->next; ++} ++ ++static bool qapi_clone_start_alternate(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp) ++{ ++ return qapi_clone_start_struct(v, name, (void **)obj, size, errp); ++} ++ ++static bool qapi_clone_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Value was already cloned by g_memdup() */ ++ return true; ++} ++ ++static bool qapi_clone_type_uint64(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Value was already cloned by g_memdup() */ ++ return true; ++} ++ ++static bool qapi_clone_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Value was already cloned by g_memdup() */ ++ return true; ++} ++ ++static bool qapi_clone_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* ++ * Pointer was already cloned by g_memdup; create fresh copy. ++ * Note that as long as qobject-output-visitor accepts NULL instead of ++ * "", then we must do likewise. However, we want to obey the ++ * input visitor semantics of never producing NULL when the empty ++ * string is intended. ++ */ ++ *obj = g_strdup(*obj ?: ""); ++ return true; ++} ++ ++static bool qapi_clone_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ /* Value was already cloned by g_memdup() */ ++ return true; ++} ++ ++static bool qapi_clone_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp) ++{ ++ QapiCloneVisitor *qcv = to_qcv(v); ++ ++ assert(qcv->depth); ++ *obj = qnull(); ++ return true; ++} ++ ++static void qapi_clone_free(Visitor *v) ++{ ++ g_free(v); ++} ++ ++Visitor *qapi_clone_visitor_new(void) ++{ ++ QapiCloneVisitor *v; ++ ++ v = g_malloc0(sizeof(*v)); ++ ++ v->visitor.type = VISITOR_CLONE; ++ v->visitor.start_struct = qapi_clone_start_struct; ++ v->visitor.end_struct = qapi_clone_end; ++ v->visitor.start_list = qapi_clone_start_list; ++ v->visitor.next_list = qapi_clone_next_list; ++ v->visitor.end_list = qapi_clone_end; ++ v->visitor.start_alternate = qapi_clone_start_alternate; ++ v->visitor.end_alternate = qapi_clone_end; ++ v->visitor.type_int64 = qapi_clone_type_int64; ++ v->visitor.type_uint64 = qapi_clone_type_uint64; ++ v->visitor.type_bool = qapi_clone_type_bool; ++ v->visitor.type_str = qapi_clone_type_str; ++ v->visitor.type_number = qapi_clone_type_number; ++ v->visitor.type_null = qapi_clone_type_null; ++ v->visitor.free = qapi_clone_free; ++ ++ return &v->visitor; ++} ++ ++Visitor *qapi_clone_members_visitor_new(void) ++{ ++ Visitor *v = qapi_clone_visitor_new(); ++ to_qcv(v)->depth++; ++ return v; ++} +diff --git a/qcow2/lib/qapi/qapi-dealloc-visitor.c b/qcow2/lib/qapi/qapi-dealloc-visitor.c +new file mode 100644 +index 00000000..ef283f29 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-dealloc-visitor.c +@@ -0,0 +1,143 @@ ++/* ++ * Dealloc Visitor ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Michael Roth ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/visitor-impl.h" ++ ++struct QapiDeallocVisitor ++{ ++ Visitor visitor; ++}; ++ ++static bool qapi_dealloc_start_struct(Visitor *v, const char *name, void **obj, ++ size_t unused, Error **errp) ++{ ++ return true; ++} ++ ++static void qapi_dealloc_end_struct(Visitor *v, void **obj) ++{ ++ if (obj) { ++ g_free(*obj); ++ } ++} ++ ++static void qapi_dealloc_end_alternate(Visitor *v, void **obj) ++{ ++ if (obj) { ++ g_free(*obj); ++ } ++} ++ ++static bool qapi_dealloc_start_list(Visitor *v, const char *name, ++ GenericList **list, size_t size, ++ Error **errp) ++{ ++ return true; ++} ++ ++static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList *tail, ++ size_t size) ++{ ++ GenericList *next = tail->next; ++ g_free(tail); ++ return next; ++} ++ ++static void qapi_dealloc_end_list(Visitor *v, void **obj) ++{ ++} ++ ++static bool qapi_dealloc_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ if (obj) { ++ g_free(*obj); ++ } ++ return true; ++} ++ ++static bool qapi_dealloc_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ return true; ++} ++ ++static bool qapi_dealloc_type_uint64(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ return true; ++} ++ ++static bool qapi_dealloc_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ return true; ++} ++ ++static bool qapi_dealloc_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ return true; ++} ++ ++static bool qapi_dealloc_type_anything(Visitor *v, const char *name, ++ QObject **obj, Error **errp) ++{ ++ if (obj) { ++ qobject_unref(*obj); ++ } ++ return true; ++} ++ ++static bool qapi_dealloc_type_null(Visitor *v, const char *name, ++ QNull **obj, Error **errp) ++{ ++ if (obj) { ++ qobject_unref(*obj); ++ } ++ return true; ++} ++ ++static void qapi_dealloc_free(Visitor *v) ++{ ++ g_free(container_of(v, QapiDeallocVisitor, visitor)); ++} ++ ++Visitor *qapi_dealloc_visitor_new(void) ++{ ++ QapiDeallocVisitor *v; ++ ++ v = g_malloc0(sizeof(*v)); ++ ++ v->visitor.type = VISITOR_DEALLOC; ++ v->visitor.start_struct = qapi_dealloc_start_struct; ++ v->visitor.end_struct = qapi_dealloc_end_struct; ++ v->visitor.end_alternate = qapi_dealloc_end_alternate; ++ v->visitor.start_list = qapi_dealloc_start_list; ++ v->visitor.next_list = qapi_dealloc_next_list; ++ v->visitor.end_list = qapi_dealloc_end_list; ++ v->visitor.type_int64 = qapi_dealloc_type_int64; ++ v->visitor.type_uint64 = qapi_dealloc_type_uint64; ++ v->visitor.type_bool = qapi_dealloc_type_bool; ++ v->visitor.type_str = qapi_dealloc_type_str; ++ v->visitor.type_number = qapi_dealloc_type_number; ++ v->visitor.type_any = qapi_dealloc_type_anything; ++ v->visitor.type_null = qapi_dealloc_type_null; ++ v->visitor.free = qapi_dealloc_free; ++ ++ return &v->visitor; ++} +diff --git a/qcow2/lib/qapi/qapi-emit-events.h b/qcow2/lib/qapi/qapi-emit-events.h +new file mode 100644 +index 00000000..725da1d4 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-emit-events.h +@@ -0,0 +1,83 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * QAPI Events emission ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QAPI_EMIT_EVENTS_H ++#define QAPI_EMIT_EVENTS_H ++ ++#include "qapi/util.h" ++ ++typedef enum QAPIEvent { ++ QAPI_EVENT_SHUTDOWN, ++ QAPI_EVENT_POWERDOWN, ++ QAPI_EVENT_RESET, ++ QAPI_EVENT_STOP, ++ QAPI_EVENT_RESUME, ++ QAPI_EVENT_SUSPEND, ++ QAPI_EVENT_SUSPEND_DISK, ++ QAPI_EVENT_WAKEUP, ++ QAPI_EVENT_WATCHDOG, ++ QAPI_EVENT_GUEST_PANICKED, ++ QAPI_EVENT_GUEST_CRASHLOADED, ++ QAPI_EVENT_GUEST_PVSHUTDOWN, ++ QAPI_EVENT_MEMORY_FAILURE, ++ QAPI_EVENT_JOB_STATUS_CHANGE, ++ QAPI_EVENT_DEVICE_TRAY_MOVED, ++ QAPI_EVENT_PR_MANAGER_STATUS_CHANGED, ++ QAPI_EVENT_BLOCK_IMAGE_CORRUPTED, ++ QAPI_EVENT_BLOCK_IO_ERROR, ++ QAPI_EVENT_BLOCK_JOB_COMPLETED, ++ QAPI_EVENT_BLOCK_JOB_CANCELLED, ++ QAPI_EVENT_BLOCK_JOB_ERROR, ++ QAPI_EVENT_BLOCK_JOB_READY, ++ QAPI_EVENT_BLOCK_JOB_PENDING, ++ QAPI_EVENT_BLOCK_WRITE_THRESHOLD, ++ QAPI_EVENT_QUORUM_FAILURE, ++ QAPI_EVENT_QUORUM_REPORT_BAD, ++ QAPI_EVENT_BLOCK_EXPORT_DELETED, ++ QAPI_EVENT_VSERPORT_CHANGE, ++ QAPI_EVENT_DUMP_COMPLETED, ++ QAPI_EVENT_NIC_RX_FILTER_CHANGED, ++ QAPI_EVENT_FAILOVER_NEGOTIATED, ++ QAPI_EVENT_NETDEV_STREAM_CONNECTED, ++ QAPI_EVENT_NETDEV_STREAM_DISCONNECTED, ++ QAPI_EVENT_SPICE_CONNECTED, ++ QAPI_EVENT_SPICE_INITIALIZED, ++ QAPI_EVENT_SPICE_DISCONNECTED, ++ QAPI_EVENT_SPICE_MIGRATE_COMPLETED, ++ QAPI_EVENT_VNC_CONNECTED, ++ QAPI_EVENT_VNC_INITIALIZED, ++ QAPI_EVENT_VNC_DISCONNECTED, ++ QAPI_EVENT_MIGRATION, ++ QAPI_EVENT_MIGRATION_PASS, ++ QAPI_EVENT_COLO_EXIT, ++ QAPI_EVENT_UNPLUG_PRIMARY, ++ QAPI_EVENT_DEVICE_DELETED, ++ QAPI_EVENT_DEVICE_UNPLUG_GUEST_ERROR, ++ QAPI_EVENT_BALLOON_CHANGE, ++ QAPI_EVENT_HV_BALLOON_STATUS_REPORT, ++ QAPI_EVENT_MEMORY_DEVICE_SIZE_CHANGE, ++ QAPI_EVENT_CPU_POLARIZATION_CHANGE, ++ QAPI_EVENT_RTC_CHANGE, ++ QAPI_EVENT_VFU_CLIENT_HANGUP, ++ QAPI_EVENT_ACPI_DEVICE_OST, ++ QAPI_EVENT_VFIO_MIGRATION, ++ QAPI_EVENT__MAX, ++} QAPIEvent; ++ ++#define QAPIEvent_str(val) \ ++ qapi_enum_lookup(&QAPIEvent_lookup, (val)) ++ ++extern const QEnumLookup QAPIEvent_lookup; ++ ++void qapi_event_emit(QAPIEvent event, QDict *qdict); ++ ++#endif /* QAPI_EMIT_EVENTS_H */ +diff --git a/qcow2/lib/qapi/qapi-events-block-core.c b/qcow2/lib/qapi/qapi-events-block-core.c +new file mode 100644 +index 00000000..a08a710c +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-events-block-core.c +@@ -0,0 +1,323 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi-emit-events.h" ++#include "qapi-events-block-core.h" ++#include "qapi-visit-block-core.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp-event.h" ++ ++void qapi_event_send_block_image_corrupted(const char *device, const char *node_name, const char *msg, bool has_offset, int64_t offset, bool has_size, int64_t size, bool fatal) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_IMAGE_CORRUPTED_arg param = { ++ (char *)device, (char *)node_name, (char *)msg, has_offset, offset, has_size, size, fatal ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_IMAGE_CORRUPTED"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_IMAGE_CORRUPTED", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_IMAGE_CORRUPTED_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_IMAGE_CORRUPTED, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_io_error(const char *device, const char *node_name, IoOperationType operation, BlockErrorAction action, bool has_nospace, bool nospace, const char *reason) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_IO_ERROR_arg param = { ++ (char *)device, (char *)node_name, operation, action, has_nospace, nospace, (char *)reason ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_IO_ERROR"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_IO_ERROR", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_IO_ERROR_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_IO_ERROR, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_completed(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed, const char *error) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_COMPLETED_arg param = { ++ type, (char *)device, len, offset, speed, (char *)error ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_COMPLETED"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_COMPLETED", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_COMPLETED_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_COMPLETED, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_cancelled(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_CANCELLED_arg param = { ++ type, (char *)device, len, offset, speed ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_CANCELLED"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_CANCELLED", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_CANCELLED_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_CANCELLED, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_error(const char *device, IoOperationType operation, BlockErrorAction action) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_ERROR_arg param = { ++ (char *)device, operation, action ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_ERROR"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_ERROR", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_ERROR, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_ready(JobType type, const char *device, int64_t len, int64_t offset, int64_t speed) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_READY_arg param = { ++ type, (char *)device, len, offset, speed ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_READY"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_READY", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_READY_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_READY, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_job_pending(JobType type, const char *id) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_JOB_PENDING_arg param = { ++ type, (char *)id ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_JOB_PENDING"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_JOB_PENDING", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_JOB_PENDING_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_JOB_PENDING, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_block_write_threshold(const char *node_name, uint64_t amount_exceeded, uint64_t write_threshold) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_BLOCK_WRITE_THRESHOLD_arg param = { ++ (char *)node_name, amount_exceeded, write_threshold ++ }; ++ ++ qmp = qmp_event_build_dict("BLOCK_WRITE_THRESHOLD"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "BLOCK_WRITE_THRESHOLD", NULL, 0, &error_abort); ++ visit_type_q_obj_BLOCK_WRITE_THRESHOLD_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_BLOCK_WRITE_THRESHOLD, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num, int64_t sectors_count) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_QUORUM_FAILURE_arg param = { ++ (char *)reference, sector_num, sectors_count ++ }; ++ ++ qmp = qmp_event_build_dict("QUORUM_FAILURE"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "QUORUM_FAILURE", NULL, 0, &error_abort); ++ visit_type_q_obj_QUORUM_FAILURE_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_QUORUM_FAILURE, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++void qapi_event_send_quorum_report_bad(QuorumOpType type, const char *error, const char *node_name, int64_t sector_num, int64_t sectors_count) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_QUORUM_REPORT_BAD_arg param = { ++ type, (char *)error, (char *)node_name, sector_num, sectors_count ++ }; ++ ++ qmp = qmp_event_build_dict("QUORUM_REPORT_BAD"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "QUORUM_REPORT_BAD", NULL, 0, &error_abort); ++ visit_type_q_obj_QUORUM_REPORT_BAD_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_QUORUM_REPORT_BAD, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_events_block_core_c; +diff --git a/qcow2/lib/qapi/qapi-events-job.c b/qcow2/lib/qapi/qapi-events-job.c +new file mode 100644 +index 00000000..cb5c3b45 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-events-job.c +@@ -0,0 +1,53 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI/QMP events ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * Copyright (c) 2015-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi-emit-events.h" ++#include "qapi-events-job.h" ++#include "qapi-visit-job.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp-event.h" ++ ++void qapi_event_send_job_status_change(const char *id, JobStatus status) ++{ ++ QDict *qmp; ++ QObject *obj; ++ Visitor *v; ++ q_obj_JOB_STATUS_CHANGE_arg param = { ++ (char *)id, status ++ }; ++ ++ qmp = qmp_event_build_dict("JOB_STATUS_CHANGE"); ++ ++ v = qobject_output_visitor_new_qmp(&obj); ++ ++ visit_start_struct(v, "JOB_STATUS_CHANGE", NULL, 0, &error_abort); ++ visit_type_q_obj_JOB_STATUS_CHANGE_arg_members(v, ¶m, &error_abort); ++ visit_check_struct(v, &error_abort); ++ visit_end_struct(v, NULL); ++ ++ visit_complete(v, &obj); ++ if (qdict_size(qobject_to(QDict, obj))) { ++ qdict_put_obj(qmp, "data", obj); ++ } else { ++ qobject_unref(obj); ++ } ++ qapi_event_emit(QAPI_EVENT_JOB_STATUS_CHANGE, qmp); ++ ++ visit_free(v); ++ qobject_unref(qmp); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_events_job_c; +diff --git a/qcow2/lib/qapi/qapi-types-block-core.c b/qcow2/lib/qapi/qapi-types-block-core.c +new file mode 100644 +index 00000000..f4ee0d21 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-block-core.c +@@ -0,0 +1,2484 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-block-core.h" ++#include "qapi-visit-block-core.h" ++ ++void qapi_free_SnapshotInfo(SnapshotInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SnapshotInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificQCow2EncryptionBase(ImageInfoSpecificQCow2EncryptionBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificQCow2EncryptionBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificQCow2Encryption(ImageInfoSpecificQCow2Encryption *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificQCow2Encryption(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_Qcow2BitmapInfoList(Qcow2BitmapInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2BitmapInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificQCow2(ImageInfoSpecificQCow2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificQCow2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_VmdkExtentInfoList(VmdkExtentInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_VmdkExtentInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificVmdk(ImageInfoSpecificVmdk *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificVmdk(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_VmdkExtentInfo(VmdkExtentInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_VmdkExtentInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificRbd(ImageInfoSpecificRbd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificRbd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificFile(ImageInfoSpecificFile *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificFile(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup ImageInfoSpecificKind_lookup = { ++ .array = (const char *const[]) { ++ [IMAGE_INFO_SPECIFIC_KIND_QCOW2] = "qcow2", ++ [IMAGE_INFO_SPECIFIC_KIND_VMDK] = "vmdk", ++ [IMAGE_INFO_SPECIFIC_KIND_LUKS] = "luks", ++ [IMAGE_INFO_SPECIFIC_KIND_RBD] = "rbd", ++ [IMAGE_INFO_SPECIFIC_KIND_FILE] = "file", ++ }, ++ .size = IMAGE_INFO_SPECIFIC_KIND__MAX ++}; ++ ++void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificQCow2Wrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificVmdkWrapper(ImageInfoSpecificVmdkWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificVmdkWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificLUKSWrapper(ImageInfoSpecificLUKSWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificLUKSWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificRbdWrapper(ImageInfoSpecificRbdWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificRbdWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecificFileWrapper(ImageInfoSpecificFileWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecificFileWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfoSpecific(ImageInfoSpecific *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfoSpecific(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SnapshotInfoList(SnapshotInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SnapshotInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockNodeInfo(BlockNodeInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockNodeInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageInfo(ImageInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockChildInfo(BlockChildInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockChildInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockChildInfoList(BlockChildInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockChildInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockGraphInfo(BlockGraphInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockGraphInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ImageCheck(ImageCheck *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ImageCheck(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_MapEntry(MapEntry *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_MapEntry(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCacheInfo(BlockdevCacheInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCacheInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyInfoList(BlockDirtyInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceInfo(BlockDeviceInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockDeviceIoStatus_lookup = { ++ .array = (const char *const[]) { ++ [BLOCK_DEVICE_IO_STATUS_OK] = "ok", ++ [BLOCK_DEVICE_IO_STATUS_FAILED] = "failed", ++ [BLOCK_DEVICE_IO_STATUS_NOSPACE] = "nospace", ++ }, ++ .size = BLOCK_DEVICE_IO_STATUS__MAX ++}; ++ ++void qapi_free_BlockDirtyInfo(BlockDirtyInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup Qcow2BitmapInfoFlags_lookup = { ++ .array = (const char *const[]) { ++ [QCOW2_BITMAP_INFO_FLAGS_IN_USE] = "in-use", ++ [QCOW2_BITMAP_INFO_FLAGS_AUTO] = "auto", ++ }, ++ .size = QCOW2_BITMAP_INFO_FLAGS__MAX ++}; ++ ++void qapi_free_Qcow2BitmapInfoFlagsList(Qcow2BitmapInfoFlagsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2BitmapInfoFlagsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_Qcow2BitmapInfo(Qcow2BitmapInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2BitmapInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockLatencyHistogramInfo(BlockLatencyHistogramInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockLatencyHistogramInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockInfo(BlockInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockMeasureInfo(BlockMeasureInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockMeasureInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockInfoList(BlockInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceTimedStats(BlockDeviceTimedStats *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceTimedStats(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceTimedStatsList(BlockDeviceTimedStatsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceTimedStatsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceStats(BlockDeviceStats *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceStats(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStatsSpecificFile(BlockStatsSpecificFile *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStatsSpecificFile(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStatsSpecificNvme(BlockStatsSpecificNvme *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStatsSpecificNvme(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStatsSpecific(BlockStatsSpecific *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStatsSpecific(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStats(BlockStats *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStats(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockStatsList(BlockStatsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockStatsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevOnError_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_ON_ERROR_REPORT] = "report", ++ [BLOCKDEV_ON_ERROR_IGNORE] = "ignore", ++ [BLOCKDEV_ON_ERROR_ENOSPC] = "enospc", ++ [BLOCKDEV_ON_ERROR_STOP] = "stop", ++ [BLOCKDEV_ON_ERROR_AUTO] = "auto", ++ }, ++ .size = BLOCKDEV_ON_ERROR__MAX ++}; ++ ++const QEnumLookup MirrorSyncMode_lookup = { ++ .array = (const char *const[]) { ++ [MIRROR_SYNC_MODE_TOP] = "top", ++ [MIRROR_SYNC_MODE_FULL] = "full", ++ [MIRROR_SYNC_MODE_NONE] = "none", ++ [MIRROR_SYNC_MODE_INCREMENTAL] = "incremental", ++ [MIRROR_SYNC_MODE_BITMAP] = "bitmap", ++ }, ++ .size = MIRROR_SYNC_MODE__MAX ++}; ++ ++const QEnumLookup BitmapSyncMode_lookup = { ++ .array = (const char *const[]) { ++ [BITMAP_SYNC_MODE_ON_SUCCESS] = "on-success", ++ [BITMAP_SYNC_MODE_NEVER] = "never", ++ [BITMAP_SYNC_MODE_ALWAYS] = "always", ++ }, ++ .size = BITMAP_SYNC_MODE__MAX ++}; ++ ++const QEnumLookup MirrorCopyMode_lookup = { ++ .array = (const char *const[]) { ++ [MIRROR_COPY_MODE_BACKGROUND] = "background", ++ [MIRROR_COPY_MODE_WRITE_BLOCKING] = "write-blocking", ++ }, ++ .size = MIRROR_COPY_MODE__MAX ++}; ++ ++void qapi_free_BlockJobInfoMirror(BlockJobInfoMirror *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobInfoMirror(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockJobInfo(BlockJobInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockJobInfoList(BlockJobInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup NewImageMode_lookup = { ++ .array = (const char *const[]) { ++ [NEW_IMAGE_MODE_EXISTING] = "existing", ++ [NEW_IMAGE_MODE_ABSOLUTE_PATHS] = "absolute-paths", ++ }, ++ .size = NEW_IMAGE_MODE__MAX ++}; ++ ++void qapi_free_BlockdevSnapshotSync(BlockdevSnapshotSync *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevSnapshotSync(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevSnapshot(BlockdevSnapshot *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevSnapshot(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BackupPerf(BackupPerf *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BackupPerf(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BackupCommon(BackupCommon *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BackupCommon(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_DriveBackup(DriveBackup *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_DriveBackup(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevBackup(BlockdevBackup *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevBackup(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDeviceInfoList(BlockDeviceInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDeviceInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup XDbgBlockGraphNodeType_lookup = { ++ .array = (const char *const[]) { ++ [X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND] = "block-backend", ++ [X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB] = "block-job", ++ [X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER] = "block-driver", ++ }, ++ .size = X_DBG_BLOCK_GRAPH_NODE_TYPE__MAX ++}; ++ ++void qapi_free_XDbgBlockGraphNode(XDbgBlockGraphNode *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraphNode(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockPermission_lookup = { ++ .array = (const char *const[]) { ++ [BLOCK_PERMISSION_CONSISTENT_READ] = "consistent-read", ++ [BLOCK_PERMISSION_WRITE] = "write", ++ [BLOCK_PERMISSION_WRITE_UNCHANGED] = "write-unchanged", ++ [BLOCK_PERMISSION_RESIZE] = "resize", ++ }, ++ .size = BLOCK_PERMISSION__MAX ++}; ++ ++void qapi_free_BlockPermissionList(BlockPermissionList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockPermissionList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_XDbgBlockGraphEdge(XDbgBlockGraphEdge *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraphEdge(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_XDbgBlockGraphNodeList(XDbgBlockGraphNodeList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraphNodeList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_XDbgBlockGraphEdgeList(XDbgBlockGraphEdgeList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraphEdgeList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_XDbgBlockGraph(XDbgBlockGraph *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_XDbgBlockGraph(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_DriveMirror(DriveMirror *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_DriveMirror(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmap(BlockDirtyBitmap *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmap(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapAdd(BlockDirtyBitmapAdd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapAdd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapOrStr(BlockDirtyBitmapOrStr *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapOrStr(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapOrStrList(BlockDirtyBitmapOrStrList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapOrStrList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapMerge(BlockDirtyBitmapMerge *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapMerge(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockDirtyBitmapSha256(BlockDirtyBitmapSha256 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockDirtyBitmapSha256(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockIOThrottle(BlockIOThrottle *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockIOThrottle(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ThrottleLimits(ThrottleLimits *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ThrottleLimits(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_ThrottleGroupProperties(ThrottleGroupProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_ThrottleGroupProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockJobChangeOptionsMirror(BlockJobChangeOptionsMirror *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobChangeOptionsMirror(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockJobChangeOptions(BlockJobChangeOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockJobChangeOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevDiscardOptions_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_DISCARD_OPTIONS_IGNORE] = "ignore", ++ [BLOCKDEV_DISCARD_OPTIONS_UNMAP] = "unmap", ++ }, ++ .size = BLOCKDEV_DISCARD_OPTIONS__MAX ++}; ++ ++const QEnumLookup BlockdevDetectZeroesOptions_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF] = "off", ++ [BLOCKDEV_DETECT_ZEROES_OPTIONS_ON] = "on", ++ [BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP] = "unmap", ++ }, ++ .size = BLOCKDEV_DETECT_ZEROES_OPTIONS__MAX ++}; ++ ++const QEnumLookup BlockdevAioOptions_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_AIO_OPTIONS_THREADS] = "threads", ++ [BLOCKDEV_AIO_OPTIONS_NATIVE] = "native", ++#if defined(CONFIG_LINUX_IO_URING) ++ [BLOCKDEV_AIO_OPTIONS_IO_URING] = "io_uring", ++#endif /* defined(CONFIG_LINUX_IO_URING) */ ++ }, ++ .size = BLOCKDEV_AIO_OPTIONS__MAX ++}; ++ ++void qapi_free_BlockdevCacheOptions(BlockdevCacheOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCacheOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevDriver_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_DRIVER_BLKDEBUG] = "blkdebug", ++ [BLOCKDEV_DRIVER_BLKLOGWRITES] = "blklogwrites", ++ [BLOCKDEV_DRIVER_BLKREPLAY] = "blkreplay", ++ [BLOCKDEV_DRIVER_BLKVERIFY] = "blkverify", ++ [BLOCKDEV_DRIVER_BOCHS] = "bochs", ++ [BLOCKDEV_DRIVER_CLOOP] = "cloop", ++ [BLOCKDEV_DRIVER_COMPRESS] = "compress", ++ [BLOCKDEV_DRIVER_COPY_BEFORE_WRITE] = "copy-before-write", ++ [BLOCKDEV_DRIVER_COPY_ON_READ] = "copy-on-read", ++ [BLOCKDEV_DRIVER_DMG] = "dmg", ++ [BLOCKDEV_DRIVER_FILE] = "file", ++ [BLOCKDEV_DRIVER_SNAPSHOT_ACCESS] = "snapshot-access", ++ [BLOCKDEV_DRIVER_FTP] = "ftp", ++ [BLOCKDEV_DRIVER_FTPS] = "ftps", ++ [BLOCKDEV_DRIVER_GLUSTER] = "gluster", ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ [BLOCKDEV_DRIVER_HOST_CDROM] = "host_cdrom", ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ [BLOCKDEV_DRIVER_HOST_DEVICE] = "host_device", ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ [BLOCKDEV_DRIVER_HTTP] = "http", ++ [BLOCKDEV_DRIVER_HTTPS] = "https", ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_IO_URING] = "io_uring", ++#endif /* defined(CONFIG_BLKIO) */ ++ [BLOCKDEV_DRIVER_ISCSI] = "iscsi", ++ [BLOCKDEV_DRIVER_LUKS] = "luks", ++ [BLOCKDEV_DRIVER_NBD] = "nbd", ++ [BLOCKDEV_DRIVER_NFS] = "nfs", ++ [BLOCKDEV_DRIVER_NULL_AIO] = "null-aio", ++ [BLOCKDEV_DRIVER_NULL_CO] = "null-co", ++ [BLOCKDEV_DRIVER_NVME] = "nvme", ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_NVME_IO_URING] = "nvme-io_uring", ++#endif /* defined(CONFIG_BLKIO) */ ++ [BLOCKDEV_DRIVER_PARALLELS] = "parallels", ++ [BLOCKDEV_DRIVER_PREALLOCATE] = "preallocate", ++ [BLOCKDEV_DRIVER_QCOW] = "qcow", ++ [BLOCKDEV_DRIVER_QCOW2] = "qcow2", ++ [BLOCKDEV_DRIVER_QED] = "qed", ++ [BLOCKDEV_DRIVER_QUORUM] = "quorum", ++ [BLOCKDEV_DRIVER_RAW] = "raw", ++ [BLOCKDEV_DRIVER_RBD] = "rbd", ++#if defined(CONFIG_REPLICATION) ++ [BLOCKDEV_DRIVER_REPLICATION] = "replication", ++#endif /* defined(CONFIG_REPLICATION) */ ++ [BLOCKDEV_DRIVER_SSH] = "ssh", ++ [BLOCKDEV_DRIVER_THROTTLE] = "throttle", ++ [BLOCKDEV_DRIVER_VDI] = "vdi", ++ [BLOCKDEV_DRIVER_VHDX] = "vhdx", ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI] = "virtio-blk-vfio-pci", ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER] = "virtio-blk-vhost-user", ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ [BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA] = "virtio-blk-vhost-vdpa", ++#endif /* defined(CONFIG_BLKIO) */ ++ [BLOCKDEV_DRIVER_VMDK] = "vmdk", ++ [BLOCKDEV_DRIVER_VPC] = "vpc", ++ [BLOCKDEV_DRIVER_VVFAT] = "vvfat", ++ }, ++ .size = BLOCKDEV_DRIVER__MAX ++}; ++ ++void qapi_free_BlockdevOptionsFile(BlockdevOptionsFile *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsFile(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsNull(BlockdevOptionsNull *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNull(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsNVMe(BlockdevOptionsNVMe *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNVMe(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsVVFAT(BlockdevOptionsVVFAT *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsVVFAT(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsGenericFormat(BlockdevOptionsGenericFormat *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsGenericFormat(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsLUKS(BlockdevOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsGenericCOWFormat(BlockdevOptionsGenericCOWFormat *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsGenericCOWFormat(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup Qcow2OverlapCheckMode_lookup = { ++ .array = (const char *const[]) { ++ [QCOW2_OVERLAP_CHECK_MODE_NONE] = "none", ++ [QCOW2_OVERLAP_CHECK_MODE_CONSTANT] = "constant", ++ [QCOW2_OVERLAP_CHECK_MODE_CACHED] = "cached", ++ [QCOW2_OVERLAP_CHECK_MODE_ALL] = "all", ++ }, ++ .size = QCOW2_OVERLAP_CHECK_MODE__MAX ++}; ++ ++void qapi_free_Qcow2OverlapCheckFlags(Qcow2OverlapCheckFlags *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2OverlapCheckFlags(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_Qcow2OverlapChecks(Qcow2OverlapChecks *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_Qcow2OverlapChecks(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevQcowEncryptionFormat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_QCOW_ENCRYPTION_FORMAT_AES] = "aes", ++ }, ++ .size = BLOCKDEV_QCOW_ENCRYPTION_FORMAT__MAX ++}; ++ ++void qapi_free_BlockdevQcowEncryption(BlockdevQcowEncryption *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevQcowEncryption(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsQcow(BlockdevOptionsQcow *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsQcow(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevQcow2EncryptionFormat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES] = "aes", ++ [BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS] = "luks", ++ }, ++ .size = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT__MAX ++}; ++ ++void qapi_free_BlockdevQcow2Encryption(BlockdevQcow2Encryption *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevQcow2Encryption(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsPreallocate(BlockdevOptionsPreallocate *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsPreallocate(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsQcow2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup SshHostKeyCheckMode_lookup = { ++ .array = (const char *const[]) { ++ [SSH_HOST_KEY_CHECK_MODE_NONE] = "none", ++ [SSH_HOST_KEY_CHECK_MODE_HASH] = "hash", ++ [SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS] = "known_hosts", ++ }, ++ .size = SSH_HOST_KEY_CHECK_MODE__MAX ++}; ++ ++const QEnumLookup SshHostKeyCheckHashType_lookup = { ++ .array = (const char *const[]) { ++ [SSH_HOST_KEY_CHECK_HASH_TYPE_MD5] = "md5", ++ [SSH_HOST_KEY_CHECK_HASH_TYPE_SHA1] = "sha1", ++ [SSH_HOST_KEY_CHECK_HASH_TYPE_SHA256] = "sha256", ++ }, ++ .size = SSH_HOST_KEY_CHECK_HASH_TYPE__MAX ++}; ++ ++void qapi_free_SshHostKeyHash(SshHostKeyHash *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SshHostKeyHash(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SshHostKeyCheck(SshHostKeyCheck *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SshHostKeyCheck(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsSsh(BlockdevOptionsSsh *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsSsh(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlkdebugEvent_lookup = { ++ .array = (const char *const[]) { ++ [BLKDBG_L1_UPDATE] = "l1_update", ++ [BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow_alloc_table", ++ [BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow_write_table", ++ [BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow_activate_table", ++ [BLKDBG_L2_LOAD] = "l2_load", ++ [BLKDBG_L2_UPDATE] = "l2_update", ++ [BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed", ++ [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc_cow_read", ++ [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc_write", ++ [BLKDBG_READ_AIO] = "read_aio", ++ [BLKDBG_READ_BACKING_AIO] = "read_backing_aio", ++ [BLKDBG_READ_COMPRESSED] = "read_compressed", ++ [BLKDBG_WRITE_AIO] = "write_aio", ++ [BLKDBG_WRITE_COMPRESSED] = "write_compressed", ++ [BLKDBG_VMSTATE_LOAD] = "vmstate_load", ++ [BLKDBG_VMSTATE_SAVE] = "vmstate_save", ++ [BLKDBG_COW_READ] = "cow_read", ++ [BLKDBG_COW_WRITE] = "cow_write", ++ [BLKDBG_REFTABLE_LOAD] = "reftable_load", ++ [BLKDBG_REFTABLE_GROW] = "reftable_grow", ++ [BLKDBG_REFTABLE_UPDATE] = "reftable_update", ++ [BLKDBG_REFBLOCK_LOAD] = "refblock_load", ++ [BLKDBG_REFBLOCK_UPDATE] = "refblock_update", ++ [BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part", ++ [BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc", ++ [BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc_hookup", ++ [BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc_write", ++ [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc_write_blocks", ++ [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc_write_table", ++ [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc_switch_table", ++ [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", ++ [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", ++ [BLKDBG_CLUSTER_FREE] = "cluster_free", ++ [BLKDBG_FLUSH_TO_OS] = "flush_to_os", ++ [BLKDBG_FLUSH_TO_DISK] = "flush_to_disk", ++ [BLKDBG_PWRITEV_RMW_HEAD] = "pwritev_rmw_head", ++ [BLKDBG_PWRITEV_RMW_AFTER_HEAD] = "pwritev_rmw_after_head", ++ [BLKDBG_PWRITEV_RMW_TAIL] = "pwritev_rmw_tail", ++ [BLKDBG_PWRITEV_RMW_AFTER_TAIL] = "pwritev_rmw_after_tail", ++ [BLKDBG_PWRITEV] = "pwritev", ++ [BLKDBG_PWRITEV_ZERO] = "pwritev_zero", ++ [BLKDBG_PWRITEV_DONE] = "pwritev_done", ++ [BLKDBG_EMPTY_IMAGE_PREPARE] = "empty_image_prepare", ++ [BLKDBG_L1_SHRINK_WRITE_TABLE] = "l1_shrink_write_table", ++ [BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS] = "l1_shrink_free_l2_clusters", ++ [BLKDBG_COR_WRITE] = "cor_write", ++ [BLKDBG_CLUSTER_ALLOC_SPACE] = "cluster_alloc_space", ++ [BLKDBG_NONE] = "none", ++ }, ++ .size = BLKDBG__MAX ++}; ++ ++const QEnumLookup BlkdebugIOType_lookup = { ++ .array = (const char *const[]) { ++ [BLKDEBUG_IO_TYPE_READ] = "read", ++ [BLKDEBUG_IO_TYPE_WRITE] = "write", ++ [BLKDEBUG_IO_TYPE_WRITE_ZEROES] = "write-zeroes", ++ [BLKDEBUG_IO_TYPE_DISCARD] = "discard", ++ [BLKDEBUG_IO_TYPE_FLUSH] = "flush", ++ [BLKDEBUG_IO_TYPE_BLOCK_STATUS] = "block-status", ++ }, ++ .size = BLKDEBUG_IO_TYPE__MAX ++}; ++ ++void qapi_free_BlkdebugInjectErrorOptions(BlkdebugInjectErrorOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlkdebugInjectErrorOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlkdebugSetStateOptions(BlkdebugSetStateOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlkdebugSetStateOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlkdebugInjectErrorOptionsList(BlkdebugInjectErrorOptionsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlkdebugInjectErrorOptionsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlkdebugSetStateOptionsList(BlkdebugSetStateOptionsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlkdebugSetStateOptionsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsBlkdebug(BlockdevOptionsBlkdebug *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsBlkdebug(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsBlklogwrites(BlockdevOptionsBlklogwrites *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsBlklogwrites(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsBlkverify(BlockdevOptionsBlkverify *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsBlkverify(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsBlkreplay(BlockdevOptionsBlkreplay *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsBlkreplay(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup QuorumReadPattern_lookup = { ++ .array = (const char *const[]) { ++ [QUORUM_READ_PATTERN_QUORUM] = "quorum", ++ [QUORUM_READ_PATTERN_FIFO] = "fifo", ++ }, ++ .size = QUORUM_READ_PATTERN__MAX ++}; ++ ++void qapi_free_BlockdevRefList(BlockdevRefList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevRefList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsQuorum(BlockdevOptionsQuorum *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsQuorum(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsGluster(BlockdevOptionsGluster *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsGluster(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsIoUring(BlockdevOptionsIoUring *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsIoUring(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsNvmeIoUring(BlockdevOptionsNvmeIoUring *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNvmeIoUring(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVfioPci(BlockdevOptionsVirtioBlkVfioPci *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsVirtioBlkVfioPci(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVhostUser(BlockdevOptionsVirtioBlkVhostUser *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsVirtioBlkVhostUser(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++void qapi_free_BlockdevOptionsVirtioBlkVhostVdpa(BlockdevOptionsVirtioBlkVhostVdpa *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsVirtioBlkVhostVdpa(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++const QEnumLookup IscsiTransport_lookup = { ++ .array = (const char *const[]) { ++ [ISCSI_TRANSPORT_TCP] = "tcp", ++ [ISCSI_TRANSPORT_ISER] = "iser", ++ }, ++ .size = ISCSI_TRANSPORT__MAX ++}; ++ ++const QEnumLookup IscsiHeaderDigest_lookup = { ++ .array = (const char *const[]) { ++ [QAPI_ISCSI_HEADER_DIGEST_CRC32C] = "crc32c", ++ [QAPI_ISCSI_HEADER_DIGEST_NONE] = "none", ++ [QAPI_ISCSI_HEADER_DIGEST_CRC32C_NONE] = "crc32c-none", ++ [QAPI_ISCSI_HEADER_DIGEST_NONE_CRC32C] = "none-crc32c", ++ }, ++ .size = QAPI_ISCSI_HEADER_DIGEST__MAX ++}; ++ ++void qapi_free_BlockdevOptionsIscsi(BlockdevOptionsIscsi *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsIscsi(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup RbdAuthMode_lookup = { ++ .array = (const char *const[]) { ++ [RBD_AUTH_MODE_CEPHX] = "cephx", ++ [RBD_AUTH_MODE_NONE] = "none", ++ }, ++ .size = RBD_AUTH_MODE__MAX ++}; ++ ++const QEnumLookup RbdImageEncryptionFormat_lookup = { ++ .array = (const char *const[]) { ++ [RBD_IMAGE_ENCRYPTION_FORMAT_LUKS] = "luks", ++ [RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2] = "luks2", ++ [RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY] = "luks-any", ++ }, ++ .size = RBD_IMAGE_ENCRYPTION_FORMAT__MAX ++}; ++ ++void qapi_free_RbdEncryptionOptionsLUKSBase(RbdEncryptionOptionsLUKSBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptionsLUKSBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKSBase(RbdEncryptionCreateOptionsLUKSBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionCreateOptionsLUKSBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKS(RbdEncryptionOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKS2(RbdEncryptionOptionsLUKS2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptionsLUKS2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionOptionsLUKSAny(RbdEncryptionOptionsLUKSAny *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptionsLUKSAny(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKS(RbdEncryptionCreateOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionCreateOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionCreateOptionsLUKS2(RbdEncryptionCreateOptionsLUKS2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionCreateOptionsLUKS2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionOptions(RbdEncryptionOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdEncryptionCreateOptions(RbdEncryptionCreateOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdEncryptionCreateOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_RbdAuthModeList(RbdAuthModeList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_RbdAuthModeList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsRbd(BlockdevOptionsRbd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsRbd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++#if defined(CONFIG_REPLICATION) ++const QEnumLookup ReplicationMode_lookup = { ++ .array = (const char *const[]) { ++ [REPLICATION_MODE_PRIMARY] = "primary", ++ [REPLICATION_MODE_SECONDARY] = "secondary", ++ }, ++ .size = REPLICATION_MODE__MAX ++}; ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++void qapi_free_BlockdevOptionsReplication(BlockdevOptionsReplication *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsReplication(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++const QEnumLookup NFSTransport_lookup = { ++ .array = (const char *const[]) { ++ [NFS_TRANSPORT_INET] = "inet", ++ }, ++ .size = NFS_TRANSPORT__MAX ++}; ++ ++void qapi_free_NFSServer(NFSServer *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_NFSServer(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsNfs(BlockdevOptionsNfs *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNfs(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlBase(BlockdevOptionsCurlBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlHttp(BlockdevOptionsCurlHttp *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlHttp(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlHttps(BlockdevOptionsCurlHttps *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlHttps(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlFtp(BlockdevOptionsCurlFtp *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlFtp(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCurlFtps(BlockdevOptionsCurlFtps *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCurlFtps(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsNbd(BlockdevOptionsNbd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsNbd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsRaw(BlockdevOptionsRaw *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsRaw(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsThrottle(BlockdevOptionsThrottle *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsThrottle(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsCor(BlockdevOptionsCor *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCor(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup OnCbwError_lookup = { ++ .array = (const char *const[]) { ++ [ON_CBW_ERROR_BREAK_GUEST_WRITE] = "break-guest-write", ++ [ON_CBW_ERROR_BREAK_SNAPSHOT] = "break-snapshot", ++ }, ++ .size = ON_CBW_ERROR__MAX ++}; ++ ++void qapi_free_BlockdevOptionsCbw(BlockdevOptionsCbw *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsCbw(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptions(BlockdevOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevRef(BlockdevRef *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevRef(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevRefOrNull(BlockdevRefOrNull *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevRefOrNull(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevOptionsList(BlockdevOptionsList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevOptionsList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsFile(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsGluster(BlockdevCreateOptionsGluster *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsGluster(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsLUKS(BlockdevCreateOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsNfs(BlockdevCreateOptionsNfs *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsNfs(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsParallels(BlockdevCreateOptionsParallels *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsParallels(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsQcow(BlockdevCreateOptionsQcow *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsQcow(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevQcow2Version_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_QCOW2_VERSION_V2] = "v2", ++ [BLOCKDEV_QCOW2_VERSION_V3] = "v3", ++ }, ++ .size = BLOCKDEV_QCOW2_VERSION__MAX ++}; ++ ++const QEnumLookup Qcow2CompressionType_lookup = { ++ .array = (const char *const[]) { ++ [QCOW2_COMPRESSION_TYPE_ZLIB] = "zlib", ++#if defined(CONFIG_ZSTD) ++ [QCOW2_COMPRESSION_TYPE_ZSTD] = "zstd", ++#endif /* defined(CONFIG_ZSTD) */ ++ }, ++ .size = QCOW2_COMPRESSION_TYPE__MAX ++}; ++ ++void qapi_free_BlockdevCreateOptionsQcow2(BlockdevCreateOptionsQcow2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsQcow2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsQed(BlockdevCreateOptionsQed *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsQed(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsRbd(BlockdevCreateOptionsRbd *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsRbd(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevVmdkSubformat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE] = "monolithicSparse", ++ [BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT] = "monolithicFlat", ++ [BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE] = "twoGbMaxExtentSparse", ++ [BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT] = "twoGbMaxExtentFlat", ++ [BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED] = "streamOptimized", ++ }, ++ .size = BLOCKDEV_VMDK_SUBFORMAT__MAX ++}; ++ ++const QEnumLookup BlockdevVmdkAdapterType_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_VMDK_ADAPTER_TYPE_IDE] = "ide", ++ [BLOCKDEV_VMDK_ADAPTER_TYPE_BUSLOGIC] = "buslogic", ++ [BLOCKDEV_VMDK_ADAPTER_TYPE_LSILOGIC] = "lsilogic", ++ [BLOCKDEV_VMDK_ADAPTER_TYPE_LEGACYESX] = "legacyESX", ++ }, ++ .size = BLOCKDEV_VMDK_ADAPTER_TYPE__MAX ++}; ++ ++void qapi_free_BlockdevCreateOptionsVmdk(BlockdevCreateOptionsVmdk *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsVmdk(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsSsh(BlockdevCreateOptionsSsh *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsSsh(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptionsVdi(BlockdevCreateOptionsVdi *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsVdi(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevVhdxSubformat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC] = "dynamic", ++ [BLOCKDEV_VHDX_SUBFORMAT_FIXED] = "fixed", ++ }, ++ .size = BLOCKDEV_VHDX_SUBFORMAT__MAX ++}; ++ ++void qapi_free_BlockdevCreateOptionsVhdx(BlockdevCreateOptionsVhdx *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsVhdx(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockdevVpcSubformat_lookup = { ++ .array = (const char *const[]) { ++ [BLOCKDEV_VPC_SUBFORMAT_DYNAMIC] = "dynamic", ++ [BLOCKDEV_VPC_SUBFORMAT_FIXED] = "fixed", ++ }, ++ .size = BLOCKDEV_VPC_SUBFORMAT__MAX ++}; ++ ++void qapi_free_BlockdevCreateOptionsVpc(BlockdevCreateOptionsVpc *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptionsVpc(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevCreateOptions(BlockdevCreateOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevCreateOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevAmendOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevAmendOptionsQcow2(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockdevAmendOptions(BlockdevAmendOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevAmendOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup BlockErrorAction_lookup = { ++ .array = (const char *const[]) { ++ [BLOCK_ERROR_ACTION_IGNORE] = "ignore", ++ [BLOCK_ERROR_ACTION_REPORT] = "report", ++ [BLOCK_ERROR_ACTION_STOP] = "stop", ++ }, ++ .size = BLOCK_ERROR_ACTION__MAX ++}; ++ ++const QEnumLookup PreallocMode_lookup = { ++ .array = (const char *const[]) { ++ [PREALLOC_MODE_OFF] = "off", ++ [PREALLOC_MODE_METADATA] = "metadata", ++ [PREALLOC_MODE_FALLOC] = "falloc", ++ [PREALLOC_MODE_FULL] = "full", ++ }, ++ .size = PREALLOC_MODE__MAX ++}; ++ ++const QEnumLookup QuorumOpType_lookup = { ++ .array = (const char *const[]) { ++ [QUORUM_OP_TYPE_READ] = "read", ++ [QUORUM_OP_TYPE_WRITE] = "write", ++ [QUORUM_OP_TYPE_FLUSH] = "flush", ++ }, ++ .size = QUORUM_OP_TYPE__MAX ++}; ++ ++void qapi_free_BlockdevSnapshotInternal(BlockdevSnapshotInternal *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockdevSnapshotInternal(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_BlockGraphInfoList(BlockGraphInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_BlockGraphInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_DummyBlockCoreForceArrays(DummyBlockCoreForceArrays *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_DummyBlockCoreForceArrays(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_block_core_c; +diff --git a/qcow2/lib/qapi/qapi-types-common.c b/qcow2/lib/qapi/qapi-types-common.c +new file mode 100644 +index 00000000..16045e73 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-common.c +@@ -0,0 +1,141 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-common.h" ++#include "qapi-visit-common.h" ++ ++const QEnumLookup IoOperationType_lookup = { ++ .array = (const char *const[]) { ++ [IO_OPERATION_TYPE_READ] = "read", ++ [IO_OPERATION_TYPE_WRITE] = "write", ++ }, ++ .size = IO_OPERATION_TYPE__MAX ++}; ++ ++const QEnumLookup OnOffAuto_lookup = { ++ .array = (const char *const[]) { ++ [ON_OFF_AUTO_AUTO] = "auto", ++ [ON_OFF_AUTO_ON] = "on", ++ [ON_OFF_AUTO_OFF] = "off", ++ }, ++ .size = ON_OFF_AUTO__MAX ++}; ++ ++const QEnumLookup OnOffSplit_lookup = { ++ .array = (const char *const[]) { ++ [ON_OFF_SPLIT_ON] = "on", ++ [ON_OFF_SPLIT_OFF] = "off", ++ [ON_OFF_SPLIT_SPLIT] = "split", ++ }, ++ .size = ON_OFF_SPLIT__MAX ++}; ++ ++void qapi_free_StrOrNull(StrOrNull *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_StrOrNull(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup OffAutoPCIBAR_lookup = { ++ .array = (const char *const[]) { ++ [OFF_AUTOPCIBAR_OFF] = "off", ++ [OFF_AUTOPCIBAR_AUTO] = "auto", ++ [OFF_AUTOPCIBAR_BAR0] = "bar0", ++ [OFF_AUTOPCIBAR_BAR1] = "bar1", ++ [OFF_AUTOPCIBAR_BAR2] = "bar2", ++ [OFF_AUTOPCIBAR_BAR3] = "bar3", ++ [OFF_AUTOPCIBAR_BAR4] = "bar4", ++ [OFF_AUTOPCIBAR_BAR5] = "bar5", ++ }, ++ .size = OFF_AUTOPCIBAR__MAX ++}; ++ ++const QEnumLookup PCIELinkSpeed_lookup = { ++ .array = (const char *const[]) { ++ [PCIE_LINK_SPEED_2_5] = "2_5", ++ [PCIE_LINK_SPEED_5] = "5", ++ [PCIE_LINK_SPEED_8] = "8", ++ [PCIE_LINK_SPEED_16] = "16", ++ [PCIE_LINK_SPEED_32] = "32", ++ [PCIE_LINK_SPEED_64] = "64", ++ }, ++ .size = PCIE_LINK_SPEED__MAX ++}; ++ ++const QEnumLookup PCIELinkWidth_lookup = { ++ .array = (const char *const[]) { ++ [PCIE_LINK_WIDTH_1] = "1", ++ [PCIE_LINK_WIDTH_2] = "2", ++ [PCIE_LINK_WIDTH_4] = "4", ++ [PCIE_LINK_WIDTH_8] = "8", ++ [PCIE_LINK_WIDTH_12] = "12", ++ [PCIE_LINK_WIDTH_16] = "16", ++ [PCIE_LINK_WIDTH_32] = "32", ++ }, ++ .size = PCIE_LINK_WIDTH__MAX ++}; ++ ++const QEnumLookup HostMemPolicy_lookup = { ++ .array = (const char *const[]) { ++ [HOST_MEM_POLICY_DEFAULT] = "default", ++ [HOST_MEM_POLICY_PREFERRED] = "preferred", ++ [HOST_MEM_POLICY_BIND] = "bind", ++ [HOST_MEM_POLICY_INTERLEAVE] = "interleave", ++ }, ++ .size = HOST_MEM_POLICY__MAX ++}; ++ ++const QEnumLookup NetFilterDirection_lookup = { ++ .array = (const char *const[]) { ++ [NET_FILTER_DIRECTION_ALL] = "all", ++ [NET_FILTER_DIRECTION_RX] = "rx", ++ [NET_FILTER_DIRECTION_TX] = "tx", ++ }, ++ .size = NET_FILTER_DIRECTION__MAX ++}; ++ ++const QEnumLookup GrabToggleKeys_lookup = { ++ .array = (const char *const[]) { ++ [GRAB_TOGGLE_KEYS_CTRL_CTRL] = "ctrl-ctrl", ++ [GRAB_TOGGLE_KEYS_ALT_ALT] = "alt-alt", ++ [GRAB_TOGGLE_KEYS_SHIFT_SHIFT] = "shift-shift", ++ [GRAB_TOGGLE_KEYS_META_META] = "meta-meta", ++ [GRAB_TOGGLE_KEYS_SCROLLLOCK] = "scrolllock", ++ [GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK] = "ctrl-scrolllock", ++ }, ++ .size = GRAB_TOGGLE_KEYS__MAX ++}; ++ ++void qapi_free_HumanReadableText(HumanReadableText *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_HumanReadableText(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_common_c; +diff --git a/qcow2/lib/qapi/qapi-types-crypto.c b/qcow2/lib/qapi/qapi-types-crypto.c +new file mode 100644 +index 00000000..cb8f701a +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-crypto.c +@@ -0,0 +1,413 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-crypto.h" ++#include "qapi-visit-crypto.h" ++ ++const QEnumLookup QCryptoTLSCredsEndpoint_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT] = "client", ++ [QCRYPTO_TLS_CREDS_ENDPOINT_SERVER] = "server", ++ }, ++ .size = QCRYPTO_TLS_CREDS_ENDPOINT__MAX ++}; ++ ++const QEnumLookup QCryptoSecretFormat_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_SECRET_FORMAT_RAW] = "raw", ++ [QCRYPTO_SECRET_FORMAT_BASE64] = "base64", ++ }, ++ .size = QCRYPTO_SECRET_FORMAT__MAX ++}; ++ ++const QEnumLookup QCryptoHashAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_HASH_ALG_MD5] = "md5", ++ [QCRYPTO_HASH_ALG_SHA1] = "sha1", ++ [QCRYPTO_HASH_ALG_SHA224] = "sha224", ++ [QCRYPTO_HASH_ALG_SHA256] = "sha256", ++ [QCRYPTO_HASH_ALG_SHA384] = "sha384", ++ [QCRYPTO_HASH_ALG_SHA512] = "sha512", ++ [QCRYPTO_HASH_ALG_RIPEMD160] = "ripemd160", ++ }, ++ .size = QCRYPTO_HASH_ALG__MAX ++}; ++ ++const QEnumLookup QCryptoCipherAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_CIPHER_ALG_AES_128] = "aes-128", ++ [QCRYPTO_CIPHER_ALG_AES_192] = "aes-192", ++ [QCRYPTO_CIPHER_ALG_AES_256] = "aes-256", ++ [QCRYPTO_CIPHER_ALG_DES] = "des", ++ [QCRYPTO_CIPHER_ALG_3DES] = "3des", ++ [QCRYPTO_CIPHER_ALG_CAST5_128] = "cast5-128", ++ [QCRYPTO_CIPHER_ALG_SERPENT_128] = "serpent-128", ++ [QCRYPTO_CIPHER_ALG_SERPENT_192] = "serpent-192", ++ [QCRYPTO_CIPHER_ALG_SERPENT_256] = "serpent-256", ++ [QCRYPTO_CIPHER_ALG_TWOFISH_128] = "twofish-128", ++ [QCRYPTO_CIPHER_ALG_TWOFISH_192] = "twofish-192", ++ [QCRYPTO_CIPHER_ALG_TWOFISH_256] = "twofish-256", ++ [QCRYPTO_CIPHER_ALG_SM4] = "sm4", ++ }, ++ .size = QCRYPTO_CIPHER_ALG__MAX ++}; ++ ++const QEnumLookup QCryptoCipherMode_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_CIPHER_MODE_ECB] = "ecb", ++ [QCRYPTO_CIPHER_MODE_CBC] = "cbc", ++ [QCRYPTO_CIPHER_MODE_XTS] = "xts", ++ [QCRYPTO_CIPHER_MODE_CTR] = "ctr", ++ }, ++ .size = QCRYPTO_CIPHER_MODE__MAX ++}; ++ ++const QEnumLookup QCryptoIVGenAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_IVGEN_ALG_PLAIN] = "plain", ++ [QCRYPTO_IVGEN_ALG_PLAIN64] = "plain64", ++ [QCRYPTO_IVGEN_ALG_ESSIV] = "essiv", ++ }, ++ .size = QCRYPTO_IVGEN_ALG__MAX ++}; ++ ++const QEnumLookup QCryptoBlockFormat_lookup = { ++ .array = (const char *const[]) { ++ [Q_CRYPTO_BLOCK_FORMAT_QCOW] = "qcow", ++ [Q_CRYPTO_BLOCK_FORMAT_LUKS] = "luks", ++ }, ++ .size = Q_CRYPTO_BLOCK_FORMAT__MAX ++}; ++ ++void qapi_free_QCryptoBlockOptionsBase(QCryptoBlockOptionsBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockOptionsBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockOptionsQCow(QCryptoBlockOptionsQCow *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockOptionsQCow(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockOptionsLUKS(QCryptoBlockOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockCreateOptionsLUKS(QCryptoBlockCreateOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockCreateOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockOpenOptions(QCryptoBlockOpenOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockOpenOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockCreateOptions(QCryptoBlockCreateOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockCreateOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfoBase(QCryptoBlockInfoBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfoBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfoLUKSSlot(QCryptoBlockInfoLUKSSlot *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfoLUKSSlot(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfoLUKSSlotList(QCryptoBlockInfoLUKSSlotList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfoLUKSSlotList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfoLUKS(QCryptoBlockInfoLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfoLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockInfo(QCryptoBlockInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup QCryptoBlockLUKSKeyslotState_lookup = { ++ .array = (const char *const[]) { ++ [Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE] = "active", ++ [Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE] = "inactive", ++ }, ++ .size = Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE__MAX ++}; ++ ++void qapi_free_QCryptoBlockAmendOptionsLUKS(QCryptoBlockAmendOptionsLUKS *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockAmendOptionsLUKS(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoBlockAmendOptions(QCryptoBlockAmendOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoBlockAmendOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SecretCommonProperties(SecretCommonProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SecretCommonProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SecretProperties(SecretProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SecretProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++#if defined(CONFIG_SECRET_KEYRING) ++void qapi_free_SecretKeyringProperties(SecretKeyringProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SecretKeyringProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++void qapi_free_TlsCredsProperties(TlsCredsProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_TlsCredsProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_TlsCredsAnonProperties(TlsCredsAnonProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_TlsCredsAnonProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_TlsCredsPskProperties(TlsCredsPskProperties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_TlsCredsPskProperties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_TlsCredsX509Properties(TlsCredsX509Properties *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_TlsCredsX509Properties(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup QCryptoAkCipherAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_AKCIPHER_ALG_RSA] = "rsa", ++ }, ++ .size = QCRYPTO_AKCIPHER_ALG__MAX ++}; ++ ++const QEnumLookup QCryptoAkCipherKeyType_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC] = "public", ++ [QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE] = "private", ++ }, ++ .size = QCRYPTO_AKCIPHER_KEY_TYPE__MAX ++}; ++ ++const QEnumLookup QCryptoRSAPaddingAlgorithm_lookup = { ++ .array = (const char *const[]) { ++ [QCRYPTO_RSA_PADDING_ALG_RAW] = "raw", ++ [QCRYPTO_RSA_PADDING_ALG_PKCS1] = "pkcs1", ++ }, ++ .size = QCRYPTO_RSA_PADDING_ALG__MAX ++}; ++ ++void qapi_free_QCryptoAkCipherOptionsRSA(QCryptoAkCipherOptionsRSA *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoAkCipherOptionsRSA(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_QCryptoAkCipherOptions(QCryptoAkCipherOptions *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_QCryptoAkCipherOptions(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_crypto_c; +diff --git a/qcow2/lib/qapi/qapi-types-job.c b/qcow2/lib/qapi/qapi-types-job.c +new file mode 100644 +index 00000000..eba27079 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-job.c +@@ -0,0 +1,91 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-job.h" ++#include "qapi-visit-job.h" ++ ++const QEnumLookup JobType_lookup = { ++ .array = (const char *const[]) { ++ [JOB_TYPE_COMMIT] = "commit", ++ [JOB_TYPE_STREAM] = "stream", ++ [JOB_TYPE_MIRROR] = "mirror", ++ [JOB_TYPE_BACKUP] = "backup", ++ [JOB_TYPE_CREATE] = "create", ++ [JOB_TYPE_AMEND] = "amend", ++ [JOB_TYPE_SNAPSHOT_LOAD] = "snapshot-load", ++ [JOB_TYPE_SNAPSHOT_SAVE] = "snapshot-save", ++ [JOB_TYPE_SNAPSHOT_DELETE] = "snapshot-delete", ++ }, ++ .size = JOB_TYPE__MAX ++}; ++ ++const QEnumLookup JobStatus_lookup = { ++ .array = (const char *const[]) { ++ [JOB_STATUS_UNDEFINED] = "undefined", ++ [JOB_STATUS_CREATED] = "created", ++ [JOB_STATUS_RUNNING] = "running", ++ [JOB_STATUS_PAUSED] = "paused", ++ [JOB_STATUS_READY] = "ready", ++ [JOB_STATUS_STANDBY] = "standby", ++ [JOB_STATUS_WAITING] = "waiting", ++ [JOB_STATUS_PENDING] = "pending", ++ [JOB_STATUS_ABORTING] = "aborting", ++ [JOB_STATUS_CONCLUDED] = "concluded", ++ [JOB_STATUS_NULL] = "null", ++ }, ++ .size = JOB_STATUS__MAX ++}; ++ ++const QEnumLookup JobVerb_lookup = { ++ .array = (const char *const[]) { ++ [JOB_VERB_CANCEL] = "cancel", ++ [JOB_VERB_PAUSE] = "pause", ++ [JOB_VERB_RESUME] = "resume", ++ [JOB_VERB_SET_SPEED] = "set-speed", ++ [JOB_VERB_COMPLETE] = "complete", ++ [JOB_VERB_DISMISS] = "dismiss", ++ [JOB_VERB_FINALIZE] = "finalize", ++ [JOB_VERB_CHANGE] = "change", ++ }, ++ .size = JOB_VERB__MAX ++}; ++ ++void qapi_free_JobInfo(JobInfo *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_JobInfo(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_JobInfoList(JobInfoList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_JobInfoList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_job_c; +diff --git a/qcow2/lib/qapi/qapi-types-sockets.c b/qcow2/lib/qapi/qapi-types-sockets.c +new file mode 100644 +index 00000000..568f49e6 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-sockets.c +@@ -0,0 +1,209 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-sockets.h" ++#include "qapi-visit-sockets.h" ++ ++const QEnumLookup NetworkAddressFamily_lookup = { ++ .array = (const char *const[]) { ++ [NETWORK_ADDRESS_FAMILY_IPV4] = "ipv4", ++ [NETWORK_ADDRESS_FAMILY_IPV6] = "ipv6", ++ [NETWORK_ADDRESS_FAMILY_UNIX] = "unix", ++ [NETWORK_ADDRESS_FAMILY_VSOCK] = "vsock", ++ [NETWORK_ADDRESS_FAMILY_UNKNOWN] = "unknown", ++ }, ++ .size = NETWORK_ADDRESS_FAMILY__MAX ++}; ++ ++void qapi_free_InetSocketAddressBase(InetSocketAddressBase *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_InetSocketAddressBase(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_InetSocketAddress(InetSocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_InetSocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_UnixSocketAddress(UnixSocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_UnixSocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_VsockSocketAddress(VsockSocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_VsockSocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_FdSocketAddress(FdSocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_FdSocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_InetSocketAddressWrapper(InetSocketAddressWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_InetSocketAddressWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_UnixSocketAddressWrapper(UnixSocketAddressWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_UnixSocketAddressWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_VsockSocketAddressWrapper(VsockSocketAddressWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_VsockSocketAddressWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_FdSocketAddressWrapper(FdSocketAddressWrapper *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_FdSocketAddressWrapper(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SocketAddressLegacy(SocketAddressLegacy *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SocketAddressLegacy(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++const QEnumLookup SocketAddressType_lookup = { ++ .array = (const char *const[]) { ++ [SOCKET_ADDRESS_TYPE_INET] = "inet", ++ [SOCKET_ADDRESS_TYPE_UNIX] = "unix", ++ [SOCKET_ADDRESS_TYPE_VSOCK] = "vsock", ++ [SOCKET_ADDRESS_TYPE_FD] = "fd", ++ }, ++ .size = SOCKET_ADDRESS_TYPE__MAX ++}; ++ ++void qapi_free_SocketAddress(SocketAddress *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SocketAddress(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_SocketAddressList(SocketAddressList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_SocketAddressList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_InetSocketAddressBaseList(InetSocketAddressBaseList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_InetSocketAddressBaseList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_sockets_c; +diff --git a/qcow2/lib/qapi/qapi-types-yank.c b/qcow2/lib/qapi/qapi-types-yank.c +new file mode 100644 +index 00000000..e75e13a2 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-types-yank.c +@@ -0,0 +1,80 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI types ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (c) 2013-2018 Red Hat Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/dealloc-visitor.h" ++#include "qapi-types-yank.h" ++#include "qapi-visit-yank.h" ++ ++const QEnumLookup YankInstanceType_lookup = { ++ .array = (const char *const[]) { ++ [YANK_INSTANCE_TYPE_BLOCK_NODE] = "block-node", ++ [YANK_INSTANCE_TYPE_CHARDEV] = "chardev", ++ [YANK_INSTANCE_TYPE_MIGRATION] = "migration", ++ }, ++ .size = YANK_INSTANCE_TYPE__MAX ++}; ++ ++void qapi_free_YankInstanceBlockNode(YankInstanceBlockNode *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_YankInstanceBlockNode(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_YankInstanceChardev(YankInstanceChardev *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_YankInstanceChardev(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_YankInstance(YankInstance *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_YankInstance(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++void qapi_free_YankInstanceList(YankInstanceList *obj) ++{ ++ Visitor *v; ++ ++ if (!obj) { ++ return; ++ } ++ ++ v = qapi_dealloc_visitor_new(); ++ visit_type_YankInstanceList(v, NULL, &obj, NULL); ++ visit_free(v); ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_types_yank_c; +diff --git a/qcow2/lib/qapi/qapi-util.c b/qcow2/lib/qapi/qapi-util.c +new file mode 100644 +index 00000000..65a7d184 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-util.c +@@ -0,0 +1,154 @@ ++/* ++ * QAPI util functions ++ * ++ * Authors: ++ * Hu Tao ++ * Peter Lieven ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qemu/ctype.h" ++#include "qapi/qmp/qerror.h" ++ ++CompatPolicy compat_policy; ++ ++static bool compat_policy_input_ok1(const char *adjective, ++ CompatPolicyInput policy, ++ ErrorClass error_class, ++ const char *kind, const char *name, ++ Error **errp) ++{ ++ switch (policy) { ++ case COMPAT_POLICY_INPUT_ACCEPT: ++ return true; ++ case COMPAT_POLICY_INPUT_REJECT: ++ error_set(errp, error_class, "%s %s %s disabled by policy", ++ adjective, kind, name); ++ return false; ++ case COMPAT_POLICY_INPUT_CRASH: ++ default: ++ abort(); ++ } ++} ++ ++bool compat_policy_input_ok(unsigned special_features, ++ const CompatPolicy *policy, ++ ErrorClass error_class, ++ const char *kind, const char *name, ++ Error **errp) ++{ ++ if ((special_features & 1u << QAPI_DEPRECATED) ++ && !compat_policy_input_ok1("Deprecated", ++ policy->deprecated_input, ++ error_class, kind, name, errp)) { ++ return false; ++ } ++ if ((special_features & (1u << QAPI_UNSTABLE)) ++ && !compat_policy_input_ok1("Unstable", ++ policy->unstable_input, ++ error_class, kind, name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++const char *qapi_enum_lookup(const QEnumLookup *lookup, int val) ++{ ++ assert(val >= 0 && val < lookup->size); ++ ++ return lookup->array[val]; ++} ++ ++int qapi_enum_parse(const QEnumLookup *lookup, const char *buf, ++ int def, Error **errp) ++{ ++ int i; ++ ++ if (!buf) { ++ return def; ++ } ++ ++ for (i = 0; i < lookup->size; i++) { ++ if (!strcmp(buf, lookup->array[i])) { ++ return i; ++ } ++ } ++ ++ error_setg(errp, "invalid parameter value: %s", buf); ++ return def; ++} ++ ++bool qapi_bool_parse(const char *name, const char *value, bool *obj, Error **errp) ++{ ++ if (g_str_equal(value, "on") || ++ g_str_equal(value, "yes") || ++ g_str_equal(value, "true") || ++ g_str_equal(value, "y")) { ++ *obj = true; ++ return true; ++ } ++ if (g_str_equal(value, "off") || ++ g_str_equal(value, "no") || ++ g_str_equal(value, "false") || ++ g_str_equal(value, "n")) { ++ *obj = false; ++ return true; ++ } ++ ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, ++ "'on' or 'off'"); ++ return false; ++} ++ ++/* ++ * Parse a valid QAPI name from @str. ++ * A valid name consists of letters, digits, hyphen and underscore. ++ * It may be prefixed by __RFQDN_ (downstream extension), where RFQDN ++ * may contain only letters, digits, hyphen and period. ++ * The special exception for enumeration names is not implemented. ++ * See docs/devel/qapi-code-gen.rst for more on QAPI naming rules. ++ * Keep this consistent with scripts/qapi-gen.py! ++ * If @complete, the parse fails unless it consumes @str completely. ++ * Return its length on success, -1 on failure. ++ */ ++int parse_qapi_name(const char *str, bool complete) ++{ ++ const char *p = str; ++ ++ if (*p == '_') { /* Downstream __RFQDN_ */ ++ p++; ++ if (*p != '_') { ++ return -1; ++ } ++ while (*++p) { ++ if (!qemu_isalnum(*p) && *p != '-' && *p != '.') { ++ break; ++ } ++ } ++ ++ if (*p != '_') { ++ return -1; ++ } ++ p++; ++ } ++ ++ if (!qemu_isalpha(*p)) { ++ return -1; ++ } ++ while (*++p) { ++ if (!qemu_isalnum(*p) && *p != '-' && *p != '_') { ++ break; ++ } ++ } ++ ++ if (complete && *p) { ++ return -1; ++ } ++ return p - str; ++} +diff --git a/qcow2/lib/qapi/qapi-visit-block-core.c b/qcow2/lib/qapi/qapi-visit-block-core.c +new file mode 100644 +index 00000000..b8d709cb +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-block-core.c +@@ -0,0 +1,9398 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-block-core.h" ++ ++bool visit_type_SnapshotInfo_members(Visitor *v, SnapshotInfo *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "vm-state-size", &obj->vm_state_size, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "date-sec", &obj->date_sec, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "date-nsec", &obj->date_nsec, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "vm-clock-sec", &obj->vm_clock_sec, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "vm-clock-nsec", &obj->vm_clock_nsec, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "icount", &obj->has_icount)) { ++ if (!visit_type_int(v, "icount", &obj->icount, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_SnapshotInfo(Visitor *v, const char *name, ++ SnapshotInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SnapshotInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SnapshotInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SnapshotInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2EncryptionBase_members(Visitor *v, ImageInfoSpecificQCow2EncryptionBase *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevQcow2EncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2EncryptionBase(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2EncryptionBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificQCow2EncryptionBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificQCow2EncryptionBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificQCow2EncryptionBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2Encryption_members(Visitor *v, ImageInfoSpecificQCow2Encryption *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificQCow2EncryptionBase_members(v, (ImageInfoSpecificQCow2EncryptionBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS: ++ return visit_type_QCryptoBlockInfoLUKS_members(v, &obj->u.luks, errp); ++ case BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2Encryption(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2Encryption **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificQCow2Encryption), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificQCow2Encryption_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificQCow2Encryption(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2BitmapInfoList(Visitor *v, const char *name, ++ Qcow2BitmapInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ Qcow2BitmapInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (Qcow2BitmapInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_Qcow2BitmapInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2BitmapInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2_members(Visitor *v, ImageInfoSpecificQCow2 *obj, Error **errp) ++{ ++ bool has_data_file = !!obj->data_file; ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_str(v, "compat", &obj->compat, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "data-file", &has_data_file)) { ++ if (!visit_type_str(v, "data-file", &obj->data_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "data-file-raw", &obj->has_data_file_raw)) { ++ if (!visit_type_bool(v, "data-file-raw", &obj->data_file_raw, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "extended-l2", &obj->has_extended_l2)) { ++ if (!visit_type_bool(v, "extended-l2", &obj->extended_l2, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "lazy-refcounts", &obj->has_lazy_refcounts)) { ++ if (!visit_type_bool(v, "lazy-refcounts", &obj->lazy_refcounts, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "corrupt", &obj->has_corrupt)) { ++ if (!visit_type_bool(v, "corrupt", &obj->corrupt, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "refcount-bits", &obj->refcount_bits, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_ImageInfoSpecificQCow2Encryption(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bitmaps", &obj->has_bitmaps)) { ++ if (!visit_type_Qcow2BitmapInfoList(v, "bitmaps", &obj->bitmaps, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_Qcow2CompressionType(v, "compression-type", &obj->compression_type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificQCow2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificQCow2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificQCow2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_VmdkExtentInfoList(Visitor *v, const char *name, ++ VmdkExtentInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ VmdkExtentInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (VmdkExtentInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_VmdkExtentInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_VmdkExtentInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificVmdk_members(Visitor *v, ImageInfoSpecificVmdk *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "create-type", &obj->create_type, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "cid", &obj->cid, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "parent-cid", &obj->parent_cid, errp)) { ++ return false; ++ } ++ if (!visit_type_VmdkExtentInfoList(v, "extents", &obj->extents, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificVmdk(Visitor *v, const char *name, ++ ImageInfoSpecificVmdk **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificVmdk), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificVmdk_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificVmdk(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_VmdkExtentInfo_members(Visitor *v, VmdkExtentInfo *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "virtual-size", &obj->virtual_size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_int(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compressed", &obj->has_compressed)) { ++ if (!visit_type_bool(v, "compressed", &obj->compressed, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_VmdkExtentInfo(Visitor *v, const char *name, ++ VmdkExtentInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(VmdkExtentInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_VmdkExtentInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_VmdkExtentInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificRbd_members(Visitor *v, ImageInfoSpecificRbd *obj, Error **errp) ++{ ++ if (visit_optional(v, "encryption-format", &obj->has_encryption_format)) { ++ if (!visit_type_RbdImageEncryptionFormat(v, "encryption-format", &obj->encryption_format, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificRbd(Visitor *v, const char *name, ++ ImageInfoSpecificRbd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificRbd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificRbd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificRbd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificFile_members(Visitor *v, ImageInfoSpecificFile *obj, Error **errp) ++{ ++ if (visit_optional(v, "extent-size-hint", &obj->has_extent_size_hint)) { ++ if (!visit_type_size(v, "extent-size-hint", &obj->extent_size_hint, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificFile(Visitor *v, const char *name, ++ ImageInfoSpecificFile **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificFile), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificFile_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificFile(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificKind(Visitor *v, const char *name, ++ ImageInfoSpecificKind *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &ImageInfoSpecificKind_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2Wrapper_members(Visitor *v, ImageInfoSpecificQCow2Wrapper *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificQCow2(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificQCow2Wrapper(Visitor *v, const char *name, ++ ImageInfoSpecificQCow2Wrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificQCow2Wrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificQCow2Wrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificQCow2Wrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificVmdkWrapper_members(Visitor *v, ImageInfoSpecificVmdkWrapper *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificVmdk(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificVmdkWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificVmdkWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificVmdkWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificVmdkWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificVmdkWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificLUKSWrapper_members(Visitor *v, ImageInfoSpecificLUKSWrapper *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockInfoLUKS(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificLUKSWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificLUKSWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificLUKSWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificLUKSWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificLUKSWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificRbdWrapper_members(Visitor *v, ImageInfoSpecificRbdWrapper *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificRbd(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificRbdWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificRbdWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificRbdWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificRbdWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificRbdWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfoSpecificFileWrapper_members(Visitor *v, ImageInfoSpecificFileWrapper *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificFile(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecificFileWrapper(Visitor *v, const char *name, ++ ImageInfoSpecificFileWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecificFileWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecificFileWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecificFileWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_ImageInfoSpecific_base_members(Visitor *v, q_obj_ImageInfoSpecific_base *obj, Error **errp) ++{ ++ if (!visit_type_ImageInfoSpecificKind(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecific_members(Visitor *v, ImageInfoSpecific *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_ImageInfoSpecific_base_members(v, (q_obj_ImageInfoSpecific_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case IMAGE_INFO_SPECIFIC_KIND_QCOW2: ++ return visit_type_ImageInfoSpecificQCow2Wrapper_members(v, &obj->u.qcow2, errp); ++ case IMAGE_INFO_SPECIFIC_KIND_VMDK: ++ return visit_type_ImageInfoSpecificVmdkWrapper_members(v, &obj->u.vmdk, errp); ++ case IMAGE_INFO_SPECIFIC_KIND_LUKS: ++ return visit_type_ImageInfoSpecificLUKSWrapper_members(v, &obj->u.luks, errp); ++ case IMAGE_INFO_SPECIFIC_KIND_RBD: ++ return visit_type_ImageInfoSpecificRbdWrapper_members(v, &obj->u.rbd, errp); ++ case IMAGE_INFO_SPECIFIC_KIND_FILE: ++ return visit_type_ImageInfoSpecificFileWrapper_members(v, &obj->u.file, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfoSpecific(Visitor *v, const char *name, ++ ImageInfoSpecific **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfoSpecific), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfoSpecific_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfoSpecific(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SnapshotInfoList(Visitor *v, const char *name, ++ SnapshotInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ SnapshotInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (SnapshotInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_SnapshotInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SnapshotInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockNodeInfo_members(Visitor *v, BlockNodeInfo *obj, Error **errp) ++{ ++ bool has_backing_filename = !!obj->backing_filename; ++ bool has_full_backing_filename = !!obj->full_backing_filename; ++ bool has_backing_filename_format = !!obj->backing_filename_format; ++ bool has_format_specific = !!obj->format_specific; ++ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "dirty-flag", &obj->has_dirty_flag)) { ++ if (!visit_type_bool(v, "dirty-flag", &obj->dirty_flag, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "actual-size", &obj->has_actual_size)) { ++ if (!visit_type_int(v, "actual-size", &obj->actual_size, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "virtual-size", &obj->virtual_size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_int(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypted", &obj->has_encrypted)) { ++ if (!visit_type_bool(v, "encrypted", &obj->encrypted, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compressed", &obj->has_compressed)) { ++ if (!visit_type_bool(v, "compressed", &obj->compressed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-filename", &has_backing_filename)) { ++ if (!visit_type_str(v, "backing-filename", &obj->backing_filename, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "full-backing-filename", &has_full_backing_filename)) { ++ if (!visit_type_str(v, "full-backing-filename", &obj->full_backing_filename, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-filename-format", &has_backing_filename_format)) { ++ if (!visit_type_str(v, "backing-filename-format", &obj->backing_filename_format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "snapshots", &obj->has_snapshots)) { ++ if (!visit_type_SnapshotInfoList(v, "snapshots", &obj->snapshots, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "format-specific", &has_format_specific)) { ++ if (!visit_type_ImageInfoSpecific(v, "format-specific", &obj->format_specific, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockNodeInfo(Visitor *v, const char *name, ++ BlockNodeInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockNodeInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockNodeInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockNodeInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageInfo_members(Visitor *v, ImageInfo *obj, Error **errp) ++{ ++ bool has_backing_image = !!obj->backing_image; ++ ++ if (!visit_type_BlockNodeInfo_members(v, (BlockNodeInfo *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing-image", &has_backing_image)) { ++ if (!visit_type_ImageInfo(v, "backing-image", &obj->backing_image, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ImageInfo(Visitor *v, const char *name, ++ ImageInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockChildInfo_members(Visitor *v, BlockChildInfo *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockGraphInfo(v, "info", &obj->info, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockChildInfo(Visitor *v, const char *name, ++ BlockChildInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockChildInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockChildInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockChildInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockChildInfoList(Visitor *v, const char *name, ++ BlockChildInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockChildInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockChildInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockChildInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockChildInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockGraphInfo_members(Visitor *v, BlockGraphInfo *obj, Error **errp) ++{ ++ if (!visit_type_BlockNodeInfo_members(v, (BlockNodeInfo *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockChildInfoList(v, "children", &obj->children, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockGraphInfo(Visitor *v, const char *name, ++ BlockGraphInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockGraphInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockGraphInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockGraphInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ImageCheck_members(Visitor *v, ImageCheck *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "check-errors", &obj->check_errors, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "image-end-offset", &obj->has_image_end_offset)) { ++ if (!visit_type_int(v, "image-end-offset", &obj->image_end_offset, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "corruptions", &obj->has_corruptions)) { ++ if (!visit_type_int(v, "corruptions", &obj->corruptions, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "leaks", &obj->has_leaks)) { ++ if (!visit_type_int(v, "leaks", &obj->leaks, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "corruptions-fixed", &obj->has_corruptions_fixed)) { ++ if (!visit_type_int(v, "corruptions-fixed", &obj->corruptions_fixed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "leaks-fixed", &obj->has_leaks_fixed)) { ++ if (!visit_type_int(v, "leaks-fixed", &obj->leaks_fixed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "total-clusters", &obj->has_total_clusters)) { ++ if (!visit_type_int(v, "total-clusters", &obj->total_clusters, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "allocated-clusters", &obj->has_allocated_clusters)) { ++ if (!visit_type_int(v, "allocated-clusters", &obj->allocated_clusters, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "fragmented-clusters", &obj->has_fragmented_clusters)) { ++ if (!visit_type_int(v, "fragmented-clusters", &obj->fragmented_clusters, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compressed-clusters", &obj->has_compressed_clusters)) { ++ if (!visit_type_int(v, "compressed-clusters", &obj->compressed_clusters, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ImageCheck(Visitor *v, const char *name, ++ ImageCheck **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ImageCheck), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ImageCheck_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ImageCheck(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_MapEntry_members(Visitor *v, MapEntry *obj, Error **errp) ++{ ++ bool has_filename = !!obj->filename; ++ ++ if (!visit_type_int(v, "start", &obj->start, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "length", &obj->length, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "zero", &obj->zero, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "compressed", &obj->compressed, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "depth", &obj->depth, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "present", &obj->present, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "offset", &obj->has_offset)) { ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filename", &has_filename)) { ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_MapEntry(Visitor *v, const char *name, ++ MapEntry **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(MapEntry), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_MapEntry_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_MapEntry(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCacheInfo_members(Visitor *v, BlockdevCacheInfo *obj, Error **errp) ++{ ++ if (!visit_type_bool(v, "writeback", &obj->writeback, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "direct", &obj->direct, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "no-flush", &obj->no_flush, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCacheInfo(Visitor *v, const char *name, ++ BlockdevCacheInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCacheInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCacheInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCacheInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyInfoList(Visitor *v, const char *name, ++ BlockDirtyInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockDirtyInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockDirtyInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockDirtyInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceInfo_members(Visitor *v, BlockDeviceInfo *obj, Error **errp) ++{ ++ bool has_node_name = !!obj->node_name; ++ bool has_backing_file = !!obj->backing_file; ++ bool has_group = !!obj->group; ++ ++ if (!visit_type_str(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_bool(v, "ro", &obj->ro, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "drv", &obj->drv, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing_file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing_file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "backing_file_depth", &obj->backing_file_depth, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "encrypted", &obj->encrypted, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevDetectZeroesOptions(v, "detect_zeroes", &obj->detect_zeroes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps", &obj->bps, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps_rd", &obj->bps_rd, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps_wr", &obj->bps_wr, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops", &obj->iops, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops_rd", &obj->iops_rd, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops_wr", &obj->iops_wr, errp)) { ++ return false; ++ } ++ if (!visit_type_ImageInfo(v, "image", &obj->image, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bps_max", &obj->has_bps_max)) { ++ if (!visit_type_int(v, "bps_max", &obj->bps_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_rd_max", &obj->has_bps_rd_max)) { ++ if (!visit_type_int(v, "bps_rd_max", &obj->bps_rd_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_wr_max", &obj->has_bps_wr_max)) { ++ if (!visit_type_int(v, "bps_wr_max", &obj->bps_wr_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_max", &obj->has_iops_max)) { ++ if (!visit_type_int(v, "iops_max", &obj->iops_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_rd_max", &obj->has_iops_rd_max)) { ++ if (!visit_type_int(v, "iops_rd_max", &obj->iops_rd_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_wr_max", &obj->has_iops_wr_max)) { ++ if (!visit_type_int(v, "iops_wr_max", &obj->iops_wr_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_max_length", &obj->has_bps_max_length)) { ++ if (!visit_type_int(v, "bps_max_length", &obj->bps_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_rd_max_length", &obj->has_bps_rd_max_length)) { ++ if (!visit_type_int(v, "bps_rd_max_length", &obj->bps_rd_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_wr_max_length", &obj->has_bps_wr_max_length)) { ++ if (!visit_type_int(v, "bps_wr_max_length", &obj->bps_wr_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_max_length", &obj->has_iops_max_length)) { ++ if (!visit_type_int(v, "iops_max_length", &obj->iops_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_rd_max_length", &obj->has_iops_rd_max_length)) { ++ if (!visit_type_int(v, "iops_rd_max_length", &obj->iops_rd_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_wr_max_length", &obj->has_iops_wr_max_length)) { ++ if (!visit_type_int(v, "iops_wr_max_length", &obj->iops_wr_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_size", &obj->has_iops_size)) { ++ if (!visit_type_int(v, "iops_size", &obj->iops_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "group", &has_group)) { ++ if (!visit_type_str(v, "group", &obj->group, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_BlockdevCacheInfo(v, "cache", &obj->cache, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "write_threshold", &obj->write_threshold, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "dirty-bitmaps", &obj->has_dirty_bitmaps)) { ++ if (!visit_type_BlockDirtyInfoList(v, "dirty-bitmaps", &obj->dirty_bitmaps, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDeviceInfo(Visitor *v, const char *name, ++ BlockDeviceInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDeviceInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDeviceInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceIoStatus(Visitor *v, const char *name, ++ BlockDeviceIoStatus *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockDeviceIoStatus_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockDirtyInfo_members(Visitor *v, BlockDirtyInfo *obj, Error **errp) ++{ ++ bool has_name = !!obj->name; ++ ++ if (visit_optional(v, "name", &has_name)) { ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "count", &obj->count, errp)) { ++ return false; ++ } ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "recording", &obj->recording, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "busy", &obj->busy, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "persistent", &obj->persistent, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "inconsistent", &obj->has_inconsistent)) { ++ if (!visit_type_bool(v, "inconsistent", &obj->inconsistent, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyInfo(Visitor *v, const char *name, ++ BlockDirtyInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2BitmapInfoFlags(Visitor *v, const char *name, ++ Qcow2BitmapInfoFlags *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &Qcow2BitmapInfoFlags_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_Qcow2BitmapInfoFlagsList(Visitor *v, const char *name, ++ Qcow2BitmapInfoFlagsList **obj, Error **errp) ++{ ++ bool ok = false; ++ Qcow2BitmapInfoFlagsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (Qcow2BitmapInfoFlagsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_Qcow2BitmapInfoFlags(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2BitmapInfoFlagsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2BitmapInfo_members(Visitor *v, Qcow2BitmapInfo *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ if (!visit_type_Qcow2BitmapInfoFlagsList(v, "flags", &obj->flags, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_Qcow2BitmapInfo(Visitor *v, const char *name, ++ Qcow2BitmapInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(Qcow2BitmapInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_Qcow2BitmapInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2BitmapInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockLatencyHistogramInfo_members(Visitor *v, BlockLatencyHistogramInfo *obj, Error **errp) ++{ ++ if (!visit_type_uint64List(v, "boundaries", &obj->boundaries, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64List(v, "bins", &obj->bins, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockLatencyHistogramInfo(Visitor *v, const char *name, ++ BlockLatencyHistogramInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockLatencyHistogramInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockLatencyHistogramInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockLatencyHistogramInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockInfo_members(Visitor *v, BlockInfo *obj, Error **errp) ++{ ++ bool has_qdev = !!obj->qdev; ++ bool has_inserted = !!obj->inserted; ++ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "qdev", &has_qdev)) { ++ if (!visit_type_str(v, "qdev", &obj->qdev, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "removable", &obj->removable, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "locked", &obj->locked, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "inserted", &has_inserted)) { ++ if (!visit_type_BlockDeviceInfo(v, "inserted", &obj->inserted, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "tray_open", &obj->has_tray_open)) { ++ if (!visit_type_bool(v, "tray_open", &obj->tray_open, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "io-status", &obj->has_io_status)) { ++ if (!visit_type_BlockDeviceIoStatus(v, "io-status", &obj->io_status, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockInfo(Visitor *v, const char *name, ++ BlockInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockMeasureInfo_members(Visitor *v, BlockMeasureInfo *obj, Error **errp) ++{ ++ if (!visit_type_int(v, "required", &obj->required, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "fully-allocated", &obj->fully_allocated, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bitmaps", &obj->has_bitmaps)) { ++ if (!visit_type_int(v, "bitmaps", &obj->bitmaps, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockMeasureInfo(Visitor *v, const char *name, ++ BlockMeasureInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockMeasureInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockMeasureInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockMeasureInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockInfoList(Visitor *v, const char *name, ++ BlockInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceTimedStats_members(Visitor *v, BlockDeviceTimedStats *obj, Error **errp) ++{ ++ if (!visit_type_int(v, "interval_length", &obj->interval_length, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "min_rd_latency_ns", &obj->min_rd_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "max_rd_latency_ns", &obj->max_rd_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "avg_rd_latency_ns", &obj->avg_rd_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "min_wr_latency_ns", &obj->min_wr_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "max_wr_latency_ns", &obj->max_wr_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "avg_wr_latency_ns", &obj->avg_wr_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "min_zone_append_latency_ns", &obj->min_zone_append_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "max_zone_append_latency_ns", &obj->max_zone_append_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "avg_zone_append_latency_ns", &obj->avg_zone_append_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "min_flush_latency_ns", &obj->min_flush_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "max_flush_latency_ns", &obj->max_flush_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "avg_flush_latency_ns", &obj->avg_flush_latency_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_number(v, "avg_rd_queue_depth", &obj->avg_rd_queue_depth, errp)) { ++ return false; ++ } ++ if (!visit_type_number(v, "avg_wr_queue_depth", &obj->avg_wr_queue_depth, errp)) { ++ return false; ++ } ++ if (!visit_type_number(v, "avg_zone_append_queue_depth", &obj->avg_zone_append_queue_depth, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockDeviceTimedStats(Visitor *v, const char *name, ++ BlockDeviceTimedStats **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDeviceTimedStats), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDeviceTimedStats_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceTimedStats(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceTimedStatsList(Visitor *v, const char *name, ++ BlockDeviceTimedStatsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockDeviceTimedStatsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockDeviceTimedStatsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockDeviceTimedStats(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceTimedStatsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDeviceStats_members(Visitor *v, BlockDeviceStats *obj, Error **errp) ++{ ++ bool has_rd_latency_histogram = !!obj->rd_latency_histogram; ++ bool has_wr_latency_histogram = !!obj->wr_latency_histogram; ++ bool has_zone_append_latency_histogram = !!obj->zone_append_latency_histogram; ++ bool has_flush_latency_histogram = !!obj->flush_latency_histogram; ++ ++ if (!visit_type_int(v, "rd_bytes", &obj->rd_bytes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_bytes", &obj->wr_bytes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "zone_append_bytes", &obj->zone_append_bytes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "unmap_bytes", &obj->unmap_bytes, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "rd_operations", &obj->rd_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_operations", &obj->wr_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "zone_append_operations", &obj->zone_append_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "flush_operations", &obj->flush_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "unmap_operations", &obj->unmap_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "rd_total_time_ns", &obj->rd_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_total_time_ns", &obj->wr_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "zone_append_total_time_ns", &obj->zone_append_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "flush_total_time_ns", &obj->flush_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "unmap_total_time_ns", &obj->unmap_total_time_ns, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_highest_offset", &obj->wr_highest_offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "rd_merged", &obj->rd_merged, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "wr_merged", &obj->wr_merged, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "zone_append_merged", &obj->zone_append_merged, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "unmap_merged", &obj->unmap_merged, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "idle_time_ns", &obj->has_idle_time_ns)) { ++ if (!visit_type_int(v, "idle_time_ns", &obj->idle_time_ns, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "failed_rd_operations", &obj->failed_rd_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "failed_wr_operations", &obj->failed_wr_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "failed_zone_append_operations", &obj->failed_zone_append_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "failed_flush_operations", &obj->failed_flush_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "failed_unmap_operations", &obj->failed_unmap_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_rd_operations", &obj->invalid_rd_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_wr_operations", &obj->invalid_wr_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_zone_append_operations", &obj->invalid_zone_append_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_flush_operations", &obj->invalid_flush_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "invalid_unmap_operations", &obj->invalid_unmap_operations, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "account_invalid", &obj->account_invalid, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "account_failed", &obj->account_failed, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockDeviceTimedStatsList(v, "timed_stats", &obj->timed_stats, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "rd_latency_histogram", &has_rd_latency_histogram)) { ++ if (!visit_type_BlockLatencyHistogramInfo(v, "rd_latency_histogram", &obj->rd_latency_histogram, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "wr_latency_histogram", &has_wr_latency_histogram)) { ++ if (!visit_type_BlockLatencyHistogramInfo(v, "wr_latency_histogram", &obj->wr_latency_histogram, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "zone_append_latency_histogram", &has_zone_append_latency_histogram)) { ++ if (!visit_type_BlockLatencyHistogramInfo(v, "zone_append_latency_histogram", &obj->zone_append_latency_histogram, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "flush_latency_histogram", &has_flush_latency_histogram)) { ++ if (!visit_type_BlockLatencyHistogramInfo(v, "flush_latency_histogram", &obj->flush_latency_histogram, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDeviceStats(Visitor *v, const char *name, ++ BlockDeviceStats **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDeviceStats), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDeviceStats_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceStats(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockStatsSpecificFile_members(Visitor *v, BlockStatsSpecificFile *obj, Error **errp) ++{ ++ if (!visit_type_uint64(v, "discard-nb-ok", &obj->discard_nb_ok, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "discard-nb-failed", &obj->discard_nb_failed, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "discard-bytes-ok", &obj->discard_bytes_ok, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsSpecificFile(Visitor *v, const char *name, ++ BlockStatsSpecificFile **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockStatsSpecificFile), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockStatsSpecificFile_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStatsSpecificFile(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockStatsSpecificNvme_members(Visitor *v, BlockStatsSpecificNvme *obj, Error **errp) ++{ ++ if (!visit_type_uint64(v, "completion-errors", &obj->completion_errors, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "aligned-accesses", &obj->aligned_accesses, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "unaligned-accesses", &obj->unaligned_accesses, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsSpecificNvme(Visitor *v, const char *name, ++ BlockStatsSpecificNvme **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockStatsSpecificNvme), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockStatsSpecificNvme_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStatsSpecificNvme(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockStatsSpecific_base_members(Visitor *v, q_obj_BlockStatsSpecific_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevDriver(v, "driver", &obj->driver, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsSpecific_members(Visitor *v, BlockStatsSpecific *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockStatsSpecific_base_members(v, (q_obj_BlockStatsSpecific_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->driver) { ++ case BLOCKDEV_DRIVER_FILE: ++ return visit_type_BlockStatsSpecificFile_members(v, &obj->u.file, errp); ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_DEVICE: ++ return visit_type_BlockStatsSpecificFile_members(v, &obj->u.host_device, errp); ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_NVME: ++ return visit_type_BlockStatsSpecificNvme_members(v, &obj->u.nvme, errp); ++ case BLOCKDEV_DRIVER_BLKDEBUG: ++ break; ++ case BLOCKDEV_DRIVER_BLKLOGWRITES: ++ break; ++ case BLOCKDEV_DRIVER_BLKREPLAY: ++ break; ++ case BLOCKDEV_DRIVER_BLKVERIFY: ++ break; ++ case BLOCKDEV_DRIVER_BOCHS: ++ break; ++ case BLOCKDEV_DRIVER_CLOOP: ++ break; ++ case BLOCKDEV_DRIVER_COMPRESS: ++ break; ++ case BLOCKDEV_DRIVER_COPY_BEFORE_WRITE: ++ break; ++ case BLOCKDEV_DRIVER_COPY_ON_READ: ++ break; ++ case BLOCKDEV_DRIVER_DMG: ++ break; ++ case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: ++ break; ++ case BLOCKDEV_DRIVER_FTP: ++ break; ++ case BLOCKDEV_DRIVER_FTPS: ++ break; ++ case BLOCKDEV_DRIVER_GLUSTER: ++ break; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_CDROM: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_HTTP: ++ break; ++ case BLOCKDEV_DRIVER_HTTPS: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_ISCSI: ++ break; ++ case BLOCKDEV_DRIVER_LUKS: ++ break; ++ case BLOCKDEV_DRIVER_NBD: ++ break; ++ case BLOCKDEV_DRIVER_NFS: ++ break; ++ case BLOCKDEV_DRIVER_NULL_AIO: ++ break; ++ case BLOCKDEV_DRIVER_NULL_CO: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_NVME_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_PARALLELS: ++ break; ++ case BLOCKDEV_DRIVER_PREALLOCATE: ++ break; ++ case BLOCKDEV_DRIVER_QCOW: ++ break; ++ case BLOCKDEV_DRIVER_QCOW2: ++ break; ++ case BLOCKDEV_DRIVER_QED: ++ break; ++ case BLOCKDEV_DRIVER_QUORUM: ++ break; ++ case BLOCKDEV_DRIVER_RAW: ++ break; ++ case BLOCKDEV_DRIVER_RBD: ++ break; ++#if defined(CONFIG_REPLICATION) ++ case BLOCKDEV_DRIVER_REPLICATION: ++ break; ++#endif /* defined(CONFIG_REPLICATION) */ ++ case BLOCKDEV_DRIVER_SSH: ++ break; ++ case BLOCKDEV_DRIVER_THROTTLE: ++ break; ++ case BLOCKDEV_DRIVER_VDI: ++ break; ++ case BLOCKDEV_DRIVER_VHDX: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_VMDK: ++ break; ++ case BLOCKDEV_DRIVER_VPC: ++ break; ++ case BLOCKDEV_DRIVER_VVFAT: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsSpecific(Visitor *v, const char *name, ++ BlockStatsSpecific **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockStatsSpecific), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockStatsSpecific_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStatsSpecific(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockStats_members(Visitor *v, BlockStats *obj, Error **errp) ++{ ++ bool has_device = !!obj->device; ++ bool has_qdev = !!obj->qdev; ++ bool has_node_name = !!obj->node_name; ++ bool has_driver_specific = !!obj->driver_specific; ++ bool has_parent = !!obj->parent; ++ bool has_backing = !!obj->backing; ++ ++ if (visit_optional(v, "device", &has_device)) { ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "qdev", &has_qdev)) { ++ if (!visit_type_str(v, "qdev", &obj->qdev, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_BlockDeviceStats(v, "stats", &obj->stats, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "driver-specific", &has_driver_specific)) { ++ if (!visit_type_BlockStatsSpecific(v, "driver-specific", &obj->driver_specific, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "parent", &has_parent)) { ++ if (!visit_type_BlockStats(v, "parent", &obj->parent, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing", &has_backing)) { ++ if (!visit_type_BlockStats(v, "backing", &obj->backing, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockStats(Visitor *v, const char *name, ++ BlockStats **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockStats), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockStats_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStats(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_query_blockstats_arg_members(Visitor *v, q_obj_query_blockstats_arg *obj, Error **errp) ++{ ++ if (visit_optional(v, "query-nodes", &obj->has_query_nodes)) { ++ if (!visit_type_bool(v, "query-nodes", &obj->query_nodes, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockStatsList(Visitor *v, const char *name, ++ BlockStatsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockStatsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockStatsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockStats(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockStatsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOnError(Visitor *v, const char *name, ++ BlockdevOnError *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevOnError_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_MirrorSyncMode(Visitor *v, const char *name, ++ MirrorSyncMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &MirrorSyncMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BitmapSyncMode(Visitor *v, const char *name, ++ BitmapSyncMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BitmapSyncMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_MirrorCopyMode(Visitor *v, const char *name, ++ MirrorCopyMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &MirrorCopyMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockJobInfoMirror_members(Visitor *v, BlockJobInfoMirror *obj, Error **errp) ++{ ++ if (!visit_type_bool(v, "actively-synced", &obj->actively_synced, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobInfoMirror(Visitor *v, const char *name, ++ BlockJobInfoMirror **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockJobInfoMirror), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockJobInfoMirror_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobInfoMirror(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockJobInfo_base_members(Visitor *v, q_obj_BlockJobInfo_base *obj, Error **errp) ++{ ++ bool has_error = !!obj->error; ++ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "len", &obj->len, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "busy", &obj->busy, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "paused", &obj->paused, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockDeviceIoStatus(v, "io-status", &obj->io_status, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "ready", &obj->ready, errp)) { ++ return false; ++ } ++ if (!visit_type_JobStatus(v, "status", &obj->status, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "error", &has_error)) { ++ if (!visit_type_str(v, "error", &obj->error, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobInfo_members(Visitor *v, BlockJobInfo *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockJobInfo_base_members(v, (q_obj_BlockJobInfo_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case JOB_TYPE_MIRROR: ++ return visit_type_BlockJobInfoMirror_members(v, &obj->u.mirror, errp); ++ case JOB_TYPE_COMMIT: ++ break; ++ case JOB_TYPE_STREAM: ++ break; ++ case JOB_TYPE_BACKUP: ++ break; ++ case JOB_TYPE_CREATE: ++ break; ++ case JOB_TYPE_AMEND: ++ break; ++ case JOB_TYPE_SNAPSHOT_LOAD: ++ break; ++ case JOB_TYPE_SNAPSHOT_SAVE: ++ break; ++ case JOB_TYPE_SNAPSHOT_DELETE: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobInfo(Visitor *v, const char *name, ++ BlockJobInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockJobInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockJobInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockJobInfoList(Visitor *v, const char *name, ++ BlockJobInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockJobInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockJobInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockJobInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_block_resize_arg_members(Visitor *v, q_obj_block_resize_arg *obj, Error **errp) ++{ ++ bool has_device = !!obj->device; ++ bool has_node_name = !!obj->node_name; ++ ++ if (visit_optional(v, "device", &has_device)) { ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_NewImageMode(Visitor *v, const char *name, ++ NewImageMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &NewImageMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevSnapshotSync_members(Visitor *v, BlockdevSnapshotSync *obj, Error **errp) ++{ ++ bool has_device = !!obj->device; ++ bool has_node_name = !!obj->node_name; ++ bool has_snapshot_node_name = !!obj->snapshot_node_name; ++ bool has_format = !!obj->format; ++ ++ if (visit_optional(v, "device", &has_device)) { ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "snapshot-file", &obj->snapshot_file, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "snapshot-node-name", &has_snapshot_node_name)) { ++ if (!visit_type_str(v, "snapshot-node-name", &obj->snapshot_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "format", &has_format)) { ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "mode", &obj->has_mode)) { ++ if (!visit_type_NewImageMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevSnapshotSync(Visitor *v, const char *name, ++ BlockdevSnapshotSync **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevSnapshotSync), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevSnapshotSync_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevSnapshotSync(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevSnapshot_members(Visitor *v, BlockdevSnapshot *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "overlay", &obj->overlay, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevSnapshot(Visitor *v, const char *name, ++ BlockdevSnapshot **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevSnapshot), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevSnapshot_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevSnapshot(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BackupPerf_members(Visitor *v, BackupPerf *obj, Error **errp) ++{ ++ if (visit_optional(v, "use-copy-range", &obj->has_use_copy_range)) { ++ if (!visit_type_bool(v, "use-copy-range", &obj->use_copy_range, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-workers", &obj->has_max_workers)) { ++ if (!visit_type_int(v, "max-workers", &obj->max_workers, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-chunk", &obj->has_max_chunk)) { ++ if (!visit_type_int64(v, "max-chunk", &obj->max_chunk, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BackupPerf(Visitor *v, const char *name, ++ BackupPerf **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BackupPerf), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BackupPerf_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BackupPerf(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BackupCommon_members(Visitor *v, BackupCommon *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_bitmap = !!obj->bitmap; ++ bool has_filter_node_name = !!obj->filter_node_name; ++ bool has_x_perf = !!obj->x_perf; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_MirrorSyncMode(v, "sync", &obj->sync, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bitmap", &has_bitmap)) { ++ if (!visit_type_str(v, "bitmap", &obj->bitmap, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bitmap-mode", &obj->has_bitmap_mode)) { ++ if (!visit_type_BitmapSyncMode(v, "bitmap-mode", &obj->bitmap_mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compress", &obj->has_compress)) { ++ if (!visit_type_bool(v, "compress", &obj->compress, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-source-error", &obj->has_on_source_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-source-error", &obj->on_source_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-target-error", &obj->has_on_target_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-target-error", &obj->on_target_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filter-node-name", &has_filter_node_name)) { ++ if (!visit_type_str(v, "filter-node-name", &obj->filter_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "discard-source", &obj->has_discard_source)) { ++ if (!visit_type_bool(v, "discard-source", &obj->discard_source, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "x-perf", &has_x_perf)) { ++ if (visit_policy_reject(v, "x-perf", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-perf", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_BackupPerf(v, "x-perf", &obj->x_perf, errp)) { ++ return false; ++ } ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BackupCommon(Visitor *v, const char *name, ++ BackupCommon **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BackupCommon), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BackupCommon_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BackupCommon(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_DriveBackup_members(Visitor *v, DriveBackup *obj, Error **errp) ++{ ++ bool has_format = !!obj->format; ++ ++ if (!visit_type_BackupCommon_members(v, (BackupCommon *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "format", &has_format)) { ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "mode", &obj->has_mode)) { ++ if (!visit_type_NewImageMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_DriveBackup(Visitor *v, const char *name, ++ DriveBackup **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(DriveBackup), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_DriveBackup_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_DriveBackup(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevBackup_members(Visitor *v, BlockdevBackup *obj, Error **errp) ++{ ++ if (!visit_type_BackupCommon_members(v, (BackupCommon *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevBackup(Visitor *v, const char *name, ++ BlockdevBackup **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevBackup), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevBackup_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevBackup(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_change_backing_file_arg_members(Visitor *v, q_obj_change_backing_file_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "image-node-name", &obj->image_node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_commit_arg_members(Visitor *v, q_obj_block_commit_arg *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_base_node = !!obj->base_node; ++ bool has_base = !!obj->base; ++ bool has_top_node = !!obj->top_node; ++ bool has_top = !!obj->top; ++ bool has_backing_file = !!obj->backing_file; ++ bool has_filter_node_name = !!obj->filter_node_name; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "base-node", &has_base_node)) { ++ if (!visit_type_str(v, "base-node", &obj->base_node, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "base", &has_base)) { ++ if (visit_policy_reject(v, "base", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "base", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_str(v, "base", &obj->base, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "top-node", &has_top_node)) { ++ if (!visit_type_str(v, "top-node", &obj->top_node, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "top", &has_top)) { ++ if (visit_policy_reject(v, "top", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "top", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_str(v, "top", &obj->top, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-mask-protocol", &obj->has_backing_mask_protocol)) { ++ if (!visit_type_bool(v, "backing-mask-protocol", &obj->backing_mask_protocol, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-error", &obj->has_on_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-error", &obj->on_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filter-node-name", &has_filter_node_name)) { ++ if (!visit_type_str(v, "filter-node-name", &obj->filter_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_query_named_block_nodes_arg_members(Visitor *v, q_obj_query_named_block_nodes_arg *obj, Error **errp) ++{ ++ if (visit_optional(v, "flat", &obj->has_flat)) { ++ if (!visit_type_bool(v, "flat", &obj->flat, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDeviceInfoList(Visitor *v, const char *name, ++ BlockDeviceInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockDeviceInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockDeviceInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockDeviceInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDeviceInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphNodeType(Visitor *v, const char *name, ++ XDbgBlockGraphNodeType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &XDbgBlockGraphNodeType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphNode_members(Visitor *v, XDbgBlockGraphNode *obj, Error **errp) ++{ ++ if (!visit_type_uint64(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_XDbgBlockGraphNodeType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_XDbgBlockGraphNode(Visitor *v, const char *name, ++ XDbgBlockGraphNode **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(XDbgBlockGraphNode), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_XDbgBlockGraphNode_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraphNode(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockPermission(Visitor *v, const char *name, ++ BlockPermission *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockPermission_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockPermissionList(Visitor *v, const char *name, ++ BlockPermissionList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockPermissionList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockPermissionList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockPermission(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockPermissionList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphEdge_members(Visitor *v, XDbgBlockGraphEdge *obj, Error **errp) ++{ ++ if (!visit_type_uint64(v, "parent", &obj->parent, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "child", &obj->child, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockPermissionList(v, "perm", &obj->perm, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockPermissionList(v, "shared-perm", &obj->shared_perm, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_XDbgBlockGraphEdge(Visitor *v, const char *name, ++ XDbgBlockGraphEdge **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(XDbgBlockGraphEdge), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_XDbgBlockGraphEdge_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraphEdge(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphNodeList(Visitor *v, const char *name, ++ XDbgBlockGraphNodeList **obj, Error **errp) ++{ ++ bool ok = false; ++ XDbgBlockGraphNodeList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (XDbgBlockGraphNodeList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_XDbgBlockGraphNode(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraphNodeList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraphEdgeList(Visitor *v, const char *name, ++ XDbgBlockGraphEdgeList **obj, Error **errp) ++{ ++ bool ok = false; ++ XDbgBlockGraphEdgeList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (XDbgBlockGraphEdgeList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_XDbgBlockGraphEdge(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraphEdgeList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_XDbgBlockGraph_members(Visitor *v, XDbgBlockGraph *obj, Error **errp) ++{ ++ if (!visit_type_XDbgBlockGraphNodeList(v, "nodes", &obj->nodes, errp)) { ++ return false; ++ } ++ if (!visit_type_XDbgBlockGraphEdgeList(v, "edges", &obj->edges, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_XDbgBlockGraph(Visitor *v, const char *name, ++ XDbgBlockGraph **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(XDbgBlockGraph), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_XDbgBlockGraph_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_XDbgBlockGraph(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_DriveMirror_members(Visitor *v, DriveMirror *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_format = !!obj->format; ++ bool has_node_name = !!obj->node_name; ++ bool has_replaces = !!obj->replaces; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "format", &has_format)) { ++ if (!visit_type_str(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "replaces", &has_replaces)) { ++ if (!visit_type_str(v, "replaces", &obj->replaces, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_MirrorSyncMode(v, "sync", &obj->sync, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "mode", &obj->has_mode)) { ++ if (!visit_type_NewImageMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "granularity", &obj->has_granularity)) { ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "buf-size", &obj->has_buf_size)) { ++ if (!visit_type_int(v, "buf-size", &obj->buf_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-source-error", &obj->has_on_source_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-source-error", &obj->on_source_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-target-error", &obj->has_on_target_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-target-error", &obj->on_target_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "unmap", &obj->has_unmap)) { ++ if (!visit_type_bool(v, "unmap", &obj->unmap, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "copy-mode", &obj->has_copy_mode)) { ++ if (!visit_type_MirrorCopyMode(v, "copy-mode", &obj->copy_mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_DriveMirror(Visitor *v, const char *name, ++ DriveMirror **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(DriveMirror), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_DriveMirror_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_DriveMirror(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmap_members(Visitor *v, BlockDirtyBitmap *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyBitmap(Visitor *v, const char *name, ++ BlockDirtyBitmap **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyBitmap), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyBitmap_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmap(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapAdd_members(Visitor *v, BlockDirtyBitmapAdd *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "granularity", &obj->has_granularity)) { ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "persistent", &obj->has_persistent)) { ++ if (!visit_type_bool(v, "persistent", &obj->persistent, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "disabled", &obj->has_disabled)) { ++ if (!visit_type_bool(v, "disabled", &obj->disabled, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyBitmapAdd(Visitor *v, const char *name, ++ BlockDirtyBitmapAdd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyBitmapAdd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyBitmapAdd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapAdd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapOrStr(Visitor *v, const char *name, ++ BlockDirtyBitmapOrStr **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QSTRING: ++ ok = visit_type_str(v, name, &(*obj)->u.local, errp); ++ break; ++ case QTYPE_QDICT: ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ break; ++ } ++ if (visit_type_BlockDirtyBitmap_members(v, &(*obj)->u.external, errp)) { ++ ok = visit_check_struct(v, errp); ++ } ++ visit_end_struct(v, NULL); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: BlockDirtyBitmapOrStr", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_BlockDirtyBitmapOrStr() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapOrStr(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapOrStrList(Visitor *v, const char *name, ++ BlockDirtyBitmapOrStrList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockDirtyBitmapOrStrList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockDirtyBitmapOrStrList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockDirtyBitmapOrStr(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapOrStrList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapMerge_members(Visitor *v, BlockDirtyBitmapMerge *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockDirtyBitmapOrStrList(v, "bitmaps", &obj->bitmaps, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyBitmapMerge(Visitor *v, const char *name, ++ BlockDirtyBitmapMerge **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyBitmapMerge), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyBitmapMerge_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapMerge(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockDirtyBitmapSha256_members(Visitor *v, BlockDirtyBitmapSha256 *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "sha256", &obj->sha256, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockDirtyBitmapSha256(Visitor *v, const char *name, ++ BlockDirtyBitmapSha256 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockDirtyBitmapSha256), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockDirtyBitmapSha256_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockDirtyBitmapSha256(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_blockdev_mirror_arg_members(Visitor *v, q_obj_blockdev_mirror_arg *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_replaces = !!obj->replaces; ++ bool has_filter_node_name = !!obj->filter_node_name; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "replaces", &has_replaces)) { ++ if (!visit_type_str(v, "replaces", &obj->replaces, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_MirrorSyncMode(v, "sync", &obj->sync, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "granularity", &obj->has_granularity)) { ++ if (!visit_type_uint32(v, "granularity", &obj->granularity, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "buf-size", &obj->has_buf_size)) { ++ if (!visit_type_int(v, "buf-size", &obj->buf_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-source-error", &obj->has_on_source_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-source-error", &obj->on_source_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-target-error", &obj->has_on_target_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-target-error", &obj->on_target_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filter-node-name", &has_filter_node_name)) { ++ if (!visit_type_str(v, "filter-node-name", &obj->filter_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "copy-mode", &obj->has_copy_mode)) { ++ if (!visit_type_MirrorCopyMode(v, "copy-mode", &obj->copy_mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockIOThrottle_members(Visitor *v, BlockIOThrottle *obj, Error **errp) ++{ ++ bool has_device = !!obj->device; ++ bool has_id = !!obj->id; ++ bool has_group = !!obj->group; ++ ++ if (visit_optional(v, "device", &has_device)) { ++ if (visit_policy_reject(v, "device", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "device", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "id", &has_id)) { ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "bps", &obj->bps, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps_rd", &obj->bps_rd, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "bps_wr", &obj->bps_wr, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops", &obj->iops, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops_rd", &obj->iops_rd, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "iops_wr", &obj->iops_wr, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bps_max", &obj->has_bps_max)) { ++ if (!visit_type_int(v, "bps_max", &obj->bps_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_rd_max", &obj->has_bps_rd_max)) { ++ if (!visit_type_int(v, "bps_rd_max", &obj->bps_rd_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_wr_max", &obj->has_bps_wr_max)) { ++ if (!visit_type_int(v, "bps_wr_max", &obj->bps_wr_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_max", &obj->has_iops_max)) { ++ if (!visit_type_int(v, "iops_max", &obj->iops_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_rd_max", &obj->has_iops_rd_max)) { ++ if (!visit_type_int(v, "iops_rd_max", &obj->iops_rd_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_wr_max", &obj->has_iops_wr_max)) { ++ if (!visit_type_int(v, "iops_wr_max", &obj->iops_wr_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_max_length", &obj->has_bps_max_length)) { ++ if (!visit_type_int(v, "bps_max_length", &obj->bps_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_rd_max_length", &obj->has_bps_rd_max_length)) { ++ if (!visit_type_int(v, "bps_rd_max_length", &obj->bps_rd_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps_wr_max_length", &obj->has_bps_wr_max_length)) { ++ if (!visit_type_int(v, "bps_wr_max_length", &obj->bps_wr_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_max_length", &obj->has_iops_max_length)) { ++ if (!visit_type_int(v, "iops_max_length", &obj->iops_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_rd_max_length", &obj->has_iops_rd_max_length)) { ++ if (!visit_type_int(v, "iops_rd_max_length", &obj->iops_rd_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_wr_max_length", &obj->has_iops_wr_max_length)) { ++ if (!visit_type_int(v, "iops_wr_max_length", &obj->iops_wr_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops_size", &obj->has_iops_size)) { ++ if (!visit_type_int(v, "iops_size", &obj->iops_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "group", &has_group)) { ++ if (!visit_type_str(v, "group", &obj->group, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockIOThrottle(Visitor *v, const char *name, ++ BlockIOThrottle **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockIOThrottle), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockIOThrottle_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockIOThrottle(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ThrottleLimits_members(Visitor *v, ThrottleLimits *obj, Error **errp) ++{ ++ if (visit_optional(v, "iops-total", &obj->has_iops_total)) { ++ if (!visit_type_int(v, "iops-total", &obj->iops_total, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-total-max", &obj->has_iops_total_max)) { ++ if (!visit_type_int(v, "iops-total-max", &obj->iops_total_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-total-max-length", &obj->has_iops_total_max_length)) { ++ if (!visit_type_int(v, "iops-total-max-length", &obj->iops_total_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-read", &obj->has_iops_read)) { ++ if (!visit_type_int(v, "iops-read", &obj->iops_read, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-read-max", &obj->has_iops_read_max)) { ++ if (!visit_type_int(v, "iops-read-max", &obj->iops_read_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-read-max-length", &obj->has_iops_read_max_length)) { ++ if (!visit_type_int(v, "iops-read-max-length", &obj->iops_read_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-write", &obj->has_iops_write)) { ++ if (!visit_type_int(v, "iops-write", &obj->iops_write, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-write-max", &obj->has_iops_write_max)) { ++ if (!visit_type_int(v, "iops-write-max", &obj->iops_write_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-write-max-length", &obj->has_iops_write_max_length)) { ++ if (!visit_type_int(v, "iops-write-max-length", &obj->iops_write_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-total", &obj->has_bps_total)) { ++ if (!visit_type_int(v, "bps-total", &obj->bps_total, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-total-max", &obj->has_bps_total_max)) { ++ if (!visit_type_int(v, "bps-total-max", &obj->bps_total_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-total-max-length", &obj->has_bps_total_max_length)) { ++ if (!visit_type_int(v, "bps-total-max-length", &obj->bps_total_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-read", &obj->has_bps_read)) { ++ if (!visit_type_int(v, "bps-read", &obj->bps_read, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-read-max", &obj->has_bps_read_max)) { ++ if (!visit_type_int(v, "bps-read-max", &obj->bps_read_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-read-max-length", &obj->has_bps_read_max_length)) { ++ if (!visit_type_int(v, "bps-read-max-length", &obj->bps_read_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-write", &obj->has_bps_write)) { ++ if (!visit_type_int(v, "bps-write", &obj->bps_write, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-write-max", &obj->has_bps_write_max)) { ++ if (!visit_type_int(v, "bps-write-max", &obj->bps_write_max, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bps-write-max-length", &obj->has_bps_write_max_length)) { ++ if (!visit_type_int(v, "bps-write-max-length", &obj->bps_write_max_length, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iops-size", &obj->has_iops_size)) { ++ if (!visit_type_int(v, "iops-size", &obj->iops_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ThrottleLimits(Visitor *v, const char *name, ++ ThrottleLimits **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ThrottleLimits), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ThrottleLimits_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ThrottleLimits(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_ThrottleGroupProperties_members(Visitor *v, ThrottleGroupProperties *obj, Error **errp) ++{ ++ bool has_limits = !!obj->limits; ++ ++ if (visit_optional(v, "limits", &has_limits)) { ++ if (!visit_type_ThrottleLimits(v, "limits", &obj->limits, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "x-iops-total", &obj->has_x_iops_total)) { ++ if (visit_policy_reject(v, "x-iops-total", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-total", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-total", &obj->x_iops_total, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-total-max", &obj->has_x_iops_total_max)) { ++ if (visit_policy_reject(v, "x-iops-total-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-total-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-total-max", &obj->x_iops_total_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-total-max-length", &obj->has_x_iops_total_max_length)) { ++ if (visit_policy_reject(v, "x-iops-total-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-total-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-total-max-length", &obj->x_iops_total_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-read", &obj->has_x_iops_read)) { ++ if (visit_policy_reject(v, "x-iops-read", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-read", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-read", &obj->x_iops_read, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-read-max", &obj->has_x_iops_read_max)) { ++ if (visit_policy_reject(v, "x-iops-read-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-read-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-read-max", &obj->x_iops_read_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-read-max-length", &obj->has_x_iops_read_max_length)) { ++ if (visit_policy_reject(v, "x-iops-read-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-read-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-read-max-length", &obj->x_iops_read_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-write", &obj->has_x_iops_write)) { ++ if (visit_policy_reject(v, "x-iops-write", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-write", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-write", &obj->x_iops_write, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-write-max", &obj->has_x_iops_write_max)) { ++ if (visit_policy_reject(v, "x-iops-write-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-write-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-write-max", &obj->x_iops_write_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-write-max-length", &obj->has_x_iops_write_max_length)) { ++ if (visit_policy_reject(v, "x-iops-write-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-write-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-write-max-length", &obj->x_iops_write_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-total", &obj->has_x_bps_total)) { ++ if (visit_policy_reject(v, "x-bps-total", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-total", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-total", &obj->x_bps_total, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-total-max", &obj->has_x_bps_total_max)) { ++ if (visit_policy_reject(v, "x-bps-total-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-total-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-total-max", &obj->x_bps_total_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-total-max-length", &obj->has_x_bps_total_max_length)) { ++ if (visit_policy_reject(v, "x-bps-total-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-total-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-total-max-length", &obj->x_bps_total_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-read", &obj->has_x_bps_read)) { ++ if (visit_policy_reject(v, "x-bps-read", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-read", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-read", &obj->x_bps_read, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-read-max", &obj->has_x_bps_read_max)) { ++ if (visit_policy_reject(v, "x-bps-read-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-read-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-read-max", &obj->x_bps_read_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-read-max-length", &obj->has_x_bps_read_max_length)) { ++ if (visit_policy_reject(v, "x-bps-read-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-read-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-read-max-length", &obj->x_bps_read_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-write", &obj->has_x_bps_write)) { ++ if (visit_policy_reject(v, "x-bps-write", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-write", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-write", &obj->x_bps_write, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-write-max", &obj->has_x_bps_write_max)) { ++ if (visit_policy_reject(v, "x-bps-write-max", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-write-max", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-write-max", &obj->x_bps_write_max, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-bps-write-max-length", &obj->has_x_bps_write_max_length)) { ++ if (visit_policy_reject(v, "x-bps-write-max-length", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-bps-write-max-length", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-bps-write-max-length", &obj->x_bps_write_max_length, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "x-iops-size", &obj->has_x_iops_size)) { ++ if (visit_policy_reject(v, "x-iops-size", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-iops-size", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_int(v, "x-iops-size", &obj->x_iops_size, errp)) { ++ return false; ++ } ++ } ++ } ++ return true; ++} ++ ++bool visit_type_ThrottleGroupProperties(Visitor *v, const char *name, ++ ThrottleGroupProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(ThrottleGroupProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_ThrottleGroupProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_ThrottleGroupProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_block_stream_arg_members(Visitor *v, q_obj_block_stream_arg *obj, Error **errp) ++{ ++ bool has_job_id = !!obj->job_id; ++ bool has_base = !!obj->base; ++ bool has_base_node = !!obj->base_node; ++ bool has_backing_file = !!obj->backing_file; ++ bool has_bottom = !!obj->bottom; ++ bool has_filter_node_name = !!obj->filter_node_name; ++ ++ if (visit_optional(v, "job-id", &has_job_id)) { ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "base", &has_base)) { ++ if (!visit_type_str(v, "base", &obj->base, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "base-node", &has_base_node)) { ++ if (!visit_type_str(v, "base-node", &obj->base_node, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-mask-protocol", &obj->has_backing_mask_protocol)) { ++ if (!visit_type_bool(v, "backing-mask-protocol", &obj->backing_mask_protocol, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bottom", &has_bottom)) { ++ if (!visit_type_str(v, "bottom", &obj->bottom, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "speed", &obj->has_speed)) { ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-error", &obj->has_on_error)) { ++ if (!visit_type_BlockdevOnError(v, "on-error", &obj->on_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "filter-node-name", &has_filter_node_name)) { ++ if (!visit_type_str(v, "filter-node-name", &obj->filter_node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-finalize", &obj->has_auto_finalize)) { ++ if (!visit_type_bool(v, "auto-finalize", &obj->auto_finalize, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-dismiss", &obj->has_auto_dismiss)) { ++ if (!visit_type_bool(v, "auto-dismiss", &obj->auto_dismiss, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_set_speed_arg_members(Visitor *v, q_obj_block_job_set_speed_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_cancel_arg_members(Visitor *v, q_obj_block_job_cancel_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "force", &obj->has_force)) { ++ if (!visit_type_bool(v, "force", &obj->force, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_pause_arg_members(Visitor *v, q_obj_block_job_pause_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_resume_arg_members(Visitor *v, q_obj_block_job_resume_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_complete_arg_members(Visitor *v, q_obj_block_job_complete_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_dismiss_arg_members(Visitor *v, q_obj_block_job_dismiss_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_job_finalize_arg_members(Visitor *v, q_obj_block_job_finalize_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobChangeOptionsMirror_members(Visitor *v, BlockJobChangeOptionsMirror *obj, Error **errp) ++{ ++ if (!visit_type_MirrorCopyMode(v, "copy-mode", &obj->copy_mode, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobChangeOptionsMirror(Visitor *v, const char *name, ++ BlockJobChangeOptionsMirror **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockJobChangeOptionsMirror), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockJobChangeOptionsMirror_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobChangeOptionsMirror(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockJobChangeOptions_base_members(Visitor *v, q_obj_BlockJobChangeOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobChangeOptions_members(Visitor *v, BlockJobChangeOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockJobChangeOptions_base_members(v, (q_obj_BlockJobChangeOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case JOB_TYPE_MIRROR: ++ return visit_type_BlockJobChangeOptionsMirror_members(v, &obj->u.mirror, errp); ++ case JOB_TYPE_COMMIT: ++ break; ++ case JOB_TYPE_STREAM: ++ break; ++ case JOB_TYPE_BACKUP: ++ break; ++ case JOB_TYPE_CREATE: ++ break; ++ case JOB_TYPE_AMEND: ++ break; ++ case JOB_TYPE_SNAPSHOT_LOAD: ++ break; ++ case JOB_TYPE_SNAPSHOT_SAVE: ++ break; ++ case JOB_TYPE_SNAPSHOT_DELETE: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockJobChangeOptions(Visitor *v, const char *name, ++ BlockJobChangeOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockJobChangeOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockJobChangeOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockJobChangeOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevDiscardOptions(Visitor *v, const char *name, ++ BlockdevDiscardOptions *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevDiscardOptions_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevDetectZeroesOptions(Visitor *v, const char *name, ++ BlockdevDetectZeroesOptions *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevDetectZeroesOptions_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevAioOptions(Visitor *v, const char *name, ++ BlockdevAioOptions *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevAioOptions_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCacheOptions_members(Visitor *v, BlockdevCacheOptions *obj, Error **errp) ++{ ++ if (visit_optional(v, "direct", &obj->has_direct)) { ++ if (!visit_type_bool(v, "direct", &obj->direct, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "no-flush", &obj->has_no_flush)) { ++ if (!visit_type_bool(v, "no-flush", &obj->no_flush, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCacheOptions(Visitor *v, const char *name, ++ BlockdevCacheOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCacheOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCacheOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCacheOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevDriver(Visitor *v, const char *name, ++ BlockdevDriver *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevDriver_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsFile_members(Visitor *v, BlockdevOptionsFile *obj, Error **errp) ++{ ++ bool has_pr_manager = !!obj->pr_manager; ++ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "pr-manager", &has_pr_manager)) { ++ if (!visit_type_str(v, "pr-manager", &obj->pr_manager, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "locking", &obj->has_locking)) { ++ if (!visit_type_OnOffAuto(v, "locking", &obj->locking, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "aio", &obj->has_aio)) { ++ if (!visit_type_BlockdevAioOptions(v, "aio", &obj->aio, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "aio-max-batch", &obj->has_aio_max_batch)) { ++ if (!visit_type_int(v, "aio-max-batch", &obj->aio_max_batch, errp)) { ++ return false; ++ } ++ } ++#if defined(CONFIG_LINUX) ++ if (visit_optional(v, "drop-cache", &obj->has_drop_cache)) { ++ if (!visit_type_bool(v, "drop-cache", &obj->drop_cache, errp)) { ++ return false; ++ } ++ } ++#endif /* defined(CONFIG_LINUX) */ ++ if (visit_optional(v, "x-check-cache-dropped", &obj->has_x_check_cache_dropped)) { ++ if (visit_policy_reject(v, "x-check-cache-dropped", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-check-cache-dropped", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_bool(v, "x-check-cache-dropped", &obj->x_check_cache_dropped, errp)) { ++ return false; ++ } ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsFile(Visitor *v, const char *name, ++ BlockdevOptionsFile **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsFile), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsFile_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsFile(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsNull_members(Visitor *v, BlockdevOptionsNull *obj, Error **errp) ++{ ++ if (visit_optional(v, "size", &obj->has_size)) { ++ if (!visit_type_int(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "latency-ns", &obj->has_latency_ns)) { ++ if (!visit_type_uint64(v, "latency-ns", &obj->latency_ns, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "read-zeroes", &obj->has_read_zeroes)) { ++ if (!visit_type_bool(v, "read-zeroes", &obj->read_zeroes, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNull(Visitor *v, const char *name, ++ BlockdevOptionsNull **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNull), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNull_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNull(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsNVMe_members(Visitor *v, BlockdevOptionsNVMe *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "namespace", &obj->q_namespace, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNVMe(Visitor *v, const char *name, ++ BlockdevOptionsNVMe **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNVMe), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNVMe_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNVMe(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsVVFAT_members(Visitor *v, BlockdevOptionsVVFAT *obj, Error **errp) ++{ ++ bool has_label = !!obj->label; ++ ++ if (!visit_type_str(v, "dir", &obj->dir, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "fat-type", &obj->has_fat_type)) { ++ if (!visit_type_int(v, "fat-type", &obj->fat_type, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "floppy", &obj->has_floppy)) { ++ if (!visit_type_bool(v, "floppy", &obj->floppy, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "label", &has_label)) { ++ if (!visit_type_str(v, "label", &obj->label, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "rw", &obj->has_rw)) { ++ if (!visit_type_bool(v, "rw", &obj->rw, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsVVFAT(Visitor *v, const char *name, ++ BlockdevOptionsVVFAT **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsVVFAT), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsVVFAT_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsVVFAT(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsGenericFormat_members(Visitor *v, BlockdevOptionsGenericFormat *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsGenericFormat(Visitor *v, const char *name, ++ BlockdevOptionsGenericFormat **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsGenericFormat), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsGenericFormat(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsLUKS_members(Visitor *v, BlockdevOptionsLUKS *obj, Error **errp) ++{ ++ bool has_key_secret = !!obj->key_secret; ++ bool has_header = !!obj->header; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "key-secret", &has_key_secret)) { ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "header", &has_header)) { ++ if (!visit_type_BlockdevRef(v, "header", &obj->header, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsLUKS(Visitor *v, const char *name, ++ BlockdevOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsGenericCOWFormat_members(Visitor *v, BlockdevOptionsGenericCOWFormat *obj, Error **errp) ++{ ++ bool has_backing = !!obj->backing; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing", &has_backing)) { ++ if (!visit_type_BlockdevRefOrNull(v, "backing", &obj->backing, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsGenericCOWFormat(Visitor *v, const char *name, ++ BlockdevOptionsGenericCOWFormat **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsGenericCOWFormat), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsGenericCOWFormat_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsGenericCOWFormat(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2OverlapCheckMode(Visitor *v, const char *name, ++ Qcow2OverlapCheckMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &Qcow2OverlapCheckMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_Qcow2OverlapCheckFlags_members(Visitor *v, Qcow2OverlapCheckFlags *obj, Error **errp) ++{ ++ if (visit_optional(v, "template", &obj->has_q_template)) { ++ if (!visit_type_Qcow2OverlapCheckMode(v, "template", &obj->q_template, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "main-header", &obj->has_main_header)) { ++ if (!visit_type_bool(v, "main-header", &obj->main_header, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "active-l1", &obj->has_active_l1)) { ++ if (!visit_type_bool(v, "active-l1", &obj->active_l1, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "active-l2", &obj->has_active_l2)) { ++ if (!visit_type_bool(v, "active-l2", &obj->active_l2, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "refcount-table", &obj->has_refcount_table)) { ++ if (!visit_type_bool(v, "refcount-table", &obj->refcount_table, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "refcount-block", &obj->has_refcount_block)) { ++ if (!visit_type_bool(v, "refcount-block", &obj->refcount_block, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "snapshot-table", &obj->has_snapshot_table)) { ++ if (!visit_type_bool(v, "snapshot-table", &obj->snapshot_table, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "inactive-l1", &obj->has_inactive_l1)) { ++ if (!visit_type_bool(v, "inactive-l1", &obj->inactive_l1, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "inactive-l2", &obj->has_inactive_l2)) { ++ if (!visit_type_bool(v, "inactive-l2", &obj->inactive_l2, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "bitmap-directory", &obj->has_bitmap_directory)) { ++ if (!visit_type_bool(v, "bitmap-directory", &obj->bitmap_directory, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_Qcow2OverlapCheckFlags(Visitor *v, const char *name, ++ Qcow2OverlapCheckFlags **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(Qcow2OverlapCheckFlags), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_Qcow2OverlapCheckFlags_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2OverlapCheckFlags(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_Qcow2OverlapChecks(Visitor *v, const char *name, ++ Qcow2OverlapChecks **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QDICT: ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ break; ++ } ++ if (visit_type_Qcow2OverlapCheckFlags_members(v, &(*obj)->u.flags, errp)) { ++ ok = visit_check_struct(v, errp); ++ } ++ visit_end_struct(v, NULL); ++ break; ++ case QTYPE_QSTRING: ++ ok = visit_type_Qcow2OverlapCheckMode(v, name, &(*obj)->u.mode, errp); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: Qcow2OverlapChecks", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_Qcow2OverlapChecks() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_Qcow2OverlapChecks(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevQcowEncryptionFormat(Visitor *v, const char *name, ++ BlockdevQcowEncryptionFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevQcowEncryptionFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevQcowEncryption_base_members(Visitor *v, q_obj_BlockdevQcowEncryption_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevQcowEncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevQcowEncryption_members(Visitor *v, BlockdevQcowEncryption *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevQcowEncryption_base_members(v, (q_obj_BlockdevQcowEncryption_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case BLOCKDEV_QCOW_ENCRYPTION_FORMAT_AES: ++ return visit_type_QCryptoBlockOptionsQCow_members(v, &obj->u.aes, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevQcowEncryption(Visitor *v, const char *name, ++ BlockdevQcowEncryption **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevQcowEncryption), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevQcowEncryption_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevQcowEncryption(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsQcow_members(Visitor *v, BlockdevOptionsQcow *obj, Error **errp) ++{ ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_BlockdevOptionsGenericCOWFormat_members(v, (BlockdevOptionsGenericCOWFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_BlockdevQcowEncryption(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsQcow(Visitor *v, const char *name, ++ BlockdevOptionsQcow **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsQcow), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsQcow_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsQcow(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevQcow2EncryptionFormat(Visitor *v, const char *name, ++ BlockdevQcow2EncryptionFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevQcow2EncryptionFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevQcow2Encryption_base_members(Visitor *v, q_obj_BlockdevQcow2Encryption_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevQcow2EncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevQcow2Encryption_members(Visitor *v, BlockdevQcow2Encryption *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevQcow2Encryption_base_members(v, (q_obj_BlockdevQcow2Encryption_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES: ++ return visit_type_QCryptoBlockOptionsQCow_members(v, &obj->u.aes, errp); ++ case BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS: ++ return visit_type_QCryptoBlockOptionsLUKS_members(v, &obj->u.luks, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevQcow2Encryption(Visitor *v, const char *name, ++ BlockdevQcow2Encryption **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevQcow2Encryption), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevQcow2Encryption_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevQcow2Encryption(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsPreallocate_members(Visitor *v, BlockdevOptionsPreallocate *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "prealloc-align", &obj->has_prealloc_align)) { ++ if (!visit_type_int(v, "prealloc-align", &obj->prealloc_align, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "prealloc-size", &obj->has_prealloc_size)) { ++ if (!visit_type_int(v, "prealloc-size", &obj->prealloc_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsPreallocate(Visitor *v, const char *name, ++ BlockdevOptionsPreallocate **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsPreallocate), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsPreallocate_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsPreallocate(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsQcow2_members(Visitor *v, BlockdevOptionsQcow2 *obj, Error **errp) ++{ ++ bool has_overlap_check = !!obj->overlap_check; ++ bool has_encrypt = !!obj->encrypt; ++ bool has_data_file = !!obj->data_file; ++ ++ if (!visit_type_BlockdevOptionsGenericCOWFormat_members(v, (BlockdevOptionsGenericCOWFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "lazy-refcounts", &obj->has_lazy_refcounts)) { ++ if (!visit_type_bool(v, "lazy-refcounts", &obj->lazy_refcounts, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "pass-discard-request", &obj->has_pass_discard_request)) { ++ if (!visit_type_bool(v, "pass-discard-request", &obj->pass_discard_request, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "pass-discard-snapshot", &obj->has_pass_discard_snapshot)) { ++ if (!visit_type_bool(v, "pass-discard-snapshot", &obj->pass_discard_snapshot, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "pass-discard-other", &obj->has_pass_discard_other)) { ++ if (!visit_type_bool(v, "pass-discard-other", &obj->pass_discard_other, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "discard-no-unref", &obj->has_discard_no_unref)) { ++ if (!visit_type_bool(v, "discard-no-unref", &obj->discard_no_unref, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "overlap-check", &has_overlap_check)) { ++ if (!visit_type_Qcow2OverlapChecks(v, "overlap-check", &obj->overlap_check, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cache-size", &obj->has_cache_size)) { ++ if (!visit_type_int(v, "cache-size", &obj->cache_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "l2-cache-size", &obj->has_l2_cache_size)) { ++ if (!visit_type_int(v, "l2-cache-size", &obj->l2_cache_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "l2-cache-entry-size", &obj->has_l2_cache_entry_size)) { ++ if (!visit_type_int(v, "l2-cache-entry-size", &obj->l2_cache_entry_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "refcount-cache-size", &obj->has_refcount_cache_size)) { ++ if (!visit_type_int(v, "refcount-cache-size", &obj->refcount_cache_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cache-clean-interval", &obj->has_cache_clean_interval)) { ++ if (!visit_type_int(v, "cache-clean-interval", &obj->cache_clean_interval, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_BlockdevQcow2Encryption(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "data-file", &has_data_file)) { ++ if (!visit_type_BlockdevRef(v, "data-file", &obj->data_file, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsQcow2(Visitor *v, const char *name, ++ BlockdevOptionsQcow2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsQcow2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsQcow2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsQcow2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SshHostKeyCheckMode(Visitor *v, const char *name, ++ SshHostKeyCheckMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &SshHostKeyCheckMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_SshHostKeyCheckHashType(Visitor *v, const char *name, ++ SshHostKeyCheckHashType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &SshHostKeyCheckHashType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_SshHostKeyHash_members(Visitor *v, SshHostKeyHash *obj, Error **errp) ++{ ++ if (!visit_type_SshHostKeyCheckHashType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "hash", &obj->hash, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SshHostKeyHash(Visitor *v, const char *name, ++ SshHostKeyHash **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SshHostKeyHash), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SshHostKeyHash_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SshHostKeyHash(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_SshHostKeyCheck_base_members(Visitor *v, q_obj_SshHostKeyCheck_base *obj, Error **errp) ++{ ++ if (!visit_type_SshHostKeyCheckMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SshHostKeyCheck_members(Visitor *v, SshHostKeyCheck *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_SshHostKeyCheck_base_members(v, (q_obj_SshHostKeyCheck_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->mode) { ++ case SSH_HOST_KEY_CHECK_MODE_HASH: ++ return visit_type_SshHostKeyHash_members(v, &obj->u.hash, errp); ++ case SSH_HOST_KEY_CHECK_MODE_NONE: ++ break; ++ case SSH_HOST_KEY_CHECK_MODE_KNOWN_HOSTS: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_SshHostKeyCheck(Visitor *v, const char *name, ++ SshHostKeyCheck **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SshHostKeyCheck), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SshHostKeyCheck_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SshHostKeyCheck(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsSsh_members(Visitor *v, BlockdevOptionsSsh *obj, Error **errp) ++{ ++ bool has_user = !!obj->user; ++ bool has_host_key_check = !!obj->host_key_check; ++ ++ if (!visit_type_InetSocketAddress(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "user", &has_user)) { ++ if (!visit_type_str(v, "user", &obj->user, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "host-key-check", &has_host_key_check)) { ++ if (!visit_type_SshHostKeyCheck(v, "host-key-check", &obj->host_key_check, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsSsh(Visitor *v, const char *name, ++ BlockdevOptionsSsh **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsSsh), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsSsh_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsSsh(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlkdebugEvent(Visitor *v, const char *name, ++ BlkdebugEvent *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlkdebugEvent_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlkdebugIOType(Visitor *v, const char *name, ++ BlkdebugIOType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlkdebugIOType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlkdebugInjectErrorOptions_members(Visitor *v, BlkdebugInjectErrorOptions *obj, Error **errp) ++{ ++ if (!visit_type_BlkdebugEvent(v, "event", &obj->event, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "state", &obj->has_state)) { ++ if (!visit_type_int(v, "state", &obj->state, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iotype", &obj->has_iotype)) { ++ if (!visit_type_BlkdebugIOType(v, "iotype", &obj->iotype, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "errno", &obj->has_q_errno)) { ++ if (!visit_type_int(v, "errno", &obj->q_errno, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "sector", &obj->has_sector)) { ++ if (!visit_type_int(v, "sector", &obj->sector, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "once", &obj->has_once)) { ++ if (!visit_type_bool(v, "once", &obj->once, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "immediately", &obj->has_immediately)) { ++ if (!visit_type_bool(v, "immediately", &obj->immediately, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlkdebugInjectErrorOptions(Visitor *v, const char *name, ++ BlkdebugInjectErrorOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlkdebugInjectErrorOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlkdebugInjectErrorOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlkdebugInjectErrorOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlkdebugSetStateOptions_members(Visitor *v, BlkdebugSetStateOptions *obj, Error **errp) ++{ ++ if (!visit_type_BlkdebugEvent(v, "event", &obj->event, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "state", &obj->has_state)) { ++ if (!visit_type_int(v, "state", &obj->state, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "new_state", &obj->new_state, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlkdebugSetStateOptions(Visitor *v, const char *name, ++ BlkdebugSetStateOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlkdebugSetStateOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlkdebugSetStateOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlkdebugSetStateOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlkdebugInjectErrorOptionsList(Visitor *v, const char *name, ++ BlkdebugInjectErrorOptionsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlkdebugInjectErrorOptionsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlkdebugInjectErrorOptionsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlkdebugInjectErrorOptions(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlkdebugInjectErrorOptionsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlkdebugSetStateOptionsList(Visitor *v, const char *name, ++ BlkdebugSetStateOptionsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlkdebugSetStateOptionsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlkdebugSetStateOptionsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlkdebugSetStateOptions(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlkdebugSetStateOptionsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsBlkdebug_members(Visitor *v, BlockdevOptionsBlkdebug *obj, Error **errp) ++{ ++ bool has_config = !!obj->config; ++ ++ if (!visit_type_BlockdevRef(v, "image", &obj->image, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "config", &has_config)) { ++ if (!visit_type_str(v, "config", &obj->config, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "align", &obj->has_align)) { ++ if (!visit_type_int(v, "align", &obj->align, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-transfer", &obj->has_max_transfer)) { ++ if (!visit_type_int32(v, "max-transfer", &obj->max_transfer, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "opt-write-zero", &obj->has_opt_write_zero)) { ++ if (!visit_type_int32(v, "opt-write-zero", &obj->opt_write_zero, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-write-zero", &obj->has_max_write_zero)) { ++ if (!visit_type_int32(v, "max-write-zero", &obj->max_write_zero, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "opt-discard", &obj->has_opt_discard)) { ++ if (!visit_type_int32(v, "opt-discard", &obj->opt_discard, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "max-discard", &obj->has_max_discard)) { ++ if (!visit_type_int32(v, "max-discard", &obj->max_discard, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "inject-error", &obj->has_inject_error)) { ++ if (!visit_type_BlkdebugInjectErrorOptionsList(v, "inject-error", &obj->inject_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "set-state", &obj->has_set_state)) { ++ if (!visit_type_BlkdebugSetStateOptionsList(v, "set-state", &obj->set_state, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "take-child-perms", &obj->has_take_child_perms)) { ++ if (!visit_type_BlockPermissionList(v, "take-child-perms", &obj->take_child_perms, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "unshare-child-perms", &obj->has_unshare_child_perms)) { ++ if (!visit_type_BlockPermissionList(v, "unshare-child-perms", &obj->unshare_child_perms, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsBlkdebug(Visitor *v, const char *name, ++ BlockdevOptionsBlkdebug **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsBlkdebug), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsBlkdebug_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsBlkdebug(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsBlklogwrites_members(Visitor *v, BlockdevOptionsBlklogwrites *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevRef(v, "log", &obj->log, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "log-sector-size", &obj->has_log_sector_size)) { ++ if (!visit_type_uint32(v, "log-sector-size", &obj->log_sector_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "log-append", &obj->has_log_append)) { ++ if (!visit_type_bool(v, "log-append", &obj->log_append, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "log-super-update-interval", &obj->has_log_super_update_interval)) { ++ if (!visit_type_uint64(v, "log-super-update-interval", &obj->log_super_update_interval, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsBlklogwrites(Visitor *v, const char *name, ++ BlockdevOptionsBlklogwrites **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsBlklogwrites), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsBlklogwrites_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsBlklogwrites(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsBlkverify_members(Visitor *v, BlockdevOptionsBlkverify *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "test", &obj->test, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevRef(v, "raw", &obj->raw, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsBlkverify(Visitor *v, const char *name, ++ BlockdevOptionsBlkverify **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsBlkverify), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsBlkverify_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsBlkverify(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsBlkreplay_members(Visitor *v, BlockdevOptionsBlkreplay *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "image", &obj->image, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsBlkreplay(Visitor *v, const char *name, ++ BlockdevOptionsBlkreplay **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsBlkreplay), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsBlkreplay_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsBlkreplay(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QuorumReadPattern(Visitor *v, const char *name, ++ QuorumReadPattern *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QuorumReadPattern_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevRefList(Visitor *v, const char *name, ++ BlockdevRefList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockdevRefList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockdevRefList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockdevRef(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevRefList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsQuorum_members(Visitor *v, BlockdevOptionsQuorum *obj, Error **errp) ++{ ++ if (visit_optional(v, "blkverify", &obj->has_blkverify)) { ++ if (!visit_type_bool(v, "blkverify", &obj->blkverify, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_BlockdevRefList(v, "children", &obj->children, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "vote-threshold", &obj->vote_threshold, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "rewrite-corrupted", &obj->has_rewrite_corrupted)) { ++ if (!visit_type_bool(v, "rewrite-corrupted", &obj->rewrite_corrupted, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "read-pattern", &obj->has_read_pattern)) { ++ if (!visit_type_QuorumReadPattern(v, "read-pattern", &obj->read_pattern, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsQuorum(Visitor *v, const char *name, ++ BlockdevOptionsQuorum **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsQuorum), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsQuorum_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsQuorum(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsGluster_members(Visitor *v, BlockdevOptionsGluster *obj, Error **errp) ++{ ++ bool has_logfile = !!obj->logfile; ++ ++ if (!visit_type_str(v, "volume", &obj->volume, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ if (!visit_type_SocketAddressList(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "debug", &obj->has_debug)) { ++ if (!visit_type_int(v, "debug", &obj->debug, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "logfile", &has_logfile)) { ++ if (!visit_type_str(v, "logfile", &obj->logfile, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsGluster(Visitor *v, const char *name, ++ BlockdevOptionsGluster **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsGluster), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsGluster_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsGluster(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsIoUring_members(Visitor *v, BlockdevOptionsIoUring *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsIoUring(Visitor *v, const char *name, ++ BlockdevOptionsIoUring **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsIoUring), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsIoUring_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsIoUring(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsNvmeIoUring_members(Visitor *v, BlockdevOptionsNvmeIoUring *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNvmeIoUring(Visitor *v, const char *name, ++ BlockdevOptionsNvmeIoUring **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNvmeIoUring), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNvmeIoUring_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNvmeIoUring(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVfioPci_members(Visitor *v, BlockdevOptionsVirtioBlkVfioPci *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsVirtioBlkVfioPci(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVfioPci **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsVirtioBlkVfioPci), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsVirtioBlkVfioPci_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsVirtioBlkVfioPci(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVhostUser_members(Visitor *v, BlockdevOptionsVirtioBlkVhostUser *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsVirtioBlkVhostUser(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVhostUser **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsVirtioBlkVhostUser), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsVirtioBlkVhostUser_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsVirtioBlkVhostUser(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++#if defined(CONFIG_BLKIO) ++bool visit_type_BlockdevOptionsVirtioBlkVhostVdpa_members(Visitor *v, BlockdevOptionsVirtioBlkVhostVdpa *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsVirtioBlkVhostVdpa(Visitor *v, const char *name, ++ BlockdevOptionsVirtioBlkVhostVdpa **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsVirtioBlkVhostVdpa), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsVirtioBlkVhostVdpa_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsVirtioBlkVhostVdpa(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_BLKIO) */ ++ ++bool visit_type_IscsiTransport(Visitor *v, const char *name, ++ IscsiTransport *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &IscsiTransport_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_IscsiHeaderDigest(Visitor *v, const char *name, ++ IscsiHeaderDigest *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &IscsiHeaderDigest_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsIscsi_members(Visitor *v, BlockdevOptionsIscsi *obj, Error **errp) ++{ ++ bool has_user = !!obj->user; ++ bool has_password_secret = !!obj->password_secret; ++ bool has_initiator_name = !!obj->initiator_name; ++ ++ if (!visit_type_IscsiTransport(v, "transport", &obj->transport, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "portal", &obj->portal, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "lun", &obj->has_lun)) { ++ if (!visit_type_int(v, "lun", &obj->lun, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "user", &has_user)) { ++ if (!visit_type_str(v, "user", &obj->user, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "password-secret", &has_password_secret)) { ++ if (!visit_type_str(v, "password-secret", &obj->password_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "initiator-name", &has_initiator_name)) { ++ if (!visit_type_str(v, "initiator-name", &obj->initiator_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "header-digest", &obj->has_header_digest)) { ++ if (!visit_type_IscsiHeaderDigest(v, "header-digest", &obj->header_digest, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "timeout", &obj->has_timeout)) { ++ if (!visit_type_int(v, "timeout", &obj->timeout, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsIscsi(Visitor *v, const char *name, ++ BlockdevOptionsIscsi **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsIscsi), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsIscsi_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsIscsi(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdAuthMode(Visitor *v, const char *name, ++ RbdAuthMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &RbdAuthMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_RbdImageEncryptionFormat(Visitor *v, const char *name, ++ RbdImageEncryptionFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &RbdImageEncryptionFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKSBase_members(Visitor *v, RbdEncryptionOptionsLUKSBase *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKSBase(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKSBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptionsLUKSBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptionsLUKSBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKSBase_members(Visitor *v, RbdEncryptionCreateOptionsLUKSBase *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, (RbdEncryptionOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cipher-alg", &obj->has_cipher_alg)) { ++ if (!visit_type_QCryptoCipherAlgorithm(v, "cipher-alg", &obj->cipher_alg, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKSBase(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKSBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionCreateOptionsLUKSBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionCreateOptionsLUKSBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionCreateOptionsLUKSBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKS_members(Visitor *v, RbdEncryptionOptionsLUKS *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, (RbdEncryptionOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKS(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKS2_members(Visitor *v, RbdEncryptionOptionsLUKS2 *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, (RbdEncryptionOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKS2(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKS2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptionsLUKS2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptionsLUKS2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptionsLUKS2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKSAny_members(Visitor *v, RbdEncryptionOptionsLUKSAny *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionOptionsLUKSBase_members(v, (RbdEncryptionOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptionsLUKSAny(Visitor *v, const char *name, ++ RbdEncryptionOptionsLUKSAny **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptionsLUKSAny), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptionsLUKSAny_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptionsLUKSAny(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS_members(Visitor *v, RbdEncryptionCreateOptionsLUKS *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionCreateOptionsLUKSBase_members(v, (RbdEncryptionCreateOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionCreateOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionCreateOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionCreateOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS2_members(Visitor *v, RbdEncryptionCreateOptionsLUKS2 *obj, Error **errp) ++{ ++ if (!visit_type_RbdEncryptionCreateOptionsLUKSBase_members(v, (RbdEncryptionCreateOptionsLUKSBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptionsLUKS2(Visitor *v, const char *name, ++ RbdEncryptionCreateOptionsLUKS2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionCreateOptionsLUKS2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionCreateOptionsLUKS2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionCreateOptionsLUKS2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_RbdEncryptionOptions_base_members(Visitor *v, q_obj_RbdEncryptionOptions_base *obj, Error **errp) ++{ ++ bool has_parent = !!obj->parent; ++ ++ if (!visit_type_RbdImageEncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "parent", &has_parent)) { ++ if (!visit_type_RbdEncryptionOptions(v, "parent", &obj->parent, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptions_members(Visitor *v, RbdEncryptionOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_RbdEncryptionOptions_base_members(v, (q_obj_RbdEncryptionOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: ++ return visit_type_RbdEncryptionOptionsLUKS_members(v, &obj->u.luks, errp); ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: ++ return visit_type_RbdEncryptionOptionsLUKS2_members(v, &obj->u.luks2, errp); ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: ++ return visit_type_RbdEncryptionOptionsLUKSAny_members(v, &obj->u.luks_any, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionOptions(Visitor *v, const char *name, ++ RbdEncryptionOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_RbdEncryptionCreateOptions_base_members(Visitor *v, q_obj_RbdEncryptionCreateOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_RbdImageEncryptionFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptions_members(Visitor *v, RbdEncryptionCreateOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_RbdEncryptionCreateOptions_base_members(v, (q_obj_RbdEncryptionCreateOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS: ++ return visit_type_RbdEncryptionCreateOptionsLUKS_members(v, &obj->u.luks, errp); ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2: ++ return visit_type_RbdEncryptionCreateOptionsLUKS2_members(v, &obj->u.luks2, errp); ++ case RBD_IMAGE_ENCRYPTION_FORMAT_LUKS_ANY: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_RbdEncryptionCreateOptions(Visitor *v, const char *name, ++ RbdEncryptionCreateOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(RbdEncryptionCreateOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_RbdEncryptionCreateOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdEncryptionCreateOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_RbdAuthModeList(Visitor *v, const char *name, ++ RbdAuthModeList **obj, Error **errp) ++{ ++ bool ok = false; ++ RbdAuthModeList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (RbdAuthModeList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_RbdAuthMode(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_RbdAuthModeList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsRbd_members(Visitor *v, BlockdevOptionsRbd *obj, Error **errp) ++{ ++ bool has_q_namespace = !!obj->q_namespace; ++ bool has_conf = !!obj->conf; ++ bool has_snapshot = !!obj->snapshot; ++ bool has_encrypt = !!obj->encrypt; ++ bool has_user = !!obj->user; ++ bool has_key_secret = !!obj->key_secret; ++ ++ if (!visit_type_str(v, "pool", &obj->pool, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "namespace", &has_q_namespace)) { ++ if (!visit_type_str(v, "namespace", &obj->q_namespace, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "image", &obj->image, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "conf", &has_conf)) { ++ if (!visit_type_str(v, "conf", &obj->conf, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "snapshot", &has_snapshot)) { ++ if (!visit_type_str(v, "snapshot", &obj->snapshot, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_RbdEncryptionOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "user", &has_user)) { ++ if (!visit_type_str(v, "user", &obj->user, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auth-client-required", &obj->has_auth_client_required)) { ++ if (!visit_type_RbdAuthModeList(v, "auth-client-required", &obj->auth_client_required, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "key-secret", &has_key_secret)) { ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "server", &obj->has_server)) { ++ if (!visit_type_InetSocketAddressBaseList(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsRbd(Visitor *v, const char *name, ++ BlockdevOptionsRbd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsRbd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsRbd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsRbd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++#if defined(CONFIG_REPLICATION) ++bool visit_type_ReplicationMode(Visitor *v, const char *name, ++ ReplicationMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &ReplicationMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++#if defined(CONFIG_REPLICATION) ++bool visit_type_BlockdevOptionsReplication_members(Visitor *v, BlockdevOptionsReplication *obj, Error **errp) ++{ ++ bool has_top_id = !!obj->top_id; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_ReplicationMode(v, "mode", &obj->mode, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "top-id", &has_top_id)) { ++ if (!visit_type_str(v, "top-id", &obj->top_id, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsReplication(Visitor *v, const char *name, ++ BlockdevOptionsReplication **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsReplication), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsReplication_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsReplication(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_REPLICATION) */ ++ ++bool visit_type_NFSTransport(Visitor *v, const char *name, ++ NFSTransport *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &NFSTransport_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_NFSServer_members(Visitor *v, NFSServer *obj, Error **errp) ++{ ++ if (!visit_type_NFSTransport(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "host", &obj->host, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_NFSServer(Visitor *v, const char *name, ++ NFSServer **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(NFSServer), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_NFSServer_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_NFSServer(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsNfs_members(Visitor *v, BlockdevOptionsNfs *obj, Error **errp) ++{ ++ if (!visit_type_NFSServer(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "user", &obj->has_user)) { ++ if (!visit_type_int(v, "user", &obj->user, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "group", &obj->has_group)) { ++ if (!visit_type_int(v, "group", &obj->group, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "tcp-syn-count", &obj->has_tcp_syn_count)) { ++ if (!visit_type_int(v, "tcp-syn-count", &obj->tcp_syn_count, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "readahead-size", &obj->has_readahead_size)) { ++ if (!visit_type_int(v, "readahead-size", &obj->readahead_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "page-cache-size", &obj->has_page_cache_size)) { ++ if (!visit_type_int(v, "page-cache-size", &obj->page_cache_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "debug", &obj->has_debug)) { ++ if (!visit_type_int(v, "debug", &obj->debug, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNfs(Visitor *v, const char *name, ++ BlockdevOptionsNfs **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNfs), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNfs_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNfs(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlBase_members(Visitor *v, BlockdevOptionsCurlBase *obj, Error **errp) ++{ ++ bool has_username = !!obj->username; ++ bool has_password_secret = !!obj->password_secret; ++ bool has_proxy_username = !!obj->proxy_username; ++ bool has_proxy_password_secret = !!obj->proxy_password_secret; ++ ++ if (!visit_type_str(v, "url", &obj->url, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "readahead", &obj->has_readahead)) { ++ if (!visit_type_int(v, "readahead", &obj->readahead, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "timeout", &obj->has_timeout)) { ++ if (!visit_type_int(v, "timeout", &obj->timeout, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "username", &has_username)) { ++ if (!visit_type_str(v, "username", &obj->username, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "password-secret", &has_password_secret)) { ++ if (!visit_type_str(v, "password-secret", &obj->password_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "proxy-username", &has_proxy_username)) { ++ if (!visit_type_str(v, "proxy-username", &obj->proxy_username, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "proxy-password-secret", &has_proxy_password_secret)) { ++ if (!visit_type_str(v, "proxy-password-secret", &obj->proxy_password_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlBase(Visitor *v, const char *name, ++ BlockdevOptionsCurlBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlHttp_members(Visitor *v, BlockdevOptionsCurlHttp *obj, Error **errp) ++{ ++ bool has_cookie = !!obj->cookie; ++ bool has_cookie_secret = !!obj->cookie_secret; ++ ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, (BlockdevOptionsCurlBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cookie", &has_cookie)) { ++ if (!visit_type_str(v, "cookie", &obj->cookie, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cookie-secret", &has_cookie_secret)) { ++ if (!visit_type_str(v, "cookie-secret", &obj->cookie_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlHttp(Visitor *v, const char *name, ++ BlockdevOptionsCurlHttp **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlHttp), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlHttp_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlHttp(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlHttps_members(Visitor *v, BlockdevOptionsCurlHttps *obj, Error **errp) ++{ ++ bool has_cookie = !!obj->cookie; ++ bool has_cookie_secret = !!obj->cookie_secret; ++ ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, (BlockdevOptionsCurlBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cookie", &has_cookie)) { ++ if (!visit_type_str(v, "cookie", &obj->cookie, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "sslverify", &obj->has_sslverify)) { ++ if (!visit_type_bool(v, "sslverify", &obj->sslverify, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cookie-secret", &has_cookie_secret)) { ++ if (!visit_type_str(v, "cookie-secret", &obj->cookie_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlHttps(Visitor *v, const char *name, ++ BlockdevOptionsCurlHttps **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlHttps), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlHttps_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlHttps(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlFtp_members(Visitor *v, BlockdevOptionsCurlFtp *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, (BlockdevOptionsCurlBase *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlFtp(Visitor *v, const char *name, ++ BlockdevOptionsCurlFtp **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlFtp), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlFtp_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlFtp(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCurlFtps_members(Visitor *v, BlockdevOptionsCurlFtps *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsCurlBase_members(v, (BlockdevOptionsCurlBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "sslverify", &obj->has_sslverify)) { ++ if (!visit_type_bool(v, "sslverify", &obj->sslverify, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCurlFtps(Visitor *v, const char *name, ++ BlockdevOptionsCurlFtps **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCurlFtps), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCurlFtps_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCurlFtps(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsNbd_members(Visitor *v, BlockdevOptionsNbd *obj, Error **errp) ++{ ++ bool has_export = !!obj->export; ++ bool has_tls_creds = !!obj->tls_creds; ++ bool has_tls_hostname = !!obj->tls_hostname; ++ bool has_x_dirty_bitmap = !!obj->x_dirty_bitmap; ++ ++ if (!visit_type_SocketAddress(v, "server", &obj->server, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "export", &has_export)) { ++ if (!visit_type_str(v, "export", &obj->export, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "tls-creds", &has_tls_creds)) { ++ if (!visit_type_str(v, "tls-creds", &obj->tls_creds, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "tls-hostname", &has_tls_hostname)) { ++ if (!visit_type_str(v, "tls-hostname", &obj->tls_hostname, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "x-dirty-bitmap", &has_x_dirty_bitmap)) { ++ if (visit_policy_reject(v, "x-dirty-bitmap", 1u << QAPI_UNSTABLE, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "x-dirty-bitmap", 1u << QAPI_UNSTABLE)) { ++ if (!visit_type_str(v, "x-dirty-bitmap", &obj->x_dirty_bitmap, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "reconnect-delay", &obj->has_reconnect_delay)) { ++ if (!visit_type_uint32(v, "reconnect-delay", &obj->reconnect_delay, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "open-timeout", &obj->has_open_timeout)) { ++ if (!visit_type_uint32(v, "open-timeout", &obj->open_timeout, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsNbd(Visitor *v, const char *name, ++ BlockdevOptionsNbd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsNbd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsNbd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsNbd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsRaw_members(Visitor *v, BlockdevOptionsRaw *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "offset", &obj->has_offset)) { ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "size", &obj->has_size)) { ++ if (!visit_type_int(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsRaw(Visitor *v, const char *name, ++ BlockdevOptionsRaw **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsRaw), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsRaw_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsRaw(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsThrottle_members(Visitor *v, BlockdevOptionsThrottle *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "throttle-group", &obj->throttle_group, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsThrottle(Visitor *v, const char *name, ++ BlockdevOptionsThrottle **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsThrottle), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsThrottle_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsThrottle(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCor_members(Visitor *v, BlockdevOptionsCor *obj, Error **errp) ++{ ++ bool has_bottom = !!obj->bottom; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bottom", &has_bottom)) { ++ if (!visit_type_str(v, "bottom", &obj->bottom, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCor(Visitor *v, const char *name, ++ BlockdevOptionsCor **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCor), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCor_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCor(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_OnCbwError(Visitor *v, const char *name, ++ OnCbwError *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &OnCbwError_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsCbw_members(Visitor *v, BlockdevOptionsCbw *obj, Error **errp) ++{ ++ bool has_bitmap = !!obj->bitmap; ++ ++ if (!visit_type_BlockdevOptionsGenericFormat_members(v, (BlockdevOptionsGenericFormat *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevRef(v, "target", &obj->target, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "bitmap", &has_bitmap)) { ++ if (!visit_type_BlockDirtyBitmap(v, "bitmap", &obj->bitmap, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "on-cbw-error", &obj->has_on_cbw_error)) { ++ if (!visit_type_OnCbwError(v, "on-cbw-error", &obj->on_cbw_error, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cbw-timeout", &obj->has_cbw_timeout)) { ++ if (!visit_type_uint32(v, "cbw-timeout", &obj->cbw_timeout, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptionsCbw(Visitor *v, const char *name, ++ BlockdevOptionsCbw **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptionsCbw), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptionsCbw_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsCbw(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevOptions_base_members(Visitor *v, q_obj_BlockdevOptions_base *obj, Error **errp) ++{ ++ bool has_node_name = !!obj->node_name; ++ bool has_cache = !!obj->cache; ++ ++ if (!visit_type_BlockdevDriver(v, "driver", &obj->driver, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "discard", &obj->has_discard)) { ++ if (!visit_type_BlockdevDiscardOptions(v, "discard", &obj->discard, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cache", &has_cache)) { ++ if (!visit_type_BlockdevCacheOptions(v, "cache", &obj->cache, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "read-only", &obj->has_read_only)) { ++ if (!visit_type_bool(v, "read-only", &obj->read_only, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "auto-read-only", &obj->has_auto_read_only)) { ++ if (!visit_type_bool(v, "auto-read-only", &obj->auto_read_only, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "force-share", &obj->has_force_share)) { ++ if (!visit_type_bool(v, "force-share", &obj->force_share, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "detect-zeroes", &obj->has_detect_zeroes)) { ++ if (!visit_type_BlockdevDetectZeroesOptions(v, "detect-zeroes", &obj->detect_zeroes, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevOptions_base_members(v, (q_obj_BlockdevOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->driver) { ++ case BLOCKDEV_DRIVER_BLKDEBUG: ++ return visit_type_BlockdevOptionsBlkdebug_members(v, &obj->u.blkdebug, errp); ++ case BLOCKDEV_DRIVER_BLKLOGWRITES: ++ return visit_type_BlockdevOptionsBlklogwrites_members(v, &obj->u.blklogwrites, errp); ++ case BLOCKDEV_DRIVER_BLKVERIFY: ++ return visit_type_BlockdevOptionsBlkverify_members(v, &obj->u.blkverify, errp); ++ case BLOCKDEV_DRIVER_BLKREPLAY: ++ return visit_type_BlockdevOptionsBlkreplay_members(v, &obj->u.blkreplay, errp); ++ case BLOCKDEV_DRIVER_BOCHS: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.bochs, errp); ++ case BLOCKDEV_DRIVER_CLOOP: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.cloop, errp); ++ case BLOCKDEV_DRIVER_COMPRESS: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.compress, errp); ++ case BLOCKDEV_DRIVER_COPY_BEFORE_WRITE: ++ return visit_type_BlockdevOptionsCbw_members(v, &obj->u.copy_before_write, errp); ++ case BLOCKDEV_DRIVER_COPY_ON_READ: ++ return visit_type_BlockdevOptionsCor_members(v, &obj->u.copy_on_read, errp); ++ case BLOCKDEV_DRIVER_DMG: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.dmg, errp); ++ case BLOCKDEV_DRIVER_FILE: ++ return visit_type_BlockdevOptionsFile_members(v, &obj->u.file, errp); ++ case BLOCKDEV_DRIVER_FTP: ++ return visit_type_BlockdevOptionsCurlFtp_members(v, &obj->u.ftp, errp); ++ case BLOCKDEV_DRIVER_FTPS: ++ return visit_type_BlockdevOptionsCurlFtps_members(v, &obj->u.ftps, errp); ++ case BLOCKDEV_DRIVER_GLUSTER: ++ return visit_type_BlockdevOptionsGluster_members(v, &obj->u.gluster, errp); ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_CDROM: ++ return visit_type_BlockdevOptionsFile_members(v, &obj->u.host_cdrom, errp); ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_DEVICE: ++ return visit_type_BlockdevOptionsFile_members(v, &obj->u.host_device, errp); ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_HTTP: ++ return visit_type_BlockdevOptionsCurlHttp_members(v, &obj->u.http, errp); ++ case BLOCKDEV_DRIVER_HTTPS: ++ return visit_type_BlockdevOptionsCurlHttps_members(v, &obj->u.https, errp); ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_IO_URING: ++ return visit_type_BlockdevOptionsIoUring_members(v, &obj->u.io_uring, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_ISCSI: ++ return visit_type_BlockdevOptionsIscsi_members(v, &obj->u.iscsi, errp); ++ case BLOCKDEV_DRIVER_LUKS: ++ return visit_type_BlockdevOptionsLUKS_members(v, &obj->u.luks, errp); ++ case BLOCKDEV_DRIVER_NBD: ++ return visit_type_BlockdevOptionsNbd_members(v, &obj->u.nbd, errp); ++ case BLOCKDEV_DRIVER_NFS: ++ return visit_type_BlockdevOptionsNfs_members(v, &obj->u.nfs, errp); ++ case BLOCKDEV_DRIVER_NULL_AIO: ++ return visit_type_BlockdevOptionsNull_members(v, &obj->u.null_aio, errp); ++ case BLOCKDEV_DRIVER_NULL_CO: ++ return visit_type_BlockdevOptionsNull_members(v, &obj->u.null_co, errp); ++ case BLOCKDEV_DRIVER_NVME: ++ return visit_type_BlockdevOptionsNVMe_members(v, &obj->u.nvme, errp); ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_NVME_IO_URING: ++ return visit_type_BlockdevOptionsNvmeIoUring_members(v, &obj->u.nvme_io_uring, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_PARALLELS: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.parallels, errp); ++ case BLOCKDEV_DRIVER_PREALLOCATE: ++ return visit_type_BlockdevOptionsPreallocate_members(v, &obj->u.preallocate, errp); ++ case BLOCKDEV_DRIVER_QCOW2: ++ return visit_type_BlockdevOptionsQcow2_members(v, &obj->u.qcow2, errp); ++ case BLOCKDEV_DRIVER_QCOW: ++ return visit_type_BlockdevOptionsQcow_members(v, &obj->u.qcow, errp); ++ case BLOCKDEV_DRIVER_QED: ++ return visit_type_BlockdevOptionsGenericCOWFormat_members(v, &obj->u.qed, errp); ++ case BLOCKDEV_DRIVER_QUORUM: ++ return visit_type_BlockdevOptionsQuorum_members(v, &obj->u.quorum, errp); ++ case BLOCKDEV_DRIVER_RAW: ++ return visit_type_BlockdevOptionsRaw_members(v, &obj->u.raw, errp); ++ case BLOCKDEV_DRIVER_RBD: ++ return visit_type_BlockdevOptionsRbd_members(v, &obj->u.rbd, errp); ++#if defined(CONFIG_REPLICATION) ++ case BLOCKDEV_DRIVER_REPLICATION: ++ return visit_type_BlockdevOptionsReplication_members(v, &obj->u.replication, errp); ++#endif /* defined(CONFIG_REPLICATION) */ ++ case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.snapshot_access, errp); ++ case BLOCKDEV_DRIVER_SSH: ++ return visit_type_BlockdevOptionsSsh_members(v, &obj->u.ssh, errp); ++ case BLOCKDEV_DRIVER_THROTTLE: ++ return visit_type_BlockdevOptionsThrottle_members(v, &obj->u.throttle, errp); ++ case BLOCKDEV_DRIVER_VDI: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.vdi, errp); ++ case BLOCKDEV_DRIVER_VHDX: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.vhdx, errp); ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI: ++ return visit_type_BlockdevOptionsVirtioBlkVfioPci_members(v, &obj->u.virtio_blk_vfio_pci, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER: ++ return visit_type_BlockdevOptionsVirtioBlkVhostUser_members(v, &obj->u.virtio_blk_vhost_user, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA: ++ return visit_type_BlockdevOptionsVirtioBlkVhostVdpa_members(v, &obj->u.virtio_blk_vhost_vdpa, errp); ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_VMDK: ++ return visit_type_BlockdevOptionsGenericCOWFormat_members(v, &obj->u.vmdk, errp); ++ case BLOCKDEV_DRIVER_VPC: ++ return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.vpc, errp); ++ case BLOCKDEV_DRIVER_VVFAT: ++ return visit_type_BlockdevOptionsVVFAT_members(v, &obj->u.vvfat, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevOptions(Visitor *v, const char *name, ++ BlockdevOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevRef(Visitor *v, const char *name, ++ BlockdevRef **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QDICT: ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ break; ++ } ++ if (visit_type_BlockdevOptions_members(v, &(*obj)->u.definition, errp)) { ++ ok = visit_check_struct(v, errp); ++ } ++ visit_end_struct(v, NULL); ++ break; ++ case QTYPE_QSTRING: ++ ok = visit_type_str(v, name, &(*obj)->u.reference, errp); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: BlockdevRef", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_BlockdevRef() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevRef(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevRefOrNull(Visitor *v, const char *name, ++ BlockdevRefOrNull **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QDICT: ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ break; ++ } ++ if (visit_type_BlockdevOptions_members(v, &(*obj)->u.definition, errp)) { ++ ok = visit_check_struct(v, errp); ++ } ++ visit_end_struct(v, NULL); ++ break; ++ case QTYPE_QSTRING: ++ ok = visit_type_str(v, name, &(*obj)->u.reference, errp); ++ break; ++ case QTYPE_QNULL: ++ ok = visit_type_null(v, name, &(*obj)->u.null, errp); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: BlockdevRefOrNull", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_BlockdevRefOrNull() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevRefOrNull(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevOptionsList(Visitor *v, const char *name, ++ BlockdevOptionsList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockdevOptionsList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockdevOptionsList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockdevOptions(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevOptionsList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_blockdev_reopen_arg_members(Visitor *v, q_obj_blockdev_reopen_arg *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsList(v, "options", &obj->options, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_blockdev_del_arg_members(Visitor *v, q_obj_blockdev_del_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsFile_members(Visitor *v, BlockdevCreateOptionsFile *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "filename", &obj->filename, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "nocow", &obj->has_nocow)) { ++ if (!visit_type_bool(v, "nocow", &obj->nocow, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "extent-size-hint", &obj->has_extent_size_hint)) { ++ if (!visit_type_size(v, "extent-size-hint", &obj->extent_size_hint, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsFile(Visitor *v, const char *name, ++ BlockdevCreateOptionsFile **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsFile), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsFile_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsFile(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsGluster_members(Visitor *v, BlockdevCreateOptionsGluster *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsGluster(v, "location", &obj->location, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsGluster(Visitor *v, const char *name, ++ BlockdevCreateOptionsGluster **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsGluster), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsGluster_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsGluster(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsLUKS_members(Visitor *v, BlockdevCreateOptionsLUKS *obj, Error **errp) ++{ ++ bool has_file = !!obj->file; ++ bool has_header = !!obj->header; ++ ++ if (!visit_type_QCryptoBlockCreateOptionsLUKS_members(v, (QCryptoBlockCreateOptionsLUKS *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "file", &has_file)) { ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "header", &has_header)) { ++ if (!visit_type_BlockdevRef(v, "header", &obj->header, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsLUKS(Visitor *v, const char *name, ++ BlockdevCreateOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsNfs_members(Visitor *v, BlockdevCreateOptionsNfs *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsNfs(v, "location", &obj->location, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsNfs(Visitor *v, const char *name, ++ BlockdevCreateOptionsNfs **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsNfs), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsNfs_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsNfs(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsParallels_members(Visitor *v, BlockdevCreateOptionsParallels *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsParallels(Visitor *v, const char *name, ++ BlockdevCreateOptionsParallels **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsParallels), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsParallels_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsParallels(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsQcow_members(Visitor *v, BlockdevCreateOptionsQcow *obj, Error **errp) ++{ ++ bool has_backing_file = !!obj->backing_file; ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_QCryptoBlockCreateOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsQcow(Visitor *v, const char *name, ++ BlockdevCreateOptionsQcow **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsQcow), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsQcow_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsQcow(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevQcow2Version(Visitor *v, const char *name, ++ BlockdevQcow2Version *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevQcow2Version_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_Qcow2CompressionType(Visitor *v, const char *name, ++ Qcow2CompressionType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &Qcow2CompressionType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsQcow2_members(Visitor *v, BlockdevCreateOptionsQcow2 *obj, Error **errp) ++{ ++ bool has_data_file = !!obj->data_file; ++ bool has_backing_file = !!obj->backing_file; ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "data-file", &has_data_file)) { ++ if (!visit_type_BlockdevRef(v, "data-file", &obj->data_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "data-file-raw", &obj->has_data_file_raw)) { ++ if (!visit_type_bool(v, "data-file-raw", &obj->data_file_raw, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "extended-l2", &obj->has_extended_l2)) { ++ if (!visit_type_bool(v, "extended-l2", &obj->extended_l2, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "version", &obj->has_version)) { ++ if (!visit_type_BlockdevQcow2Version(v, "version", &obj->version, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-fmt", &obj->has_backing_fmt)) { ++ if (!visit_type_BlockdevDriver(v, "backing-fmt", &obj->backing_fmt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_QCryptoBlockCreateOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "lazy-refcounts", &obj->has_lazy_refcounts)) { ++ if (!visit_type_bool(v, "lazy-refcounts", &obj->lazy_refcounts, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "refcount-bits", &obj->has_refcount_bits)) { ++ if (!visit_type_int(v, "refcount-bits", &obj->refcount_bits, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "compression-type", &obj->has_compression_type)) { ++ if (!visit_type_Qcow2CompressionType(v, "compression-type", &obj->compression_type, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsQcow2(Visitor *v, const char *name, ++ BlockdevCreateOptionsQcow2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsQcow2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsQcow2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsQcow2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsQed_members(Visitor *v, BlockdevCreateOptionsQed *obj, Error **errp) ++{ ++ bool has_backing_file = !!obj->backing_file; ++ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-fmt", &obj->has_backing_fmt)) { ++ if (!visit_type_BlockdevDriver(v, "backing-fmt", &obj->backing_fmt, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "table-size", &obj->has_table_size)) { ++ if (!visit_type_int(v, "table-size", &obj->table_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsQed(Visitor *v, const char *name, ++ BlockdevCreateOptionsQed **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsQed), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsQed_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsQed(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsRbd_members(Visitor *v, BlockdevCreateOptionsRbd *obj, Error **errp) ++{ ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (!visit_type_BlockdevOptionsRbd(v, "location", &obj->location, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { ++ if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_RbdEncryptionCreateOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsRbd(Visitor *v, const char *name, ++ BlockdevCreateOptionsRbd **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsRbd), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsRbd_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsRbd(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevVmdkSubformat(Visitor *v, const char *name, ++ BlockdevVmdkSubformat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevVmdkSubformat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevVmdkAdapterType(Visitor *v, const char *name, ++ BlockdevVmdkAdapterType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevVmdkAdapterType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsVmdk_members(Visitor *v, BlockdevCreateOptionsVmdk *obj, Error **errp) ++{ ++ bool has_backing_file = !!obj->backing_file; ++ bool has_hwversion = !!obj->hwversion; ++ bool has_toolsversion = !!obj->toolsversion; ++ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "extents", &obj->has_extents)) { ++ if (!visit_type_BlockdevRefList(v, "extents", &obj->extents, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "subformat", &obj->has_subformat)) { ++ if (!visit_type_BlockdevVmdkSubformat(v, "subformat", &obj->subformat, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "backing-file", &has_backing_file)) { ++ if (!visit_type_str(v, "backing-file", &obj->backing_file, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "adapter-type", &obj->has_adapter_type)) { ++ if (!visit_type_BlockdevVmdkAdapterType(v, "adapter-type", &obj->adapter_type, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "hwversion", &has_hwversion)) { ++ if (!visit_type_str(v, "hwversion", &obj->hwversion, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "toolsversion", &has_toolsversion)) { ++ if (!visit_type_str(v, "toolsversion", &obj->toolsversion, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "zeroed-grain", &obj->has_zeroed_grain)) { ++ if (!visit_type_bool(v, "zeroed-grain", &obj->zeroed_grain, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsVmdk(Visitor *v, const char *name, ++ BlockdevCreateOptionsVmdk **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsVmdk), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsVmdk_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsVmdk(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsSsh_members(Visitor *v, BlockdevCreateOptionsSsh *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevOptionsSsh(v, "location", &obj->location, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsSsh(Visitor *v, const char *name, ++ BlockdevCreateOptionsSsh **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsSsh), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsSsh_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsSsh(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsVdi_members(Visitor *v, BlockdevCreateOptionsVdi *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "preallocation", &obj->has_preallocation)) { ++ if (!visit_type_PreallocMode(v, "preallocation", &obj->preallocation, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsVdi(Visitor *v, const char *name, ++ BlockdevCreateOptionsVdi **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsVdi), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsVdi_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsVdi(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevVhdxSubformat(Visitor *v, const char *name, ++ BlockdevVhdxSubformat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevVhdxSubformat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsVhdx_members(Visitor *v, BlockdevCreateOptionsVhdx *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "log-size", &obj->has_log_size)) { ++ if (!visit_type_size(v, "log-size", &obj->log_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "block-size", &obj->has_block_size)) { ++ if (!visit_type_size(v, "block-size", &obj->block_size, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "subformat", &obj->has_subformat)) { ++ if (!visit_type_BlockdevVhdxSubformat(v, "subformat", &obj->subformat, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "block-state-zero", &obj->has_block_state_zero)) { ++ if (!visit_type_bool(v, "block-state-zero", &obj->block_state_zero, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsVhdx(Visitor *v, const char *name, ++ BlockdevCreateOptionsVhdx **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsVhdx), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsVhdx_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsVhdx(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevVpcSubformat(Visitor *v, const char *name, ++ BlockdevVpcSubformat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockdevVpcSubformat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_BlockdevCreateOptionsVpc_members(Visitor *v, BlockdevCreateOptionsVpc *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ if (!visit_type_size(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "subformat", &obj->has_subformat)) { ++ if (!visit_type_BlockdevVpcSubformat(v, "subformat", &obj->subformat, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "force-size", &obj->has_force_size)) { ++ if (!visit_type_bool(v, "force-size", &obj->force_size, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptionsVpc(Visitor *v, const char *name, ++ BlockdevCreateOptionsVpc **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptionsVpc), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptionsVpc_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptionsVpc(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevCreateOptions_base_members(Visitor *v, q_obj_BlockdevCreateOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevDriver(v, "driver", &obj->driver, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevCreateOptions_base_members(v, (q_obj_BlockdevCreateOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->driver) { ++ case BLOCKDEV_DRIVER_FILE: ++ return visit_type_BlockdevCreateOptionsFile_members(v, &obj->u.file, errp); ++ case BLOCKDEV_DRIVER_GLUSTER: ++ return visit_type_BlockdevCreateOptionsGluster_members(v, &obj->u.gluster, errp); ++ case BLOCKDEV_DRIVER_LUKS: ++ return visit_type_BlockdevCreateOptionsLUKS_members(v, &obj->u.luks, errp); ++ case BLOCKDEV_DRIVER_NFS: ++ return visit_type_BlockdevCreateOptionsNfs_members(v, &obj->u.nfs, errp); ++ case BLOCKDEV_DRIVER_PARALLELS: ++ return visit_type_BlockdevCreateOptionsParallels_members(v, &obj->u.parallels, errp); ++ case BLOCKDEV_DRIVER_QCOW: ++ return visit_type_BlockdevCreateOptionsQcow_members(v, &obj->u.qcow, errp); ++ case BLOCKDEV_DRIVER_QCOW2: ++ return visit_type_BlockdevCreateOptionsQcow2_members(v, &obj->u.qcow2, errp); ++ case BLOCKDEV_DRIVER_QED: ++ return visit_type_BlockdevCreateOptionsQed_members(v, &obj->u.qed, errp); ++ case BLOCKDEV_DRIVER_RBD: ++ return visit_type_BlockdevCreateOptionsRbd_members(v, &obj->u.rbd, errp); ++ case BLOCKDEV_DRIVER_SSH: ++ return visit_type_BlockdevCreateOptionsSsh_members(v, &obj->u.ssh, errp); ++ case BLOCKDEV_DRIVER_VDI: ++ return visit_type_BlockdevCreateOptionsVdi_members(v, &obj->u.vdi, errp); ++ case BLOCKDEV_DRIVER_VHDX: ++ return visit_type_BlockdevCreateOptionsVhdx_members(v, &obj->u.vhdx, errp); ++ case BLOCKDEV_DRIVER_VMDK: ++ return visit_type_BlockdevCreateOptionsVmdk_members(v, &obj->u.vmdk, errp); ++ case BLOCKDEV_DRIVER_VPC: ++ return visit_type_BlockdevCreateOptionsVpc_members(v, &obj->u.vpc, errp); ++ case BLOCKDEV_DRIVER_BLKDEBUG: ++ break; ++ case BLOCKDEV_DRIVER_BLKLOGWRITES: ++ break; ++ case BLOCKDEV_DRIVER_BLKREPLAY: ++ break; ++ case BLOCKDEV_DRIVER_BLKVERIFY: ++ break; ++ case BLOCKDEV_DRIVER_BOCHS: ++ break; ++ case BLOCKDEV_DRIVER_CLOOP: ++ break; ++ case BLOCKDEV_DRIVER_COMPRESS: ++ break; ++ case BLOCKDEV_DRIVER_COPY_BEFORE_WRITE: ++ break; ++ case BLOCKDEV_DRIVER_COPY_ON_READ: ++ break; ++ case BLOCKDEV_DRIVER_DMG: ++ break; ++ case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: ++ break; ++ case BLOCKDEV_DRIVER_FTP: ++ break; ++ case BLOCKDEV_DRIVER_FTPS: ++ break; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_CDROM: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_DEVICE: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_HTTP: ++ break; ++ case BLOCKDEV_DRIVER_HTTPS: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_ISCSI: ++ break; ++ case BLOCKDEV_DRIVER_NBD: ++ break; ++ case BLOCKDEV_DRIVER_NULL_AIO: ++ break; ++ case BLOCKDEV_DRIVER_NULL_CO: ++ break; ++ case BLOCKDEV_DRIVER_NVME: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_NVME_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_PREALLOCATE: ++ break; ++ case BLOCKDEV_DRIVER_QUORUM: ++ break; ++ case BLOCKDEV_DRIVER_RAW: ++ break; ++#if defined(CONFIG_REPLICATION) ++ case BLOCKDEV_DRIVER_REPLICATION: ++ break; ++#endif /* defined(CONFIG_REPLICATION) */ ++ case BLOCKDEV_DRIVER_THROTTLE: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_VVFAT: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevCreateOptions(Visitor *v, const char *name, ++ BlockdevCreateOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevCreateOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevCreateOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevCreateOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_blockdev_create_arg_members(Visitor *v, q_obj_blockdev_create_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevCreateOptions(v, "options", &obj->options, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptionsLUKS_members(Visitor *v, BlockdevAmendOptionsLUKS *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockAmendOptionsLUKS_members(v, (QCryptoBlockAmendOptionsLUKS *)obj, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptionsLUKS(Visitor *v, const char *name, ++ BlockdevAmendOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevAmendOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevAmendOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevAmendOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_BlockdevAmendOptionsQcow2_members(Visitor *v, BlockdevAmendOptionsQcow2 *obj, Error **errp) ++{ ++ bool has_encrypt = !!obj->encrypt; ++ ++ if (visit_optional(v, "encrypt", &has_encrypt)) { ++ if (!visit_type_QCryptoBlockAmendOptions(v, "encrypt", &obj->encrypt, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptionsQcow2(Visitor *v, const char *name, ++ BlockdevAmendOptionsQcow2 **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevAmendOptionsQcow2), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevAmendOptionsQcow2_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevAmendOptionsQcow2(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_BlockdevAmendOptions_base_members(Visitor *v, q_obj_BlockdevAmendOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_BlockdevDriver(v, "driver", &obj->driver, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_BlockdevAmendOptions_base_members(v, (q_obj_BlockdevAmendOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->driver) { ++ case BLOCKDEV_DRIVER_LUKS: ++ return visit_type_BlockdevAmendOptionsLUKS_members(v, &obj->u.luks, errp); ++ case BLOCKDEV_DRIVER_QCOW2: ++ return visit_type_BlockdevAmendOptionsQcow2_members(v, &obj->u.qcow2, errp); ++ case BLOCKDEV_DRIVER_BLKDEBUG: ++ break; ++ case BLOCKDEV_DRIVER_BLKLOGWRITES: ++ break; ++ case BLOCKDEV_DRIVER_BLKREPLAY: ++ break; ++ case BLOCKDEV_DRIVER_BLKVERIFY: ++ break; ++ case BLOCKDEV_DRIVER_BOCHS: ++ break; ++ case BLOCKDEV_DRIVER_CLOOP: ++ break; ++ case BLOCKDEV_DRIVER_COMPRESS: ++ break; ++ case BLOCKDEV_DRIVER_COPY_BEFORE_WRITE: ++ break; ++ case BLOCKDEV_DRIVER_COPY_ON_READ: ++ break; ++ case BLOCKDEV_DRIVER_DMG: ++ break; ++ case BLOCKDEV_DRIVER_FILE: ++ break; ++ case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: ++ break; ++ case BLOCKDEV_DRIVER_FTP: ++ break; ++ case BLOCKDEV_DRIVER_FTPS: ++ break; ++ case BLOCKDEV_DRIVER_GLUSTER: ++ break; ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_CDROM: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if defined(HAVE_HOST_BLOCK_DEVICE) ++ case BLOCKDEV_DRIVER_HOST_DEVICE: ++ break; ++#endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++ case BLOCKDEV_DRIVER_HTTP: ++ break; ++ case BLOCKDEV_DRIVER_HTTPS: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_ISCSI: ++ break; ++ case BLOCKDEV_DRIVER_NBD: ++ break; ++ case BLOCKDEV_DRIVER_NFS: ++ break; ++ case BLOCKDEV_DRIVER_NULL_AIO: ++ break; ++ case BLOCKDEV_DRIVER_NULL_CO: ++ break; ++ case BLOCKDEV_DRIVER_NVME: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_NVME_IO_URING: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_PARALLELS: ++ break; ++ case BLOCKDEV_DRIVER_PREALLOCATE: ++ break; ++ case BLOCKDEV_DRIVER_QCOW: ++ break; ++ case BLOCKDEV_DRIVER_QED: ++ break; ++ case BLOCKDEV_DRIVER_QUORUM: ++ break; ++ case BLOCKDEV_DRIVER_RAW: ++ break; ++ case BLOCKDEV_DRIVER_RBD: ++ break; ++#if defined(CONFIG_REPLICATION) ++ case BLOCKDEV_DRIVER_REPLICATION: ++ break; ++#endif /* defined(CONFIG_REPLICATION) */ ++ case BLOCKDEV_DRIVER_SSH: ++ break; ++ case BLOCKDEV_DRIVER_THROTTLE: ++ break; ++ case BLOCKDEV_DRIVER_VDI: ++ break; ++ case BLOCKDEV_DRIVER_VHDX: ++ break; ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VFIO_PCI: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_USER: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++#if defined(CONFIG_BLKIO) ++ case BLOCKDEV_DRIVER_VIRTIO_BLK_VHOST_VDPA: ++ break; ++#endif /* defined(CONFIG_BLKIO) */ ++ case BLOCKDEV_DRIVER_VMDK: ++ break; ++ case BLOCKDEV_DRIVER_VPC: ++ break; ++ case BLOCKDEV_DRIVER_VVFAT: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevAmendOptions(Visitor *v, const char *name, ++ BlockdevAmendOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevAmendOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevAmendOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevAmendOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_x_blockdev_amend_arg_members(Visitor *v, q_obj_x_blockdev_amend_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockdevAmendOptions(v, "options", &obj->options, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "force", &obj->has_force)) { ++ if (!visit_type_bool(v, "force", &obj->force, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockErrorAction(Visitor *v, const char *name, ++ BlockErrorAction *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &BlockErrorAction_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_BLOCK_IMAGE_CORRUPTED_arg_members(Visitor *v, q_obj_BLOCK_IMAGE_CORRUPTED_arg *obj, Error **errp) ++{ ++ bool has_node_name = !!obj->node_name; ++ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "msg", &obj->msg, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "offset", &obj->has_offset)) { ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "size", &obj->has_size)) { ++ if (!visit_type_int(v, "size", &obj->size, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_bool(v, "fatal", &obj->fatal, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_IO_ERROR_arg_members(Visitor *v, q_obj_BLOCK_IO_ERROR_arg *obj, Error **errp) ++{ ++ bool has_node_name = !!obj->node_name; ++ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "node-name", &has_node_name)) { ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_IoOperationType(v, "operation", &obj->operation, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockErrorAction(v, "action", &obj->action, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "nospace", &obj->has_nospace)) { ++ if (!visit_type_bool(v, "nospace", &obj->nospace, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "reason", &obj->reason, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_COMPLETED_arg_members(Visitor *v, q_obj_BLOCK_JOB_COMPLETED_arg *obj, Error **errp) ++{ ++ bool has_error = !!obj->error; ++ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "len", &obj->len, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "error", &has_error)) { ++ if (!visit_type_str(v, "error", &obj->error, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_CANCELLED_arg_members(Visitor *v, q_obj_BLOCK_JOB_CANCELLED_arg *obj, Error **errp) ++{ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "len", &obj->len, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_ERROR_arg_members(Visitor *v, q_obj_BLOCK_JOB_ERROR_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_IoOperationType(v, "operation", &obj->operation, errp)) { ++ return false; ++ } ++ if (!visit_type_BlockErrorAction(v, "action", &obj->action, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_READY_arg_members(Visitor *v, q_obj_BLOCK_JOB_READY_arg *obj, Error **errp) ++{ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "len", &obj->len, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "offset", &obj->offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "speed", &obj->speed, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_BLOCK_JOB_PENDING_arg_members(Visitor *v, q_obj_BLOCK_JOB_PENDING_arg *obj, Error **errp) ++{ ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_PreallocMode(Visitor *v, const char *name, ++ PreallocMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &PreallocMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_BLOCK_WRITE_THRESHOLD_arg_members(Visitor *v, q_obj_BLOCK_WRITE_THRESHOLD_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "amount-exceeded", &obj->amount_exceeded, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "write-threshold", &obj->write_threshold, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_block_set_write_threshold_arg_members(Visitor *v, q_obj_block_set_write_threshold_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_uint64(v, "write-threshold", &obj->write_threshold, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_x_blockdev_change_arg_members(Visitor *v, q_obj_x_blockdev_change_arg *obj, Error **errp) ++{ ++ bool has_child = !!obj->child; ++ bool has_node = !!obj->node; ++ ++ if (!visit_type_str(v, "parent", &obj->parent, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "child", &has_child)) { ++ if (!visit_type_str(v, "child", &obj->child, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "node", &has_node)) { ++ if (!visit_type_str(v, "node", &obj->node, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_x_blockdev_set_iothread_arg_members(Visitor *v, q_obj_x_blockdev_set_iothread_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_StrOrNull(v, "iothread", &obj->iothread, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "force", &obj->has_force)) { ++ if (!visit_type_bool(v, "force", &obj->force, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QuorumOpType(Visitor *v, const char *name, ++ QuorumOpType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QuorumOpType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_QUORUM_FAILURE_arg_members(Visitor *v, q_obj_QUORUM_FAILURE_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "reference", &obj->reference, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "sector-num", &obj->sector_num, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "sectors-count", &obj->sectors_count, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_QUORUM_REPORT_BAD_arg_members(Visitor *v, q_obj_QUORUM_REPORT_BAD_arg *obj, Error **errp) ++{ ++ bool has_error = !!obj->error; ++ ++ if (!visit_type_QuorumOpType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "error", &has_error)) { ++ if (!visit_type_str(v, "error", &obj->error, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "sector-num", &obj->sector_num, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "sectors-count", &obj->sectors_count, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevSnapshotInternal_members(Visitor *v, BlockdevSnapshotInternal *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_BlockdevSnapshotInternal(Visitor *v, const char *name, ++ BlockdevSnapshotInternal **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(BlockdevSnapshotInternal), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_BlockdevSnapshotInternal_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockdevSnapshotInternal(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_blockdev_snapshot_delete_internal_sync_arg_members(Visitor *v, q_obj_blockdev_snapshot_delete_internal_sync_arg *obj, Error **errp) ++{ ++ bool has_id = !!obj->id; ++ bool has_name = !!obj->name; ++ ++ if (!visit_type_str(v, "device", &obj->device, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "id", &has_id)) { ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "name", &has_name)) { ++ if (!visit_type_str(v, "name", &obj->name, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_BlockGraphInfoList(Visitor *v, const char *name, ++ BlockGraphInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ BlockGraphInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (BlockGraphInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_BlockGraphInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_BlockGraphInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_DummyBlockCoreForceArrays_members(Visitor *v, DummyBlockCoreForceArrays *obj, Error **errp) ++{ ++ if (!visit_type_BlockGraphInfoList(v, "unused-block-graph-info", &obj->unused_block_graph_info, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_DummyBlockCoreForceArrays(Visitor *v, const char *name, ++ DummyBlockCoreForceArrays **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(DummyBlockCoreForceArrays), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_DummyBlockCoreForceArrays_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_DummyBlockCoreForceArrays(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_block_core_c; +diff --git a/qcow2/lib/qapi/qapi-visit-common.c b/qcow2/lib/qapi/qapi-visit-common.c +new file mode 100644 +index 00000000..cacb730a +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-common.c +@@ -0,0 +1,176 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-common.h" ++ ++bool visit_type_IoOperationType(Visitor *v, const char *name, ++ IoOperationType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &IoOperationType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_OnOffAuto(Visitor *v, const char *name, ++ OnOffAuto *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &OnOffAuto_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_OnOffSplit(Visitor *v, const char *name, ++ OnOffSplit *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &OnOffSplit_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_StrOrNull(Visitor *v, const char *name, ++ StrOrNull **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_alternate(v, name, (GenericAlternate **)obj, ++ sizeof(**obj), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ switch ((*obj)->type) { ++ case QTYPE_QSTRING: ++ ok = visit_type_str(v, name, &(*obj)->u.s, errp); ++ break; ++ case QTYPE_QNULL: ++ ok = visit_type_null(v, name, &(*obj)->u.n, errp); ++ break; ++ case QTYPE_NONE: ++ abort(); ++ default: ++ assert(visit_is_input(v)); ++ error_setg(errp, ++ "Invalid parameter type for '%s', expected: StrOrNull", ++ name ? name : "null"); ++ /* Avoid passing invalid *obj to qapi_free_StrOrNull() */ ++ g_free(*obj); ++ *obj = NULL; ++ } ++out_obj: ++ visit_end_alternate(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_StrOrNull(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_OffAutoPCIBAR(Visitor *v, const char *name, ++ OffAutoPCIBAR *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &OffAutoPCIBAR_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_PCIELinkSpeed(Visitor *v, const char *name, ++ PCIELinkSpeed *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &PCIELinkSpeed_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_PCIELinkWidth(Visitor *v, const char *name, ++ PCIELinkWidth *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &PCIELinkWidth_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_HostMemPolicy(Visitor *v, const char *name, ++ HostMemPolicy *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &HostMemPolicy_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_NetFilterDirection(Visitor *v, const char *name, ++ NetFilterDirection *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &NetFilterDirection_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_GrabToggleKeys(Visitor *v, const char *name, ++ GrabToggleKeys *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &GrabToggleKeys_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_HumanReadableText_members(Visitor *v, HumanReadableText *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "human-readable-text", &obj->human_readable_text, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_HumanReadableText(Visitor *v, const char *name, ++ HumanReadableText **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(HumanReadableText), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_HumanReadableText_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_HumanReadableText(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_common_c; +diff --git a/qcow2/lib/qapi/qapi-visit-core.c b/qcow2/lib/qapi/qapi-visit-core.c +new file mode 100644 +index 00000000..6c13510a +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-core.c +@@ -0,0 +1,444 @@ ++/* ++ * Core Definitions for QAPI Visitor Classes ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/visitor.h" ++#include "qapi/visitor-impl.h" ++#include "trace.h" ++ ++/* Zero-initialization must result in default policy */ ++QEMU_BUILD_BUG_ON(COMPAT_POLICY_INPUT_ACCEPT || COMPAT_POLICY_OUTPUT_ACCEPT); ++ ++ ++void visit_complete(Visitor *v, void *opaque) ++{ ++ assert(v->type != VISITOR_OUTPUT || v->complete); ++ trace_visit_complete(v, opaque); ++ if (v->complete) { ++ v->complete(v, opaque); ++ } ++} ++ ++void visit_free(Visitor *v) ++{ ++ trace_visit_free(v); ++ if (v) { ++ v->free(v); ++ } ++} ++ ++bool visit_start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp) ++{ ++ bool ok; ++ ++ trace_visit_start_struct(v, name, obj, size); ++ if (obj) { ++ assert(size); ++ assert(!(v->type & VISITOR_OUTPUT) || *obj); ++ } ++ ok = v->start_struct(v, name, obj, size, errp); ++ if (obj && (v->type & VISITOR_INPUT)) { ++ assert(ok != !*obj); ++ } ++ return ok; ++} ++ ++bool visit_check_struct(Visitor *v, Error **errp) ++{ ++ trace_visit_check_struct(v); ++ return v->check_struct ? v->check_struct(v, errp) : true; ++} ++ ++void visit_end_struct(Visitor *v, void **obj) ++{ ++ trace_visit_end_struct(v, obj); ++ v->end_struct(v, obj); ++} ++ ++bool visit_start_list(Visitor *v, const char *name, GenericList **list, ++ size_t size, Error **errp) ++{ ++ bool ok; ++ ++ assert(!list || size >= sizeof(GenericList)); ++ trace_visit_start_list(v, name, list, size); ++ ok = v->start_list(v, name, list, size, errp); ++ if (list && (v->type & VISITOR_INPUT)) { ++ assert(ok || !*list); ++ } ++ return ok; ++} ++ ++GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size) ++{ ++ assert(tail && size >= sizeof(GenericList)); ++ trace_visit_next_list(v, tail, size); ++ return v->next_list(v, tail, size); ++} ++ ++bool visit_check_list(Visitor *v, Error **errp) ++{ ++ trace_visit_check_list(v); ++ return v->check_list ? v->check_list(v, errp) : true; ++} ++ ++void visit_end_list(Visitor *v, void **obj) ++{ ++ trace_visit_end_list(v, obj); ++ v->end_list(v, obj); ++} ++ ++bool visit_start_alternate(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp) ++{ ++ bool ok; ++ ++ assert(obj && size >= sizeof(GenericAlternate)); ++ assert(!(v->type & VISITOR_OUTPUT) || *obj); ++ trace_visit_start_alternate(v, name, obj, size); ++ if (!v->start_alternate) { ++ assert(!(v->type & VISITOR_INPUT)); ++ return true; ++ } ++ ok = v->start_alternate(v, name, obj, size, errp); ++ if (v->type & VISITOR_INPUT) { ++ assert(ok != !*obj); ++ } ++ return ok; ++} ++ ++void visit_end_alternate(Visitor *v, void **obj) ++{ ++ trace_visit_end_alternate(v, obj); ++ if (v->end_alternate) { ++ v->end_alternate(v, obj); ++ } ++} ++ ++bool visit_optional(Visitor *v, const char *name, bool *present) ++{ ++ trace_visit_optional(v, name, present); ++ if (v->optional) { ++ v->optional(v, name, present); ++ } ++ return *present; ++} ++ ++bool visit_policy_reject(Visitor *v, const char *name, ++ unsigned special_features, Error **errp) ++{ ++ trace_visit_policy_reject(v, name); ++ if (v->policy_reject) { ++ return v->policy_reject(v, name, special_features, errp); ++ } ++ return false; ++} ++ ++bool visit_policy_skip(Visitor *v, const char *name, ++ unsigned special_features) ++{ ++ trace_visit_policy_skip(v, name); ++ if (v->policy_skip) { ++ return v->policy_skip(v, name, special_features); ++ } ++ return false; ++} ++ ++void visit_set_policy(Visitor *v, CompatPolicy *policy) ++{ ++ v->compat_policy = *policy; ++} ++ ++bool visit_is_input(Visitor *v) ++{ ++ return v->type == VISITOR_INPUT; ++} ++ ++bool visit_is_dealloc(Visitor *v) ++{ ++ return v->type == VISITOR_DEALLOC; ++} ++ ++bool visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_int(v, name, obj); ++ return v->type_int64(v, name, obj, errp); ++} ++ ++static bool visit_type_uintN(Visitor *v, uint64_t *obj, const char *name, ++ uint64_t max, const char *type, Error **errp) ++{ ++ uint64_t value = *obj; ++ ++ assert(v->type == VISITOR_INPUT || value <= max); ++ ++ if (!v->type_uint64(v, name, &value, errp)) { ++ return false; ++ } ++ if (value > max) { ++ assert(v->type == VISITOR_INPUT); ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ name ? name : "null", type); ++ return false; ++ } ++ *obj = value; ++ return true; ++} ++ ++bool visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, ++ Error **errp) ++{ ++ uint64_t value; ++ bool ok; ++ ++ trace_visit_type_uint8(v, name, obj); ++ value = *obj; ++ ok = visit_type_uintN(v, &value, name, UINT8_MAX, "uint8_t", errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, ++ Error **errp) ++{ ++ uint64_t value; ++ bool ok; ++ ++ trace_visit_type_uint16(v, name, obj); ++ value = *obj; ++ ok = visit_type_uintN(v, &value, name, UINT16_MAX, "uint16_t", errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, ++ Error **errp) ++{ ++ uint64_t value; ++ bool ok; ++ ++ trace_visit_type_uint32(v, name, obj); ++ value = *obj; ++ ok = visit_type_uintN(v, &value, name, UINT32_MAX, "uint32_t", errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_uint64(v, name, obj); ++ return v->type_uint64(v, name, obj, errp); ++} ++ ++static bool visit_type_intN(Visitor *v, int64_t *obj, const char *name, ++ int64_t min, int64_t max, const char *type, ++ Error **errp) ++{ ++ int64_t value = *obj; ++ ++ assert(v->type == VISITOR_INPUT || (value >= min && value <= max)); ++ ++ if (!v->type_int64(v, name, &value, errp)) { ++ return false; ++ } ++ if (value < min || value > max) { ++ assert(v->type == VISITOR_INPUT); ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ name ? name : "null", type); ++ return false; ++ } ++ *obj = value; ++ return true; ++} ++ ++bool visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp) ++{ ++ int64_t value; ++ bool ok; ++ ++ trace_visit_type_int8(v, name, obj); ++ value = *obj; ++ ok = visit_type_intN(v, &value, name, INT8_MIN, INT8_MAX, "int8_t", errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_int16(Visitor *v, const char *name, int16_t *obj, ++ Error **errp) ++{ ++ int64_t value; ++ bool ok; ++ ++ trace_visit_type_int16(v, name, obj); ++ value = *obj; ++ ok = visit_type_intN(v, &value, name, INT16_MIN, INT16_MAX, "int16_t", ++ errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_int32(Visitor *v, const char *name, int32_t *obj, ++ Error **errp) ++{ ++ int64_t value; ++ bool ok; ++ ++ trace_visit_type_int32(v, name, obj); ++ value = *obj; ++ ok = visit_type_intN(v, &value, name, INT32_MIN, INT32_MAX, "int32_t", ++ errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_int64(v, name, obj); ++ return v->type_int64(v, name, obj, errp); ++} ++ ++bool visit_type_size(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_size(v, name, obj); ++ if (v->type_size) { ++ return v->type_size(v, name, obj, errp); ++ } ++ return v->type_uint64(v, name, obj, errp); ++} ++ ++bool visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_bool(v, name, obj); ++ return v->type_bool(v, name, obj, errp); ++} ++ ++bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) ++{ ++ bool ok; ++ ++ assert(obj); ++ /* TODO: Fix callers to not pass NULL when they mean "", so that we ++ * can enable: ++ assert(!(v->type & VISITOR_OUTPUT) || *obj); ++ */ ++ trace_visit_type_str(v, name, obj); ++ ok = v->type_str(v, name, obj, errp); ++ if (v->type & VISITOR_INPUT) { ++ assert(ok != !*obj); ++ } ++ return ok; ++} ++ ++bool visit_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ assert(obj); ++ trace_visit_type_number(v, name, obj); ++ return v->type_number(v, name, obj, errp); ++} ++ ++bool visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) ++{ ++ bool ok; ++ ++ assert(obj); ++ assert(v->type != VISITOR_OUTPUT || *obj); ++ trace_visit_type_any(v, name, obj); ++ ok = v->type_any(v, name, obj, errp); ++ if (v->type == VISITOR_INPUT) { ++ assert(ok != !*obj); ++ } ++ return ok; ++} ++ ++bool visit_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp) ++{ ++ trace_visit_type_null(v, name, obj); ++ return v->type_null(v, name, obj, errp); ++} ++ ++static bool output_type_enum(Visitor *v, const char *name, int *obj, ++ const QEnumLookup *lookup, Error **errp) ++{ ++ int value = *obj; ++ char *enum_str; ++ ++ enum_str = (char *)qapi_enum_lookup(lookup, value); ++ return visit_type_str(v, name, &enum_str, errp); ++} ++ ++static bool input_type_enum(Visitor *v, const char *name, int *obj, ++ const QEnumLookup *lookup, Error **errp) ++{ ++ int64_t value; ++ g_autofree char *enum_str = NULL; ++ ++ if (!visit_type_str(v, name, &enum_str, errp)) { ++ return false; ++ } ++ ++ value = qapi_enum_parse(lookup, enum_str, -1, NULL); ++ if (value < 0) { ++ error_setg(errp, "Parameter '%s' does not accept value '%s'", ++ name ? name : "null", enum_str); ++ return false; ++ } ++ ++ if (lookup->special_features ++ && !compat_policy_input_ok(lookup->special_features[value], ++ &v->compat_policy, ++ ERROR_CLASS_GENERIC_ERROR, ++ "value", enum_str, errp)) { ++ return false; ++ } ++ ++ *obj = value; ++ return true; ++} ++ ++bool visit_type_enum(Visitor *v, const char *name, int *obj, ++ const QEnumLookup *lookup, Error **errp) ++{ ++ assert(obj && lookup); ++ trace_visit_type_enum(v, name, obj); ++ switch (v->type) { ++ case VISITOR_INPUT: ++ return input_type_enum(v, name, obj, lookup, errp); ++ case VISITOR_OUTPUT: ++ return output_type_enum(v, name, obj, lookup, errp); ++ case VISITOR_CLONE: ++ /* nothing further to do, scalar value was already copied by ++ * g_memdup() during visit_start_*() */ ++ return true; ++ case VISITOR_DEALLOC: ++ /* nothing to deallocate for a scalar */ ++ return true; ++ default: ++ abort(); ++ } ++} +diff --git a/qcow2/lib/qapi/qapi-visit-crypto.c b/qcow2/lib/qapi/qapi-visit-crypto.c +new file mode 100644 +index 00000000..09eab7e3 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-crypto.c +@@ -0,0 +1,1150 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-crypto.h" ++ ++bool visit_type_QCryptoTLSCredsEndpoint(Visitor *v, const char *name, ++ QCryptoTLSCredsEndpoint *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoTLSCredsEndpoint_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoSecretFormat(Visitor *v, const char *name, ++ QCryptoSecretFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoSecretFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoHashAlgorithm(Visitor *v, const char *name, ++ QCryptoHashAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoHashAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoCipherAlgorithm(Visitor *v, const char *name, ++ QCryptoCipherAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoCipherAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoCipherMode(Visitor *v, const char *name, ++ QCryptoCipherMode *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoCipherMode_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoIVGenAlgorithm(Visitor *v, const char *name, ++ QCryptoIVGenAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoIVGenAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockFormat(Visitor *v, const char *name, ++ QCryptoBlockFormat *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoBlockFormat_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockOptionsBase_members(Visitor *v, QCryptoBlockOptionsBase *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockOptionsBase(Visitor *v, const char *name, ++ QCryptoBlockOptionsBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockOptionsBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockOptionsBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockOptionsBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockOptionsQCow_members(Visitor *v, QCryptoBlockOptionsQCow *obj, Error **errp) ++{ ++ bool has_key_secret = !!obj->key_secret; ++ ++ if (visit_optional(v, "key-secret", &has_key_secret)) { ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockOptionsQCow(Visitor *v, const char *name, ++ QCryptoBlockOptionsQCow **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockOptionsQCow), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockOptionsQCow_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockOptionsQCow(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockOptionsLUKS_members(Visitor *v, QCryptoBlockOptionsLUKS *obj, Error **errp) ++{ ++ bool has_key_secret = !!obj->key_secret; ++ ++ if (visit_optional(v, "key-secret", &has_key_secret)) { ++ if (!visit_type_str(v, "key-secret", &obj->key_secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockCreateOptionsLUKS_members(Visitor *v, QCryptoBlockCreateOptionsLUKS *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockOptionsLUKS_members(v, (QCryptoBlockOptionsLUKS *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "cipher-alg", &obj->has_cipher_alg)) { ++ if (!visit_type_QCryptoCipherAlgorithm(v, "cipher-alg", &obj->cipher_alg, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "cipher-mode", &obj->has_cipher_mode)) { ++ if (!visit_type_QCryptoCipherMode(v, "cipher-mode", &obj->cipher_mode, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "ivgen-alg", &obj->has_ivgen_alg)) { ++ if (!visit_type_QCryptoIVGenAlgorithm(v, "ivgen-alg", &obj->ivgen_alg, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "ivgen-hash-alg", &obj->has_ivgen_hash_alg)) { ++ if (!visit_type_QCryptoHashAlgorithm(v, "ivgen-hash-alg", &obj->ivgen_hash_alg, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "hash-alg", &obj->has_hash_alg)) { ++ if (!visit_type_QCryptoHashAlgorithm(v, "hash-alg", &obj->hash_alg, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iter-time", &obj->has_iter_time)) { ++ if (!visit_type_int(v, "iter-time", &obj->iter_time, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockCreateOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockCreateOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockCreateOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockCreateOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockCreateOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockOpenOptions_members(Visitor *v, QCryptoBlockOpenOptions *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockOptionsBase_members(v, (QCryptoBlockOptionsBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ return visit_type_QCryptoBlockOptionsQCow_members(v, &obj->u.qcow, errp); ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ return visit_type_QCryptoBlockOptionsLUKS_members(v, &obj->u.luks, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockOpenOptions(Visitor *v, const char *name, ++ QCryptoBlockOpenOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockOpenOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockOpenOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockOpenOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockCreateOptions_members(Visitor *v, QCryptoBlockCreateOptions *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockOptionsBase_members(v, (QCryptoBlockOptionsBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ return visit_type_QCryptoBlockOptionsQCow_members(v, &obj->u.qcow, errp); ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ return visit_type_QCryptoBlockCreateOptionsLUKS_members(v, &obj->u.luks, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockCreateOptions(Visitor *v, const char *name, ++ QCryptoBlockCreateOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockCreateOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockCreateOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockCreateOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfoBase_members(Visitor *v, QCryptoBlockInfoBase *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockInfoBase(Visitor *v, const char *name, ++ QCryptoBlockInfoBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockInfoBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockInfoBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfoBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKSSlot_members(Visitor *v, QCryptoBlockInfoLUKSSlot *obj, Error **errp) ++{ ++ if (!visit_type_bool(v, "active", &obj->active, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "iters", &obj->has_iters)) { ++ if (!visit_type_int(v, "iters", &obj->iters, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "stripes", &obj->has_stripes)) { ++ if (!visit_type_int(v, "stripes", &obj->stripes, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_int(v, "key-offset", &obj->key_offset, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKSSlot(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKSSlot **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockInfoLUKSSlot), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockInfoLUKSSlot_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfoLUKSSlot(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKSSlotList(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKSSlotList **obj, Error **errp) ++{ ++ bool ok = false; ++ QCryptoBlockInfoLUKSSlotList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (QCryptoBlockInfoLUKSSlotList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_QCryptoBlockInfoLUKSSlot(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfoLUKSSlotList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKS_members(Visitor *v, QCryptoBlockInfoLUKS *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoCipherAlgorithm(v, "cipher-alg", &obj->cipher_alg, errp)) { ++ return false; ++ } ++ if (!visit_type_QCryptoCipherMode(v, "cipher-mode", &obj->cipher_mode, errp)) { ++ return false; ++ } ++ if (!visit_type_QCryptoIVGenAlgorithm(v, "ivgen-alg", &obj->ivgen_alg, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "ivgen-hash-alg", &obj->has_ivgen_hash_alg)) { ++ if (!visit_type_QCryptoHashAlgorithm(v, "ivgen-hash-alg", &obj->ivgen_hash_alg, errp)) { ++ return false; ++ } ++ } ++ if (!visit_type_QCryptoHashAlgorithm(v, "hash-alg", &obj->hash_alg, errp)) { ++ return false; ++ } ++ if (!visit_type_bool(v, "detached-header", &obj->detached_header, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "payload-offset", &obj->payload_offset, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "master-key-iters", &obj->master_key_iters, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "uuid", &obj->uuid, errp)) { ++ return false; ++ } ++ if (!visit_type_QCryptoBlockInfoLUKSSlotList(v, "slots", &obj->slots, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockInfoLUKS(Visitor *v, const char *name, ++ QCryptoBlockInfoLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockInfoLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockInfoLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfoLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockInfo_members(Visitor *v, QCryptoBlockInfo *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockInfoBase_members(v, (QCryptoBlockInfoBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ return visit_type_QCryptoBlockInfoLUKS_members(v, &obj->u.luks, errp); ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockInfo(Visitor *v, const char *name, ++ QCryptoBlockInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockLUKSKeyslotState(Visitor *v, const char *name, ++ QCryptoBlockLUKSKeyslotState *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoBlockLUKSKeyslotState_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockAmendOptionsLUKS_members(Visitor *v, QCryptoBlockAmendOptionsLUKS *obj, Error **errp) ++{ ++ bool has_new_secret = !!obj->new_secret; ++ bool has_old_secret = !!obj->old_secret; ++ bool has_secret = !!obj->secret; ++ ++ if (!visit_type_QCryptoBlockLUKSKeyslotState(v, "state", &obj->state, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "new-secret", &has_new_secret)) { ++ if (!visit_type_str(v, "new-secret", &obj->new_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "old-secret", &has_old_secret)) { ++ if (!visit_type_str(v, "old-secret", &obj->old_secret, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "keyslot", &obj->has_keyslot)) { ++ if (!visit_type_int(v, "keyslot", &obj->keyslot, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iter-time", &obj->has_iter_time)) { ++ if (!visit_type_int(v, "iter-time", &obj->iter_time, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "secret", &has_secret)) { ++ if (!visit_type_str(v, "secret", &obj->secret, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockAmendOptionsLUKS(Visitor *v, const char *name, ++ QCryptoBlockAmendOptionsLUKS **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockAmendOptionsLUKS), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockAmendOptionsLUKS_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockAmendOptionsLUKS(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoBlockAmendOptions_members(Visitor *v, QCryptoBlockAmendOptions *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoBlockOptionsBase_members(v, (QCryptoBlockOptionsBase *)obj, errp)) { ++ return false; ++ } ++ switch (obj->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ return visit_type_QCryptoBlockAmendOptionsLUKS_members(v, &obj->u.luks, errp); ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoBlockAmendOptions(Visitor *v, const char *name, ++ QCryptoBlockAmendOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoBlockAmendOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoBlockAmendOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoBlockAmendOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SecretCommonProperties_members(Visitor *v, SecretCommonProperties *obj, Error **errp) ++{ ++ bool has_keyid = !!obj->keyid; ++ bool has_iv = !!obj->iv; ++ ++ if (visit_optional(v, "loaded", &obj->has_loaded)) { ++ if (visit_policy_reject(v, "loaded", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "loaded", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_bool(v, "loaded", &obj->loaded, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "format", &obj->has_format)) { ++ if (!visit_type_QCryptoSecretFormat(v, "format", &obj->format, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "keyid", &has_keyid)) { ++ if (!visit_type_str(v, "keyid", &obj->keyid, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "iv", &has_iv)) { ++ if (!visit_type_str(v, "iv", &obj->iv, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_SecretCommonProperties(Visitor *v, const char *name, ++ SecretCommonProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SecretCommonProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SecretCommonProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SecretCommonProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SecretProperties_members(Visitor *v, SecretProperties *obj, Error **errp) ++{ ++ bool has_data = !!obj->data; ++ bool has_file = !!obj->file; ++ ++ if (!visit_type_SecretCommonProperties_members(v, (SecretCommonProperties *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "data", &has_data)) { ++ if (!visit_type_str(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "file", &has_file)) { ++ if (!visit_type_str(v, "file", &obj->file, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_SecretProperties(Visitor *v, const char *name, ++ SecretProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SecretProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SecretProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SecretProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++#if defined(CONFIG_SECRET_KEYRING) ++bool visit_type_SecretKeyringProperties_members(Visitor *v, SecretKeyringProperties *obj, Error **errp) ++{ ++ if (!visit_type_SecretCommonProperties_members(v, (SecretCommonProperties *)obj, errp)) { ++ return false; ++ } ++ if (!visit_type_int32(v, "serial", &obj->serial, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SecretKeyringProperties(Visitor *v, const char *name, ++ SecretKeyringProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SecretKeyringProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SecretKeyringProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SecretKeyringProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++#endif /* defined(CONFIG_SECRET_KEYRING) */ ++ ++bool visit_type_TlsCredsProperties_members(Visitor *v, TlsCredsProperties *obj, Error **errp) ++{ ++ bool has_dir = !!obj->dir; ++ bool has_priority = !!obj->priority; ++ ++ if (visit_optional(v, "verify-peer", &obj->has_verify_peer)) { ++ if (!visit_type_bool(v, "verify-peer", &obj->verify_peer, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "dir", &has_dir)) { ++ if (!visit_type_str(v, "dir", &obj->dir, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "endpoint", &obj->has_endpoint)) { ++ if (!visit_type_QCryptoTLSCredsEndpoint(v, "endpoint", &obj->endpoint, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "priority", &has_priority)) { ++ if (!visit_type_str(v, "priority", &obj->priority, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_TlsCredsProperties(Visitor *v, const char *name, ++ TlsCredsProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(TlsCredsProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_TlsCredsProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_TlsCredsProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_TlsCredsAnonProperties_members(Visitor *v, TlsCredsAnonProperties *obj, Error **errp) ++{ ++ if (!visit_type_TlsCredsProperties_members(v, (TlsCredsProperties *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "loaded", &obj->has_loaded)) { ++ if (visit_policy_reject(v, "loaded", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "loaded", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_bool(v, "loaded", &obj->loaded, errp)) { ++ return false; ++ } ++ } ++ } ++ return true; ++} ++ ++bool visit_type_TlsCredsAnonProperties(Visitor *v, const char *name, ++ TlsCredsAnonProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(TlsCredsAnonProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_TlsCredsAnonProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_TlsCredsAnonProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_TlsCredsPskProperties_members(Visitor *v, TlsCredsPskProperties *obj, Error **errp) ++{ ++ bool has_username = !!obj->username; ++ ++ if (!visit_type_TlsCredsProperties_members(v, (TlsCredsProperties *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "loaded", &obj->has_loaded)) { ++ if (visit_policy_reject(v, "loaded", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "loaded", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_bool(v, "loaded", &obj->loaded, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "username", &has_username)) { ++ if (!visit_type_str(v, "username", &obj->username, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_TlsCredsPskProperties(Visitor *v, const char *name, ++ TlsCredsPskProperties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(TlsCredsPskProperties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_TlsCredsPskProperties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_TlsCredsPskProperties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_TlsCredsX509Properties_members(Visitor *v, TlsCredsX509Properties *obj, Error **errp) ++{ ++ bool has_passwordid = !!obj->passwordid; ++ ++ if (!visit_type_TlsCredsProperties_members(v, (TlsCredsProperties *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "loaded", &obj->has_loaded)) { ++ if (visit_policy_reject(v, "loaded", 1u << QAPI_DEPRECATED, errp)) { ++ return false; ++ } ++ if (!visit_policy_skip(v, "loaded", 1u << QAPI_DEPRECATED)) { ++ if (!visit_type_bool(v, "loaded", &obj->loaded, errp)) { ++ return false; ++ } ++ } ++ } ++ if (visit_optional(v, "sanity-check", &obj->has_sanity_check)) { ++ if (!visit_type_bool(v, "sanity-check", &obj->sanity_check, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "passwordid", &has_passwordid)) { ++ if (!visit_type_str(v, "passwordid", &obj->passwordid, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_TlsCredsX509Properties(Visitor *v, const char *name, ++ TlsCredsX509Properties **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(TlsCredsX509Properties), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_TlsCredsX509Properties_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_TlsCredsX509Properties(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_QCryptoAkCipherAlgorithm(Visitor *v, const char *name, ++ QCryptoAkCipherAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoAkCipherAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoAkCipherKeyType(Visitor *v, const char *name, ++ QCryptoAkCipherKeyType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoAkCipherKeyType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoRSAPaddingAlgorithm(Visitor *v, const char *name, ++ QCryptoRSAPaddingAlgorithm *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &QCryptoRSAPaddingAlgorithm_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_QCryptoAkCipherOptionsRSA_members(Visitor *v, QCryptoAkCipherOptionsRSA *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoHashAlgorithm(v, "hash-alg", &obj->hash_alg, errp)) { ++ return false; ++ } ++ if (!visit_type_QCryptoRSAPaddingAlgorithm(v, "padding-alg", &obj->padding_alg, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoAkCipherOptionsRSA(Visitor *v, const char *name, ++ QCryptoAkCipherOptionsRSA **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoAkCipherOptionsRSA), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoAkCipherOptionsRSA_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoAkCipherOptionsRSA(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_QCryptoAkCipherOptions_base_members(Visitor *v, q_obj_QCryptoAkCipherOptions_base *obj, Error **errp) ++{ ++ if (!visit_type_QCryptoAkCipherAlgorithm(v, "alg", &obj->alg, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoAkCipherOptions_members(Visitor *v, QCryptoAkCipherOptions *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_QCryptoAkCipherOptions_base_members(v, (q_obj_QCryptoAkCipherOptions_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->alg) { ++ case QCRYPTO_AKCIPHER_ALG_RSA: ++ return visit_type_QCryptoAkCipherOptionsRSA_members(v, &obj->u.rsa, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_QCryptoAkCipherOptions(Visitor *v, const char *name, ++ QCryptoAkCipherOptions **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(QCryptoAkCipherOptions), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_QCryptoAkCipherOptions_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_QCryptoAkCipherOptions(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_crypto_c; +diff --git a/qcow2/lib/qapi/qapi-visit-job.c b/qcow2/lib/qapi/qapi-visit-job.c +new file mode 100644 +index 00000000..092f9a03 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-job.c +@@ -0,0 +1,186 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-job.h" ++ ++bool visit_type_JobType(Visitor *v, const char *name, ++ JobType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &JobType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_JobStatus(Visitor *v, const char *name, ++ JobStatus *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &JobStatus_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_JobVerb(Visitor *v, const char *name, ++ JobVerb *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &JobVerb_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_JOB_STATUS_CHANGE_arg_members(Visitor *v, q_obj_JOB_STATUS_CHANGE_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_JobStatus(v, "status", &obj->status, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_pause_arg_members(Visitor *v, q_obj_job_pause_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_resume_arg_members(Visitor *v, q_obj_job_resume_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_cancel_arg_members(Visitor *v, q_obj_job_cancel_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_complete_arg_members(Visitor *v, q_obj_job_complete_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_dismiss_arg_members(Visitor *v, q_obj_job_dismiss_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_q_obj_job_finalize_arg_members(Visitor *v, q_obj_job_finalize_arg *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_JobInfo_members(Visitor *v, JobInfo *obj, Error **errp) ++{ ++ bool has_error = !!obj->error; ++ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ if (!visit_type_JobType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ if (!visit_type_JobStatus(v, "status", &obj->status, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "current-progress", &obj->current_progress, errp)) { ++ return false; ++ } ++ if (!visit_type_int(v, "total-progress", &obj->total_progress, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "error", &has_error)) { ++ if (!visit_type_str(v, "error", &obj->error, errp)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool visit_type_JobInfo(Visitor *v, const char *name, ++ JobInfo **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(JobInfo), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_JobInfo_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_JobInfo(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_JobInfoList(Visitor *v, const char *name, ++ JobInfoList **obj, Error **errp) ++{ ++ bool ok = false; ++ JobInfoList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (JobInfoList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_JobInfo(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_JobInfoList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_job_c; +diff --git a/qcow2/lib/qapi/qapi-visit-sockets.c b/qcow2/lib/qapi/qapi-visit-sockets.c +new file mode 100644 +index 00000000..34d0b894 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-sockets.c +@@ -0,0 +1,569 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-sockets.h" ++ ++bool visit_type_NetworkAddressFamily(Visitor *v, const char *name, ++ NetworkAddressFamily *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &NetworkAddressFamily_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_InetSocketAddressBase_members(Visitor *v, InetSocketAddressBase *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "host", &obj->host, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "port", &obj->port, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_InetSocketAddressBase(Visitor *v, const char *name, ++ InetSocketAddressBase **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(InetSocketAddressBase), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_InetSocketAddressBase_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_InetSocketAddressBase(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_InetSocketAddress_members(Visitor *v, InetSocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_InetSocketAddressBase_members(v, (InetSocketAddressBase *)obj, errp)) { ++ return false; ++ } ++ if (visit_optional(v, "numeric", &obj->has_numeric)) { ++ if (!visit_type_bool(v, "numeric", &obj->numeric, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "to", &obj->has_to)) { ++ if (!visit_type_uint16(v, "to", &obj->to, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "ipv4", &obj->has_ipv4)) { ++ if (!visit_type_bool(v, "ipv4", &obj->ipv4, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "ipv6", &obj->has_ipv6)) { ++ if (!visit_type_bool(v, "ipv6", &obj->ipv6, errp)) { ++ return false; ++ } ++ } ++ if (visit_optional(v, "keep-alive", &obj->has_keep_alive)) { ++ if (!visit_type_bool(v, "keep-alive", &obj->keep_alive, errp)) { ++ return false; ++ } ++ } ++#if defined(HAVE_IPPROTO_MPTCP) ++ if (visit_optional(v, "mptcp", &obj->has_mptcp)) { ++ if (!visit_type_bool(v, "mptcp", &obj->mptcp, errp)) { ++ return false; ++ } ++ } ++#endif /* defined(HAVE_IPPROTO_MPTCP) */ ++ return true; ++} ++ ++bool visit_type_InetSocketAddress(Visitor *v, const char *name, ++ InetSocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(InetSocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_InetSocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_InetSocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_UnixSocketAddress_members(Visitor *v, UnixSocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "path", &obj->path, errp)) { ++ return false; ++ } ++#if defined(CONFIG_LINUX) ++ if (visit_optional(v, "abstract", &obj->has_abstract)) { ++ if (!visit_type_bool(v, "abstract", &obj->abstract, errp)) { ++ return false; ++ } ++ } ++#endif /* defined(CONFIG_LINUX) */ ++#if defined(CONFIG_LINUX) ++ if (visit_optional(v, "tight", &obj->has_tight)) { ++ if (!visit_type_bool(v, "tight", &obj->tight, errp)) { ++ return false; ++ } ++ } ++#endif /* defined(CONFIG_LINUX) */ ++ return true; ++} ++ ++bool visit_type_UnixSocketAddress(Visitor *v, const char *name, ++ UnixSocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(UnixSocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_UnixSocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_UnixSocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_VsockSocketAddress_members(Visitor *v, VsockSocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "cid", &obj->cid, errp)) { ++ return false; ++ } ++ if (!visit_type_str(v, "port", &obj->port, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_VsockSocketAddress(Visitor *v, const char *name, ++ VsockSocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(VsockSocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_VsockSocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_VsockSocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_FdSocketAddress_members(Visitor *v, FdSocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "str", &obj->str, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_FdSocketAddress(Visitor *v, const char *name, ++ FdSocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(FdSocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_FdSocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_FdSocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_InetSocketAddressWrapper_members(Visitor *v, InetSocketAddressWrapper *obj, Error **errp) ++{ ++ if (!visit_type_InetSocketAddress(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_InetSocketAddressWrapper(Visitor *v, const char *name, ++ InetSocketAddressWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(InetSocketAddressWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_InetSocketAddressWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_InetSocketAddressWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_UnixSocketAddressWrapper_members(Visitor *v, UnixSocketAddressWrapper *obj, Error **errp) ++{ ++ if (!visit_type_UnixSocketAddress(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_UnixSocketAddressWrapper(Visitor *v, const char *name, ++ UnixSocketAddressWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(UnixSocketAddressWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_UnixSocketAddressWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_UnixSocketAddressWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_VsockSocketAddressWrapper_members(Visitor *v, VsockSocketAddressWrapper *obj, Error **errp) ++{ ++ if (!visit_type_VsockSocketAddress(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_VsockSocketAddressWrapper(Visitor *v, const char *name, ++ VsockSocketAddressWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(VsockSocketAddressWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_VsockSocketAddressWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_VsockSocketAddressWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_FdSocketAddressWrapper_members(Visitor *v, FdSocketAddressWrapper *obj, Error **errp) ++{ ++ if (!visit_type_FdSocketAddress(v, "data", &obj->data, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_FdSocketAddressWrapper(Visitor *v, const char *name, ++ FdSocketAddressWrapper **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(FdSocketAddressWrapper), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_FdSocketAddressWrapper_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_FdSocketAddressWrapper(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_SocketAddressLegacy_base_members(Visitor *v, q_obj_SocketAddressLegacy_base *obj, Error **errp) ++{ ++ if (!visit_type_SocketAddressType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SocketAddressLegacy_members(Visitor *v, SocketAddressLegacy *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_SocketAddressLegacy_base_members(v, (q_obj_SocketAddressLegacy_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ return visit_type_InetSocketAddressWrapper_members(v, &obj->u.inet, errp); ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ return visit_type_UnixSocketAddressWrapper_members(v, &obj->u.q_unix, errp); ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ return visit_type_VsockSocketAddressWrapper_members(v, &obj->u.vsock, errp); ++ case SOCKET_ADDRESS_TYPE_FD: ++ return visit_type_FdSocketAddressWrapper_members(v, &obj->u.fd, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_SocketAddressLegacy(Visitor *v, const char *name, ++ SocketAddressLegacy **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SocketAddressLegacy), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SocketAddressLegacy_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SocketAddressLegacy(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SocketAddressType(Visitor *v, const char *name, ++ SocketAddressType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &SocketAddressType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_q_obj_SocketAddress_base_members(Visitor *v, q_obj_SocketAddress_base *obj, Error **errp) ++{ ++ if (!visit_type_SocketAddressType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_SocketAddress_members(Visitor *v, SocketAddress *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_SocketAddress_base_members(v, (q_obj_SocketAddress_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ return visit_type_InetSocketAddress_members(v, &obj->u.inet, errp); ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ return visit_type_UnixSocketAddress_members(v, &obj->u.q_unix, errp); ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ return visit_type_VsockSocketAddress_members(v, &obj->u.vsock, errp); ++ case SOCKET_ADDRESS_TYPE_FD: ++ return visit_type_FdSocketAddress_members(v, &obj->u.fd, errp); ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_SocketAddress(Visitor *v, const char *name, ++ SocketAddress **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(SocketAddress), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_SocketAddress_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SocketAddress(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_SocketAddressList(Visitor *v, const char *name, ++ SocketAddressList **obj, Error **errp) ++{ ++ bool ok = false; ++ SocketAddressList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (SocketAddressList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_SocketAddress(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_SocketAddressList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_InetSocketAddressBaseList(Visitor *v, const char *name, ++ InetSocketAddressBaseList **obj, Error **errp) ++{ ++ bool ok = false; ++ InetSocketAddressBaseList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (InetSocketAddressBaseList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_InetSocketAddressBase(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_InetSocketAddressBaseList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_sockets_c; +diff --git a/qcow2/lib/qapi/qapi-visit-yank.c b/qcow2/lib/qapi/qapi-visit-yank.c +new file mode 100644 +index 00000000..f5d5bf25 +--- /dev/null ++++ b/qcow2/lib/qapi/qapi-visit-yank.c +@@ -0,0 +1,186 @@ ++/* AUTOMATICALLY GENERATED by qapi-gen.py DO NOT MODIFY */ ++ ++/* ++ * Schema-defined QAPI visitors ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2014-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi-visit-yank.h" ++ ++bool visit_type_YankInstanceType(Visitor *v, const char *name, ++ YankInstanceType *obj, Error **errp) ++{ ++ int value = *obj; ++ bool ok = visit_type_enum(v, name, &value, &YankInstanceType_lookup, errp); ++ *obj = value; ++ return ok; ++} ++ ++bool visit_type_YankInstanceBlockNode_members(Visitor *v, YankInstanceBlockNode *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_YankInstanceBlockNode(Visitor *v, const char *name, ++ YankInstanceBlockNode **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(YankInstanceBlockNode), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_YankInstanceBlockNode_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_YankInstanceBlockNode(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_YankInstanceChardev_members(Visitor *v, YankInstanceChardev *obj, Error **errp) ++{ ++ if (!visit_type_str(v, "id", &obj->id, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_YankInstanceChardev(Visitor *v, const char *name, ++ YankInstanceChardev **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(YankInstanceChardev), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_YankInstanceChardev_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_YankInstanceChardev(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_YankInstance_base_members(Visitor *v, q_obj_YankInstance_base *obj, Error **errp) ++{ ++ if (!visit_type_YankInstanceType(v, "type", &obj->type, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++bool visit_type_YankInstance_members(Visitor *v, YankInstance *obj, Error **errp) ++{ ++ if (!visit_type_q_obj_YankInstance_base_members(v, (q_obj_YankInstance_base *)obj, errp)) { ++ return false; ++ } ++ switch (obj->type) { ++ case YANK_INSTANCE_TYPE_BLOCK_NODE: ++ return visit_type_YankInstanceBlockNode_members(v, &obj->u.block_node, errp); ++ case YANK_INSTANCE_TYPE_CHARDEV: ++ return visit_type_YankInstanceChardev_members(v, &obj->u.chardev, errp); ++ case YANK_INSTANCE_TYPE_MIGRATION: ++ break; ++ default: ++ abort(); ++ } ++ return true; ++} ++ ++bool visit_type_YankInstance(Visitor *v, const char *name, ++ YankInstance **obj, Error **errp) ++{ ++ bool ok = false; ++ ++ if (!visit_start_struct(v, name, (void **)obj, sizeof(YankInstance), errp)) { ++ return false; ++ } ++ if (!*obj) { ++ /* incomplete */ ++ assert(visit_is_dealloc(v)); ++ ok = true; ++ goto out_obj; ++ } ++ if (!visit_type_YankInstance_members(v, *obj, errp)) { ++ goto out_obj; ++ } ++ ok = visit_check_struct(v, errp); ++out_obj: ++ visit_end_struct(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_YankInstance(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_YankInstanceList(Visitor *v, const char *name, ++ YankInstanceList **obj, Error **errp) ++{ ++ bool ok = false; ++ YankInstanceList *tail; ++ size_t size = sizeof(**obj); ++ ++ if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) { ++ return false; ++ } ++ ++ for (tail = *obj; tail; ++ tail = (YankInstanceList *)visit_next_list(v, (GenericList *)tail, size)) { ++ if (!visit_type_YankInstance(v, NULL, &tail->value, errp)) { ++ goto out_obj; ++ } ++ } ++ ++ ok = visit_check_list(v, errp); ++out_obj: ++ visit_end_list(v, (void **)obj); ++ if (!ok && visit_is_input(v)) { ++ qapi_free_YankInstanceList(*obj); ++ *obj = NULL; ++ } ++ return ok; ++} ++ ++bool visit_type_q_obj_yank_arg_members(Visitor *v, q_obj_yank_arg *obj, Error **errp) ++{ ++ if (!visit_type_YankInstanceList(v, "instances", &obj->instances, errp)) { ++ return false; ++ } ++ return true; ++} ++ ++/* Dummy declaration to prevent empty .o file */ ++char qapi_dummy_qapi_visit_yank_c; +diff --git a/qcow2/lib/qapi/qmp-dispatch.c b/qcow2/lib/qapi/qmp-dispatch.c +new file mode 100644 +index 00000000..176b5494 +--- /dev/null ++++ b/qcow2/lib/qapi/qmp-dispatch.c +@@ -0,0 +1,296 @@ ++/* ++ * Core Definitions for QAPI/QMP Dispatch ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "block/aio.h" ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qmp/dispatch.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/qmp/qbool.h" ++#include "qemu/coroutine.h" ++#include "qemu/main-loop.h" ++ ++Visitor *qobject_input_visitor_new_qmp(QObject *obj) ++{ ++ Visitor *v = qobject_input_visitor_new(obj); ++ ++ visit_set_policy(v, &compat_policy); ++ return v; ++} ++ ++Visitor *qobject_output_visitor_new_qmp(QObject **result) ++{ ++ Visitor *v = qobject_output_visitor_new(result); ++ ++ visit_set_policy(v, &compat_policy); ++ return v; ++} ++ ++static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, ++ Error **errp) ++{ ++ const char *exec_key = NULL; ++ const QDictEntry *ent; ++ const char *arg_name; ++ const QObject *arg_obj; ++ ++ for (ent = qdict_first(dict); ent; ++ ent = qdict_next(dict, ent)) { ++ arg_name = qdict_entry_key(ent); ++ arg_obj = qdict_entry_value(ent); ++ ++ if (!strcmp(arg_name, "execute") ++ || (!strcmp(arg_name, "exec-oob") && allow_oob)) { ++ if (qobject_type(arg_obj) != QTYPE_QSTRING) { ++ error_setg(errp, "QMP input member '%s' must be a string", ++ arg_name); ++ return NULL; ++ } ++ if (exec_key) { ++ error_setg(errp, "QMP input member '%s' clashes with '%s'", ++ arg_name, exec_key); ++ return NULL; ++ } ++ exec_key = arg_name; ++ } else if (!strcmp(arg_name, "arguments")) { ++ if (qobject_type(arg_obj) != QTYPE_QDICT) { ++ error_setg(errp, ++ "QMP input member 'arguments' must be an object"); ++ return NULL; ++ } ++ } else if (!strcmp(arg_name, "id")) { ++ continue; ++ } else { ++ error_setg(errp, "QMP input member '%s' is unexpected", ++ arg_name); ++ return NULL; ++ } ++ } ++ ++ if (!exec_key) { ++ error_setg(errp, "QMP input lacks member 'execute'"); ++ return NULL; ++ } ++ ++ return dict; ++} ++ ++QDict *qmp_error_response(Error *err) ++{ ++ QDict *rsp; ++ ++ rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", ++ QapiErrorClass_str(error_get_class(err)), ++ error_get_pretty(err)); ++ error_free(err); ++ return rsp; ++} ++ ++/* ++ * Does @qdict look like a command to be run out-of-band? ++ */ ++bool qmp_is_oob(const QDict *dict) ++{ ++ return qdict_haskey(dict, "exec-oob") ++ && !qdict_haskey(dict, "execute"); ++} ++ ++typedef struct QmpDispatchBH { ++ const QmpCommand *cmd; ++ Monitor *cur_mon; ++ QDict *args; ++ QObject **ret; ++ Error **errp; ++ Coroutine *co; ++} QmpDispatchBH; ++ ++static void do_qmp_dispatch_bh(void *opaque) ++{ ++ QmpDispatchBH *data = opaque; ++ ++ assert(monitor_cur() == NULL); ++ monitor_set_cur(qemu_coroutine_self(), data->cur_mon); ++ data->cmd->fn(data->args, data->ret, data->errp); ++ monitor_set_cur(qemu_coroutine_self(), NULL); ++ aio_co_wake(data->co); ++} ++ ++/* ++ * Runs outside of coroutine context for OOB commands, but in coroutine ++ * context for everything else. ++ */ ++QDict *coroutine_mixed_fn qmp_dispatch(const QmpCommandList *cmds, QObject *request, ++ bool allow_oob, Monitor *cur_mon) ++{ ++ Error *err = NULL; ++ bool oob; ++ const char *command; ++ QDict *args; ++ const QmpCommand *cmd; ++ QDict *dict; ++ QObject *id; ++ QObject *ret = NULL; ++ QDict *rsp = NULL; ++ ++ dict = qobject_to(QDict, request); ++ if (!dict) { ++ id = NULL; ++ error_setg(&err, "QMP input must be a JSON object"); ++ goto out; ++ } ++ ++ id = qdict_get(dict, "id"); ++ ++ if (!qmp_dispatch_check_obj(dict, allow_oob, &err)) { ++ goto out; ++ } ++ ++ command = qdict_get_try_str(dict, "execute"); ++ oob = false; ++ if (!command) { ++ assert(allow_oob); ++ command = qdict_get_str(dict, "exec-oob"); ++ oob = true; ++ } ++ cmd = qmp_find_command(cmds, command); ++ if (cmd == NULL) { ++ error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, ++ "The command %s has not been found", command); ++ goto out; ++ } ++ if (!compat_policy_input_ok(cmd->special_features, &compat_policy, ++ ERROR_CLASS_COMMAND_NOT_FOUND, ++ "command", command, &err)) { ++ goto out; ++ } ++ if (!cmd->enabled) { ++ error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, ++ "Command %s has been disabled%s%s", ++ command, ++ cmd->disable_reason ? ": " : "", ++ cmd->disable_reason ?: ""); ++ goto out; ++ } ++ if (oob && !(cmd->options & QCO_ALLOW_OOB)) { ++ error_setg(&err, "The command %s does not support OOB", ++ command); ++ goto out; ++ } ++ ++ if (!qmp_command_available(cmd, &err)) { ++ goto out; ++ } ++ ++ if (!qdict_haskey(dict, "arguments")) { ++ args = qdict_new(); ++ } else { ++ args = qdict_get_qdict(dict, "arguments"); ++ qobject_ref(args); ++ } ++ ++ assert(!(oob && qemu_in_coroutine())); ++ assert(monitor_cur() == NULL); ++ if (!!(cmd->options & QCO_COROUTINE) == qemu_in_coroutine()) { ++ if (qemu_in_coroutine()) { ++ /* ++ * Move the coroutine from iohandler_ctx to qemu_aio_context for ++ * executing the command handler so that it can make progress if it ++ * involves an AIO_WAIT_WHILE(). ++ */ ++ aio_co_schedule(qemu_get_aio_context(), qemu_coroutine_self()); ++ qemu_coroutine_yield(); ++ } ++ ++ monitor_set_cur(qemu_coroutine_self(), cur_mon); ++ cmd->fn(args, &ret, &err); ++ monitor_set_cur(qemu_coroutine_self(), NULL); ++ ++ if (qemu_in_coroutine()) { ++ /* ++ * Yield and reschedule so the main loop stays responsive. ++ * ++ * Move back to iohandler_ctx so that nested event loops for ++ * qemu_aio_context don't start new monitor commands. ++ */ ++ aio_co_schedule(iohandler_get_aio_context(), ++ qemu_coroutine_self()); ++ qemu_coroutine_yield(); ++ } ++ } else { ++ /* ++ * Actual context doesn't match the one the command needs. ++ * ++ * Case 1: we are in coroutine context, but command does not ++ * have QCO_COROUTINE. We need to drop out of coroutine ++ * context for executing it. ++ * ++ * Case 2: we are outside coroutine context, but command has ++ * QCO_COROUTINE. Can't actually happen, because we get here ++ * outside coroutine context only when executing a command ++ * out of band, and OOB commands never have QCO_COROUTINE. ++ */ ++ assert(!oob && qemu_in_coroutine() && !(cmd->options & QCO_COROUTINE)); ++ ++ QmpDispatchBH data = { ++ .cur_mon = cur_mon, ++ .cmd = cmd, ++ .args = args, ++ .ret = &ret, ++ .errp = &err, ++ .co = qemu_coroutine_self(), ++ }; ++ aio_bh_schedule_oneshot(iohandler_get_aio_context(), do_qmp_dispatch_bh, ++ &data); ++ qemu_coroutine_yield(); ++ } ++ qobject_unref(args); ++ if (err) { ++ /* or assert(!ret) after reviewing all handlers: */ ++ qobject_unref(ret); ++ goto out; ++ } ++ ++ if (cmd->options & QCO_NO_SUCCESS_RESP) { ++ g_assert(!ret); ++ return NULL; ++ } else if (!ret) { ++ /* ++ * When the command's schema has no 'returns', cmd->fn() ++ * leaves @ret null. The QMP spec calls for an empty object ++ * then; supply it. ++ */ ++ ret = QOBJECT(qdict_new()); ++ } ++ ++ rsp = qdict_new(); ++ qdict_put_obj(rsp, "return", ret); ++ ++out: ++ if (err) { ++ assert(!rsp); ++ rsp = qmp_error_response(err); ++ } ++ ++ assert(rsp); ++ ++ if (id) { ++ qdict_put_obj(rsp, "id", qobject_ref(id)); ++ } ++ ++ return rsp; ++} +diff --git a/qcow2/lib/qapi/qmp-event.c b/qcow2/lib/qapi/qmp-event.c +new file mode 100644 +index 00000000..0fe0d0a5 +--- /dev/null ++++ b/qcow2/lib/qapi/qmp-event.c +@@ -0,0 +1,42 @@ ++/* ++ * QMP Event related ++ * ++ * Copyright (c) 2014 Wenchao Xia ++ * ++ * Authors: ++ * Wenchao Xia ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "qapi/qmp-event.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qjson.h" ++ ++static void timestamp_put(QDict *qdict) ++{ ++ QDict *ts; ++ int64_t rt = g_get_real_time(); ++ ++ ts = qdict_from_jsonf_nofail("{ 'seconds': %lld, 'microseconds': %lld }", ++ (long long)rt / G_USEC_PER_SEC, ++ (long long)rt % G_USEC_PER_SEC); ++ qdict_put(qdict, "timestamp", ts); ++} ++ ++/* ++ * Build a QDict, then fill event name and time stamp, caller should free the ++ * QDict after usage. ++ */ ++QDict *qmp_event_build_dict(const char *event_name) ++{ ++ QDict *dict = qdict_new(); ++ qdict_put_str(dict, "event", event_name); ++ timestamp_put(dict); ++ return dict; ++} +diff --git a/qcow2/lib/qapi/qobject-input-visitor.c b/qcow2/lib/qapi/qobject-input-visitor.c +new file mode 100644 +index 00000000..f110a804 +--- /dev/null ++++ b/qcow2/lib/qapi/qobject-input-visitor.c +@@ -0,0 +1,776 @@ ++/* ++ * Input Visitor ++ * ++ * Copyright (C) 2012-2017 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include ++#include "qapi/compat-policy.h" ++#include "qapi/error.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/visitor-impl.h" ++#include "qemu/queue.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/cutils.h" ++#include "qemu/keyval.h" ++ ++typedef struct StackObject { ++ const char *name; /* Name of @obj in its parent, if any */ ++ QObject *obj; /* QDict or QList being visited */ ++ void *qapi; /* sanity check that caller uses same pointer */ ++ ++ GHashTable *h; /* If @obj is QDict: unvisited keys */ ++ const QListEntry *entry; /* If @obj is QList: unvisited tail */ ++ unsigned index; /* If @obj is QList: list index of @entry */ ++ ++ QSLIST_ENTRY(StackObject) node; /* parent */ ++} StackObject; ++ ++struct QObjectInputVisitor { ++ Visitor visitor; ++ ++ /* Root of visit at visitor creation. */ ++ QObject *root; ++ bool keyval; /* Assume @root made with keyval_parse() */ ++ ++ /* Stack of objects being visited (all entries will be either ++ * QDict or QList). */ ++ QSLIST_HEAD(, StackObject) stack; ++ ++ GString *errname; /* Accumulator for full_name() */ ++}; ++ ++static QObjectInputVisitor *to_qiv(Visitor *v) ++{ ++ return container_of(v, QObjectInputVisitor, visitor); ++} ++ ++/* ++ * Find the full name of something @qiv is currently visiting. ++ * @qiv is visiting something named @name in the stack of containers ++ * @qiv->stack. ++ * If @n is zero, return its full name. ++ * If @n is positive, return the full name of the @n-th container ++ * counting from the top. The stack of containers must have at least ++ * @n elements. ++ * The returned string is valid until the next full_name_nth(@v) or ++ * destruction of @v. ++ */ ++static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name, ++ int n) ++{ ++ StackObject *so; ++ char buf[32]; ++ ++ if (qiv->errname) { ++ g_string_truncate(qiv->errname, 0); ++ } else { ++ qiv->errname = g_string_new(""); ++ } ++ ++ QSLIST_FOREACH(so , &qiv->stack, node) { ++ if (n) { ++ n--; ++ } else if (qobject_type(so->obj) == QTYPE_QDICT) { ++ g_string_prepend(qiv->errname, name ?: ""); ++ g_string_prepend_c(qiv->errname, '.'); ++ } else { ++ snprintf(buf, sizeof(buf), ++ qiv->keyval ? ".%u" : "[%u]", ++ so->index); ++ g_string_prepend(qiv->errname, buf); ++ } ++ name = so->name; ++ } ++ assert(!n); ++ ++ if (name) { ++ g_string_prepend(qiv->errname, name); ++ } else if (qiv->errname->str[0] == '.') { ++ g_string_erase(qiv->errname, 0, 1); ++ } else if (!qiv->errname->str[0]) { ++ return ""; ++ } ++ ++ return qiv->errname->str; ++} ++ ++static const char *full_name(QObjectInputVisitor *qiv, const char *name) ++{ ++ return full_name_nth(qiv, name, 0); ++} ++ ++static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv, ++ const char *name, ++ bool consume) ++{ ++ StackObject *tos; ++ QObject *qobj; ++ QObject *ret; ++ ++ if (QSLIST_EMPTY(&qiv->stack)) { ++ /* Starting at root, name is ignored. */ ++ assert(qiv->root); ++ return qiv->root; ++ } ++ ++ /* We are in a container; find the next element. */ ++ tos = QSLIST_FIRST(&qiv->stack); ++ qobj = tos->obj; ++ assert(qobj); ++ ++ if (qobject_type(qobj) == QTYPE_QDICT) { ++ assert(name); ++ ret = qdict_get(qobject_to(QDict, qobj), name); ++ if (tos->h && consume && ret) { ++ bool removed = g_hash_table_remove(tos->h, name); ++ assert(removed); ++ } ++ } else { ++ assert(qobject_type(qobj) == QTYPE_QLIST); ++ assert(!name); ++ if (tos->entry) { ++ ret = qlist_entry_obj(tos->entry); ++ if (consume) { ++ tos->entry = qlist_next(tos->entry); ++ } ++ } else { ++ ret = NULL; ++ } ++ if (consume) { ++ tos->index++; ++ } ++ } ++ ++ return ret; ++} ++ ++static QObject *qobject_input_get_object(QObjectInputVisitor *qiv, ++ const char *name, ++ bool consume, Error **errp) ++{ ++ QObject *obj = qobject_input_try_get_object(qiv, name, consume); ++ ++ if (!obj) { ++ error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name)); ++ } ++ return obj; ++} ++ ++static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv, ++ const char *name, ++ Error **errp) ++{ ++ QObject *qobj; ++ QString *qstr; ++ ++ qobj = qobject_input_get_object(qiv, name, true, errp); ++ if (!qobj) { ++ return NULL; ++ } ++ ++ qstr = qobject_to(QString, qobj); ++ if (!qstr) { ++ switch (qobject_type(qobj)) { ++ case QTYPE_QDICT: ++ case QTYPE_QLIST: ++ error_setg(errp, "Parameters '%s.*' are unexpected", ++ full_name(qiv, name)); ++ return NULL; ++ default: ++ /* Non-string scalar (should this be an assertion?) */ ++ error_setg(errp, "Internal error: parameter %s invalid", ++ full_name(qiv, name)); ++ return NULL; ++ } ++ } ++ ++ return qstring_get_str(qstr); ++} ++ ++static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv, ++ const char *name, ++ QObject *obj, void *qapi) ++{ ++ GHashTable *h; ++ StackObject *tos = g_new0(StackObject, 1); ++ QDict *qdict = qobject_to(QDict, obj); ++ QList *qlist = qobject_to(QList, obj); ++ const QDictEntry *entry; ++ ++ assert(obj); ++ tos->name = name; ++ tos->obj = obj; ++ tos->qapi = qapi; ++ ++ if (qdict) { ++ h = g_hash_table_new(g_str_hash, g_str_equal); ++ for (entry = qdict_first(qdict); ++ entry; ++ entry = qdict_next(qdict, entry)) { ++ g_hash_table_insert(h, (void *)qdict_entry_key(entry), NULL); ++ } ++ tos->h = h; ++ } else { ++ assert(qlist); ++ tos->entry = qlist_first(qlist); ++ tos->index = -1; ++ } ++ ++ QSLIST_INSERT_HEAD(&qiv->stack, tos, node); ++ return tos->entry; ++} ++ ++ ++static bool qobject_input_check_struct(Visitor *v, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ GHashTableIter iter; ++ const char *key; ++ ++ assert(tos && !tos->entry); ++ ++ g_hash_table_iter_init(&iter, tos->h); ++ if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) { ++ error_setg(errp, "Parameter '%s' is unexpected", ++ full_name(qiv, key)); ++ return false; ++ } ++ return true; ++} ++ ++static void qobject_input_stack_object_free(StackObject *tos) ++{ ++ if (tos->h) { ++ g_hash_table_unref(tos->h); ++ } ++ ++ g_free(tos); ++} ++ ++static void qobject_input_pop(Visitor *v, void **obj) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(tos && tos->qapi == obj); ++ QSLIST_REMOVE_HEAD(&qiv->stack, node); ++ qobject_input_stack_object_free(tos); ++} ++ ++static bool qobject_input_start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ ++ if (obj) { ++ *obj = NULL; ++ } ++ if (!qobj) { ++ return false; ++ } ++ if (qobject_type(qobj) != QTYPE_QDICT) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: object", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ qobject_input_push(qiv, name, qobj, obj); ++ ++ if (obj) { ++ *obj = g_malloc0(size); ++ } ++ return true; ++} ++ ++static void qobject_input_end_struct(Visitor *v, void **obj) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(qobject_type(tos->obj) == QTYPE_QDICT && tos->h); ++ qobject_input_pop(v, obj); ++} ++ ++ ++static bool qobject_input_start_list(Visitor *v, const char *name, ++ GenericList **list, size_t size, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ const QListEntry *entry; ++ ++ if (list) { ++ *list = NULL; ++ } ++ if (!qobj) { ++ return false; ++ } ++ if (qobject_type(qobj) != QTYPE_QLIST) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: array", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ entry = qobject_input_push(qiv, name, qobj, list); ++ if (entry && list) { ++ *list = g_malloc0(size); ++ } ++ return true; ++} ++ ++static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail, ++ size_t size) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(tos && qobject_to(QList, tos->obj)); ++ ++ if (!tos->entry) { ++ return NULL; ++ } ++ tail->next = g_malloc0(size); ++ return tail->next; ++} ++ ++static bool qobject_input_check_list(Visitor *v, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(tos && qobject_to(QList, tos->obj)); ++ ++ if (tos->entry) { ++ error_setg(errp, "Only %u list elements expected in %s", ++ tos->index + 1, full_name_nth(qiv, NULL, 1)); ++ return false; ++ } ++ return true; ++} ++ ++static void qobject_input_end_list(Visitor *v, void **obj) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ assert(qobject_type(tos->obj) == QTYPE_QLIST && !tos->h); ++ qobject_input_pop(v, obj); ++} ++ ++static bool qobject_input_start_alternate(Visitor *v, const char *name, ++ GenericAlternate **obj, size_t size, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, false, errp); ++ ++ if (!qobj) { ++ *obj = NULL; ++ return false; ++ } ++ *obj = g_malloc0(size); ++ (*obj)->type = qobject_type(qobj); ++ return true; ++} ++ ++static bool qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QNum *qnum; ++ ++ if (!qobj) { ++ return false; ++ } ++ qnum = qobject_to(QNum, qobj); ++ if (!qnum || !qnum_get_try_int(qnum, obj)) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: integer", ++ full_name(qiv, name)); ++ return false; ++ } ++ return true; ++} ++ ++static bool qobject_input_type_int64_keyval(Visitor *v, const char *name, ++ int64_t *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (qemu_strtoi64(str, NULL, 0, obj) < 0) { ++ /* TODO report -ERANGE more nicely */ ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "integer"); ++ return false; ++ } ++ return true; ++} ++ ++static bool qobject_input_type_uint64(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QNum *qnum; ++ int64_t val; ++ ++ if (!qobj) { ++ return false; ++ } ++ qnum = qobject_to(QNum, qobj); ++ if (!qnum) { ++ goto err; ++ } ++ ++ if (qnum_get_try_uint(qnum, obj)) { ++ return true; ++ } ++ ++ /* Need to accept negative values for backward compatibility */ ++ if (qnum_get_try_int(qnum, &val)) { ++ *obj = val; ++ return true; ++ } ++ ++err: ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "uint64"); ++ return false; ++} ++ ++static bool qobject_input_type_uint64_keyval(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (qemu_strtou64(str, NULL, 0, obj) < 0) { ++ /* TODO report -ERANGE more nicely */ ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "integer"); ++ return false; ++ } ++ return true; ++} ++ ++static bool qobject_input_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QBool *qbool; ++ ++ if (!qobj) { ++ return false; ++ } ++ qbool = qobject_to(QBool, qobj); ++ if (!qbool) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: boolean", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ *obj = qbool_get_bool(qbool); ++ return true; ++} ++ ++static bool qobject_input_type_bool_keyval(Visitor *v, const char *name, ++ bool *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (!qapi_bool_parse(name, str, obj, NULL)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "'on' or 'off'"); ++ return false; ++ } ++ return true; ++} ++ ++static bool qobject_input_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QString *qstr; ++ ++ *obj = NULL; ++ if (!qobj) { ++ return false; ++ } ++ qstr = qobject_to(QString, qobj); ++ if (!qstr) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: string", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ *obj = g_strdup(qstring_get_str(qstr)); ++ return true; ++} ++ ++static bool qobject_input_type_str_keyval(Visitor *v, const char *name, ++ char **obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ *obj = g_strdup(str); ++ return !!str; ++} ++ ++static bool qobject_input_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ QNum *qnum; ++ ++ if (!qobj) { ++ return false; ++ } ++ qnum = qobject_to(QNum, qobj); ++ if (!qnum) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: number", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ *obj = qnum_get_double(qnum); ++ return true; ++} ++ ++static bool qobject_input_type_number_keyval(Visitor *v, const char *name, ++ double *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ double val; ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (qemu_strtod_finite(str, NULL, &val)) { ++ /* TODO report -ERANGE more nicely */ ++ error_setg(errp, "Invalid parameter type for '%s', expected: number", ++ full_name(qiv, name)); ++ return false; ++ } ++ ++ *obj = val; ++ return true; ++} ++ ++static bool qobject_input_type_any(Visitor *v, const char *name, QObject **obj, ++ Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ ++ *obj = NULL; ++ if (!qobj) { ++ return false; ++ } ++ ++ *obj = qobject_ref(qobj); ++ return true; ++} ++ ++static bool qobject_input_type_null(Visitor *v, const char *name, ++ QNull **obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_get_object(qiv, name, true, errp); ++ ++ *obj = NULL; ++ if (!qobj) { ++ return false; ++ } ++ ++ if (qobject_type(qobj) != QTYPE_QNULL) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: null", ++ full_name(qiv, name)); ++ return false; ++ } ++ *obj = qnull(); ++ return true; ++} ++ ++static bool qobject_input_type_size_keyval(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ const char *str = qobject_input_get_keyval(qiv, name, errp); ++ ++ if (!str) { ++ return false; ++ } ++ ++ if (qemu_strtosz(str, NULL, obj) < 0) { ++ /* TODO report -ERANGE more nicely */ ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ full_name(qiv, name), "size"); ++ return false; ++ } ++ return true; ++} ++ ++static void qobject_input_optional(Visitor *v, const char *name, bool *present) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ QObject *qobj = qobject_input_try_get_object(qiv, name, false); ++ ++ if (!qobj) { ++ *present = false; ++ return; ++ } ++ ++ *present = true; ++} ++ ++static bool qobject_input_policy_reject(Visitor *v, const char *name, ++ unsigned special_features, ++ Error **errp) ++{ ++ return !compat_policy_input_ok(special_features, &v->compat_policy, ++ ERROR_CLASS_GENERIC_ERROR, ++ "parameter", name, errp); ++} ++ ++static void qobject_input_free(Visitor *v) ++{ ++ QObjectInputVisitor *qiv = to_qiv(v); ++ ++ while (!QSLIST_EMPTY(&qiv->stack)) { ++ StackObject *tos = QSLIST_FIRST(&qiv->stack); ++ ++ QSLIST_REMOVE_HEAD(&qiv->stack, node); ++ qobject_input_stack_object_free(tos); ++ } ++ ++ qobject_unref(qiv->root); ++ if (qiv->errname) { ++ g_string_free(qiv->errname, TRUE); ++ } ++ g_free(qiv); ++} ++ ++static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) ++{ ++ QObjectInputVisitor *v = g_malloc0(sizeof(*v)); ++ ++ assert(obj); ++ ++ v->visitor.type = VISITOR_INPUT; ++ v->visitor.start_struct = qobject_input_start_struct; ++ v->visitor.check_struct = qobject_input_check_struct; ++ v->visitor.end_struct = qobject_input_end_struct; ++ v->visitor.start_list = qobject_input_start_list; ++ v->visitor.next_list = qobject_input_next_list; ++ v->visitor.check_list = qobject_input_check_list; ++ v->visitor.end_list = qobject_input_end_list; ++ v->visitor.start_alternate = qobject_input_start_alternate; ++ v->visitor.optional = qobject_input_optional; ++ v->visitor.policy_reject = qobject_input_policy_reject; ++ v->visitor.free = qobject_input_free; ++ ++ v->root = qobject_ref(obj); ++ ++ return v; ++} ++ ++Visitor *qobject_input_visitor_new(QObject *obj) ++{ ++ QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); ++ ++ v->visitor.type_int64 = qobject_input_type_int64; ++ v->visitor.type_uint64 = qobject_input_type_uint64; ++ v->visitor.type_bool = qobject_input_type_bool; ++ v->visitor.type_str = qobject_input_type_str; ++ v->visitor.type_number = qobject_input_type_number; ++ v->visitor.type_any = qobject_input_type_any; ++ v->visitor.type_null = qobject_input_type_null; ++ ++ return &v->visitor; ++} ++ ++Visitor *qobject_input_visitor_new_keyval(QObject *obj) ++{ ++ QObjectInputVisitor *v = qobject_input_visitor_base_new(obj); ++ ++ v->visitor.type_int64 = qobject_input_type_int64_keyval; ++ v->visitor.type_uint64 = qobject_input_type_uint64_keyval; ++ v->visitor.type_bool = qobject_input_type_bool_keyval; ++ v->visitor.type_str = qobject_input_type_str_keyval; ++ v->visitor.type_number = qobject_input_type_number_keyval; ++ v->visitor.type_any = qobject_input_type_any; ++ v->visitor.type_null = qobject_input_type_null; ++ v->visitor.type_size = qobject_input_type_size_keyval; ++ v->keyval = true; ++ ++ return &v->visitor; ++} ++ ++Visitor *qobject_input_visitor_new_str(const char *str, ++ const char *implied_key, ++ Error **errp) ++{ ++ bool is_json = str[0] == '{'; ++ QObject *obj; ++ QDict *args; ++ Visitor *v; ++ ++ if (is_json) { ++ obj = qobject_from_json(str, errp); ++ if (!obj) { ++ return NULL; ++ } ++ args = qobject_to(QDict, obj); ++ assert(args); ++ v = qobject_input_visitor_new(QOBJECT(args)); ++ } else { ++ args = keyval_parse(str, implied_key, NULL, errp); ++ if (!args) { ++ return NULL; ++ } ++ v = qobject_input_visitor_new_keyval(QOBJECT(args)); ++ } ++ qobject_unref(args); ++ ++ return v; ++} +diff --git a/qcow2/lib/qapi/qobject-output-visitor.c b/qcow2/lib/qapi/qobject-output-visitor.c +new file mode 100644 +index 00000000..74770edd +--- /dev/null ++++ b/qcow2/lib/qapi/qobject-output-visitor.c +@@ -0,0 +1,280 @@ ++/* ++ * Core Definitions for QAPI/QMP Command Registry ++ * ++ * Copyright (C) 2012-2016 Red Hat, Inc. ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/compat-policy.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/visitor-impl.h" ++#include "qemu/queue.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++ ++typedef struct QStackEntry { ++ QObject *value; ++ void *qapi; /* sanity check that caller uses same pointer */ ++ QSLIST_ENTRY(QStackEntry) node; ++} QStackEntry; ++ ++struct QObjectOutputVisitor { ++ Visitor visitor; ++ ++ QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */ ++ QObject *root; /* Root of the output visit */ ++ QObject **result; /* User's storage location for result */ ++}; ++ ++#define qobject_output_add(qov, name, value) \ ++ qobject_output_add_obj(qov, name, QOBJECT(value)) ++#define qobject_output_push(qov, value, qapi) \ ++ qobject_output_push_obj(qov, QOBJECT(value), qapi) ++ ++static QObjectOutputVisitor *to_qov(Visitor *v) ++{ ++ return container_of(v, QObjectOutputVisitor, visitor); ++} ++ ++/* Push @value onto the stack of current QObjects being built */ ++static void qobject_output_push_obj(QObjectOutputVisitor *qov, QObject *value, ++ void *qapi) ++{ ++ QStackEntry *e = g_malloc0(sizeof(*e)); ++ ++ assert(qov->root); ++ assert(value); ++ e->value = value; ++ e->qapi = qapi; ++ QSLIST_INSERT_HEAD(&qov->stack, e, node); ++} ++ ++/* Pop a value off the stack of QObjects being built, and return it. */ ++static QObject *qobject_output_pop(QObjectOutputVisitor *qov, void *qapi) ++{ ++ QStackEntry *e = QSLIST_FIRST(&qov->stack); ++ QObject *value; ++ ++ assert(e); ++ assert(e->qapi == qapi); ++ QSLIST_REMOVE_HEAD(&qov->stack, node); ++ value = e->value; ++ assert(value); ++ g_free(e); ++ return value; ++} ++ ++/* Add @value to the current QObject being built. ++ * If the stack is visiting a dictionary or list, @value is now owned ++ * by that container. Otherwise, @value is now the root. */ ++static void qobject_output_add_obj(QObjectOutputVisitor *qov, const char *name, ++ QObject *value) ++{ ++ QStackEntry *e = QSLIST_FIRST(&qov->stack); ++ QObject *cur = e ? e->value : NULL; ++ ++ if (!cur) { ++ /* Don't allow reuse of visitor on more than one root */ ++ assert(!qov->root); ++ qov->root = value; ++ } else { ++ switch (qobject_type(cur)) { ++ case QTYPE_QDICT: ++ assert(name); ++ qdict_put_obj(qobject_to(QDict, cur), name, value); ++ break; ++ case QTYPE_QLIST: ++ assert(!name); ++ qlist_append_obj(qobject_to(QList, cur), value); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++} ++ ++static bool qobject_output_start_struct(Visitor *v, const char *name, ++ void **obj, size_t unused, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QDict *dict = qdict_new(); ++ ++ qobject_output_add(qov, name, dict); ++ qobject_output_push(qov, dict, obj); ++ return true; ++} ++ ++static void qobject_output_end_struct(Visitor *v, void **obj) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QObject *value = qobject_output_pop(qov, obj); ++ assert(qobject_type(value) == QTYPE_QDICT); ++} ++ ++static bool qobject_output_start_list(Visitor *v, const char *name, ++ GenericList **listp, size_t size, ++ Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QList *list = qlist_new(); ++ ++ qobject_output_add(qov, name, list); ++ qobject_output_push(qov, list, listp); ++ return true; ++} ++ ++static GenericList *qobject_output_next_list(Visitor *v, GenericList *tail, ++ size_t size) ++{ ++ return tail->next; ++} ++ ++static void qobject_output_end_list(Visitor *v, void **obj) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QObject *value = qobject_output_pop(qov, obj); ++ assert(qobject_type(value) == QTYPE_QLIST); ++} ++ ++static bool qobject_output_type_int64(Visitor *v, const char *name, ++ int64_t *obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qnum_from_int(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_uint64(Visitor *v, const char *name, ++ uint64_t *obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qnum_from_uint(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qbool_from_bool(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ if (*obj) { ++ qobject_output_add(qov, name, qstring_from_str(*obj)); ++ } else { ++ qobject_output_add(qov, name, qstring_from_str("")); ++ } ++ return true; ++} ++ ++static bool qobject_output_type_number(Visitor *v, const char *name, ++ double *obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qnum_from_double(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_any(Visitor *v, const char *name, ++ QObject **obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ ++ qobject_output_add_obj(qov, name, qobject_ref(*obj)); ++ return true; ++} ++ ++static bool qobject_output_type_null(Visitor *v, const char *name, ++ QNull **obj, Error **errp) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ qobject_output_add(qov, name, qnull()); ++ return true; ++} ++ ++static bool qobject_output_policy_skip(Visitor *v, const char *name, ++ unsigned special_features) ++{ ++ CompatPolicy *pol = &v->compat_policy; ++ ++ return ((special_features & 1u << QAPI_DEPRECATED) ++ && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) ++ || ((special_features & 1u << QAPI_UNSTABLE) ++ && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE); ++} ++ ++/* Finish building, and return the root object. ++ * The root object is never null. The caller becomes the object's ++ * owner, and should use qobject_unref() when done with it. */ ++static void qobject_output_complete(Visitor *v, void *opaque) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ ++ /* A visit must have occurred, with each start paired with end. */ ++ assert(qov->root && QSLIST_EMPTY(&qov->stack)); ++ assert(opaque == qov->result); ++ ++ *qov->result = qobject_ref(qov->root); ++ qov->result = NULL; ++} ++ ++static void qobject_output_free(Visitor *v) ++{ ++ QObjectOutputVisitor *qov = to_qov(v); ++ QStackEntry *e; ++ ++ while (!QSLIST_EMPTY(&qov->stack)) { ++ e = QSLIST_FIRST(&qov->stack); ++ QSLIST_REMOVE_HEAD(&qov->stack, node); ++ g_free(e); ++ } ++ ++ qobject_unref(qov->root); ++ g_free(qov); ++} ++ ++Visitor *qobject_output_visitor_new(QObject **result) ++{ ++ QObjectOutputVisitor *v; ++ ++ v = g_malloc0(sizeof(*v)); ++ ++ v->visitor.type = VISITOR_OUTPUT; ++ v->visitor.start_struct = qobject_output_start_struct; ++ v->visitor.end_struct = qobject_output_end_struct; ++ v->visitor.start_list = qobject_output_start_list; ++ v->visitor.next_list = qobject_output_next_list; ++ v->visitor.end_list = qobject_output_end_list; ++ v->visitor.type_int64 = qobject_output_type_int64; ++ v->visitor.type_uint64 = qobject_output_type_uint64; ++ v->visitor.type_bool = qobject_output_type_bool; ++ v->visitor.type_str = qobject_output_type_str; ++ v->visitor.type_number = qobject_output_type_number; ++ v->visitor.type_any = qobject_output_type_any; ++ v->visitor.type_null = qobject_output_type_null; ++ v->visitor.policy_skip = qobject_output_policy_skip; ++ v->visitor.complete = qobject_output_complete; ++ v->visitor.free = qobject_output_free; ++ ++ *result = NULL; ++ v->result = result; ++ ++ return &v->visitor; ++} +diff --git a/qcow2/lib/qapi/string-input-visitor.c b/qcow2/lib/qapi/string-input-visitor.c +new file mode 100644 +index 00000000..3f1b9e9b +--- /dev/null ++++ b/qcow2/lib/qapi/string-input-visitor.c +@@ -0,0 +1,414 @@ ++/* ++ * String parsing visitor ++ * ++ * Copyright Red Hat, Inc. 2012-2016 ++ * ++ * Author: Paolo Bonzini ++ * David Hildenbrand ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi/string-input-visitor.h" ++#include "qapi/visitor-impl.h" ++#include "qapi/qmp/qerror.h" ++#include "qapi/qmp/qnull.h" ++#include "qemu/option.h" ++#include "qemu/cutils.h" ++ ++typedef enum ListMode { ++ /* no list parsing active / no list expected */ ++ LM_NONE, ++ /* we have an unparsed string remaining */ ++ LM_UNPARSED, ++ /* we have an unfinished int64 range */ ++ LM_INT64_RANGE, ++ /* we have an unfinished uint64 range */ ++ LM_UINT64_RANGE, ++ /* we have parsed the string completely and no range is remaining */ ++ LM_END, ++} ListMode; ++ ++/* protect against DOS attacks, limit the amount of elements per range */ ++#define RANGE_MAX_ELEMENTS 65536 ++ ++typedef union RangeElement { ++ int64_t i64; ++ uint64_t u64; ++} RangeElement; ++ ++struct StringInputVisitor ++{ ++ Visitor visitor; ++ ++ /* List parsing state */ ++ ListMode lm; ++ RangeElement rangeNext; ++ RangeElement rangeEnd; ++ const char *unparsed_string; ++ void *list; ++ ++ /* The original string to parse */ ++ const char *string; ++}; ++ ++static StringInputVisitor *to_siv(Visitor *v) ++{ ++ return container_of(v, StringInputVisitor, visitor); ++} ++ ++static bool start_list(Visitor *v, const char *name, GenericList **list, ++ size_t size, Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm == LM_NONE); ++ siv->list = list; ++ siv->unparsed_string = siv->string; ++ ++ if (!siv->string[0]) { ++ if (list) { ++ *list = NULL; ++ } ++ siv->lm = LM_END; ++ } else { ++ if (list) { ++ *list = g_malloc0(size); ++ } ++ siv->lm = LM_UNPARSED; ++ } ++ return true; ++} ++ ++static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ switch (siv->lm) { ++ case LM_END: ++ return NULL; ++ case LM_INT64_RANGE: ++ case LM_UINT64_RANGE: ++ case LM_UNPARSED: ++ /* we have an unparsed string or something left in a range */ ++ break; ++ default: ++ abort(); ++ } ++ ++ tail->next = g_malloc0(size); ++ return tail->next; ++} ++ ++static bool check_list(Visitor *v, Error **errp) ++{ ++ const StringInputVisitor *siv = to_siv(v); ++ ++ switch (siv->lm) { ++ case LM_INT64_RANGE: ++ case LM_UINT64_RANGE: ++ case LM_UNPARSED: ++ error_setg(errp, "Fewer list elements expected"); ++ return false; ++ case LM_END: ++ return true; ++ default: ++ abort(); ++ } ++} ++ ++static void end_list(Visitor *v, void **obj) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm != LM_NONE); ++ assert(siv->list == obj); ++ siv->list = NULL; ++ siv->unparsed_string = NULL; ++ siv->lm = LM_NONE; ++} ++ ++static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj) ++{ ++ const char *endptr; ++ int64_t start, end; ++ ++ /* parse a simple int64 or range */ ++ if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) { ++ return -EINVAL; ++ } ++ end = start; ++ ++ switch (endptr[0]) { ++ case '\0': ++ siv->unparsed_string = endptr; ++ break; ++ case ',': ++ siv->unparsed_string = endptr + 1; ++ break; ++ case '-': ++ /* parse the end of the range */ ++ if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) { ++ return -EINVAL; ++ } ++ if (start > end || end - start >= RANGE_MAX_ELEMENTS) { ++ return -EINVAL; ++ } ++ switch (endptr[0]) { ++ case '\0': ++ siv->unparsed_string = endptr; ++ break; ++ case ',': ++ siv->unparsed_string = endptr + 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* we have a proper range (with maybe only one element) */ ++ siv->lm = LM_INT64_RANGE; ++ siv->rangeNext.i64 = start; ++ siv->rangeEnd.i64 = end; ++ return 0; ++} ++ ++static bool parse_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ int64_t val; ++ ++ switch (siv->lm) { ++ case LM_NONE: ++ /* just parse a simple int64, bail out if not completely consumed */ ++ if (qemu_strtoi64(siv->string, NULL, 0, &val)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, ++ name ? name : "null", "int64"); ++ return false; ++ } ++ *obj = val; ++ return true; ++ case LM_UNPARSED: ++ if (try_parse_int64_list_entry(siv, obj)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", ++ "list of int64 values or ranges"); ++ return false; ++ } ++ assert(siv->lm == LM_INT64_RANGE); ++ /* fall through */ ++ case LM_INT64_RANGE: ++ /* return the next element in the range */ ++ assert(siv->rangeNext.i64 <= siv->rangeEnd.i64); ++ *obj = siv->rangeNext.i64++; ++ ++ if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) { ++ /* end of range, check if there is more to parse */ ++ siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; ++ } ++ return true; ++ case LM_END: ++ error_setg(errp, "Fewer list elements expected"); ++ return false; ++ default: ++ abort(); ++ } ++} ++ ++static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj) ++{ ++ const char *endptr; ++ uint64_t start, end; ++ ++ /* parse a simple uint64 or range */ ++ if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) { ++ return -EINVAL; ++ } ++ end = start; ++ ++ switch (endptr[0]) { ++ case '\0': ++ siv->unparsed_string = endptr; ++ break; ++ case ',': ++ siv->unparsed_string = endptr + 1; ++ break; ++ case '-': ++ /* parse the end of the range */ ++ if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) { ++ return -EINVAL; ++ } ++ if (start > end || end - start >= RANGE_MAX_ELEMENTS) { ++ return -EINVAL; ++ } ++ switch (endptr[0]) { ++ case '\0': ++ siv->unparsed_string = endptr; ++ break; ++ case ',': ++ siv->unparsed_string = endptr + 1; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* we have a proper range (with maybe only one element) */ ++ siv->lm = LM_UINT64_RANGE; ++ siv->rangeNext.u64 = start; ++ siv->rangeEnd.u64 = end; ++ return 0; ++} ++ ++static bool parse_type_uint64(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ uint64_t val; ++ ++ switch (siv->lm) { ++ case LM_NONE: ++ /* just parse a simple uint64, bail out if not completely consumed */ ++ if (qemu_strtou64(siv->string, NULL, 0, &val)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", ++ "uint64"); ++ return false; ++ } ++ *obj = val; ++ return true; ++ case LM_UNPARSED: ++ if (try_parse_uint64_list_entry(siv, obj)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", ++ "list of uint64 values or ranges"); ++ return false; ++ } ++ assert(siv->lm == LM_UINT64_RANGE); ++ /* fall through */ ++ case LM_UINT64_RANGE: ++ /* return the next element in the range */ ++ assert(siv->rangeNext.u64 <= siv->rangeEnd.u64); ++ *obj = siv->rangeNext.u64++; ++ ++ if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) { ++ /* end of range, check if there is more to parse */ ++ siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; ++ } ++ return true; ++ case LM_END: ++ error_setg(errp, "Fewer list elements expected"); ++ return false; ++ default: ++ abort(); ++ } ++} ++ ++static bool parse_type_size(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ uint64_t val; ++ ++ assert(siv->lm == LM_NONE); ++ if (!parse_option_size(name, siv->string, &val, errp)) { ++ return false; ++ } ++ ++ *obj = val; ++ return true; ++} ++ ++static bool parse_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm == LM_NONE); ++ return qapi_bool_parse(name ? name : "null", siv->string, obj, errp); ++} ++ ++static bool parse_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm == LM_NONE); ++ *obj = g_strdup(siv->string); ++ return true; ++} ++ ++static bool parse_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ double val; ++ ++ assert(siv->lm == LM_NONE); ++ if (qemu_strtod_finite(siv->string, NULL, &val)) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: number", ++ name ? name : "null"); ++ return false; ++ } ++ ++ *obj = val; ++ return true; ++} ++ ++static bool parse_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ assert(siv->lm == LM_NONE); ++ *obj = NULL; ++ ++ if (siv->string[0]) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: null", ++ name ? name : "null"); ++ return false; ++ } ++ ++ *obj = qnull(); ++ return true; ++} ++ ++static void string_input_free(Visitor *v) ++{ ++ StringInputVisitor *siv = to_siv(v); ++ ++ g_free(siv); ++} ++ ++Visitor *string_input_visitor_new(const char *str) ++{ ++ StringInputVisitor *v; ++ ++ assert(str); ++ v = g_malloc0(sizeof(*v)); ++ ++ v->visitor.type = VISITOR_INPUT; ++ v->visitor.type_int64 = parse_type_int64; ++ v->visitor.type_uint64 = parse_type_uint64; ++ v->visitor.type_size = parse_type_size; ++ v->visitor.type_bool = parse_type_bool; ++ v->visitor.type_str = parse_type_str; ++ v->visitor.type_number = parse_type_number; ++ v->visitor.type_null = parse_type_null; ++ v->visitor.start_list = start_list; ++ v->visitor.next_list = next_list; ++ v->visitor.check_list = check_list; ++ v->visitor.end_list = end_list; ++ v->visitor.free = string_input_free; ++ ++ v->string = str; ++ v->lm = LM_NONE; ++ return &v->visitor; ++} +diff --git a/qcow2/lib/qapi/string-output-visitor.c b/qcow2/lib/qapi/string-output-visitor.c +new file mode 100644 +index 00000000..5115536b +--- /dev/null ++++ b/qcow2/lib/qapi/string-output-visitor.c +@@ -0,0 +1,451 @@ ++/* ++ * String printing Visitor ++ * ++ * Copyright Red Hat, Inc. 2012-2016 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "qapi/string-output-visitor.h" ++#include "qapi/visitor-impl.h" ++#include ++#include "qemu/range.h" ++ ++enum ListMode { ++ LM_NONE, /* not traversing a list of repeated options */ ++ LM_STARTED, /* next_list() ready to be called */ ++ ++ LM_IN_PROGRESS, /* next_list() has been called. ++ * ++ * Generating the next list link will consume the most ++ * recently parsed QemuOpt instance of the repeated ++ * option. ++ * ++ * Parsing a value into the list link will examine the ++ * next QemuOpt instance of the repeated option, and ++ * possibly enter LM_SIGNED_INTERVAL or ++ * LM_UNSIGNED_INTERVAL. ++ */ ++ ++ LM_SIGNED_INTERVAL, /* next_list() has been called. ++ * ++ * Generating the next list link will consume the most ++ * recently stored element from the signed interval, ++ * parsed from the most recent QemuOpt instance of the ++ * repeated option. This may consume QemuOpt itself ++ * and return to LM_IN_PROGRESS. ++ * ++ * Parsing a value into the list link will store the ++ * next element of the signed interval. ++ */ ++ ++ LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */ ++ ++ LM_END, /* next_list() called, about to see last element. */ ++}; ++ ++typedef enum ListMode ListMode; ++ ++struct StringOutputVisitor ++{ ++ Visitor visitor; ++ bool human; ++ GString *string; ++ char **result; ++ ListMode list_mode; ++ union { ++ int64_t s; ++ uint64_t u; ++ } range_start, range_end; ++ GList *ranges; ++ void *list; /* Only needed for sanity checking the caller */ ++ unsigned int struct_nesting; ++}; ++ ++static StringOutputVisitor *to_sov(Visitor *v) ++{ ++ return container_of(v, StringOutputVisitor, visitor); ++} ++ ++static void string_output_set(StringOutputVisitor *sov, char *string) ++{ ++ switch (sov->list_mode) { ++ case LM_STARTED: ++ sov->list_mode = LM_IN_PROGRESS; ++ /* fall through */ ++ case LM_NONE: ++ if (sov->string) { ++ g_string_free(sov->string, true); ++ } ++ sov->string = g_string_new(string); ++ g_free(string); ++ break; ++ ++ case LM_IN_PROGRESS: ++ case LM_END: ++ g_string_append(sov->string, ", "); ++ g_string_append(sov->string, string); ++ break; ++ ++ default: ++ abort(); ++ } ++} ++ ++static void string_output_append(StringOutputVisitor *sov, int64_t a) ++{ ++ Range *r = g_malloc0(sizeof(*r)); ++ ++ range_set_bounds(r, a, a); ++ sov->ranges = range_list_insert(sov->ranges, r); ++} ++ ++static void string_output_append_range(StringOutputVisitor *sov, ++ int64_t s, int64_t e) ++{ ++ Range *r = g_malloc0(sizeof(*r)); ++ ++ range_set_bounds(r, s, e); ++ sov->ranges = range_list_insert(sov->ranges, r); ++} ++ ++static void format_string(StringOutputVisitor *sov, Range *r, bool next, ++ bool human) ++{ ++ if (range_lob(r) != range_upb(r)) { ++ if (human) { ++ g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64, ++ range_lob(r), range_upb(r)); ++ ++ } else { ++ g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64, ++ range_lob(r), range_upb(r)); ++ } ++ } else { ++ if (human) { ++ g_string_append_printf(sov->string, "0x%" PRIx64, range_lob(r)); ++ } else { ++ g_string_append_printf(sov->string, "%" PRId64, range_lob(r)); ++ } ++ } ++ if (next) { ++ g_string_append(sov->string, ","); ++ } ++} ++ ++static bool print_type_int64(Visitor *v, const char *name, int64_t *obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ GList *l; ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ switch (sov->list_mode) { ++ case LM_NONE: ++ string_output_append(sov, *obj); ++ break; ++ ++ case LM_STARTED: ++ sov->range_start.s = *obj; ++ sov->range_end.s = *obj; ++ sov->list_mode = LM_IN_PROGRESS; ++ return true; ++ ++ case LM_IN_PROGRESS: ++ if (sov->range_end.s + 1 == *obj) { ++ sov->range_end.s++; ++ } else { ++ if (sov->range_start.s == sov->range_end.s) { ++ string_output_append(sov, sov->range_end.s); ++ } else { ++ assert(sov->range_start.s < sov->range_end.s); ++ string_output_append_range(sov, sov->range_start.s, ++ sov->range_end.s); ++ } ++ ++ sov->range_start.s = *obj; ++ sov->range_end.s = *obj; ++ } ++ return true; ++ ++ case LM_END: ++ if (sov->range_end.s + 1 == *obj) { ++ sov->range_end.s++; ++ assert(sov->range_start.s < sov->range_end.s); ++ string_output_append_range(sov, sov->range_start.s, ++ sov->range_end.s); ++ } else { ++ if (sov->range_start.s == sov->range_end.s) { ++ string_output_append(sov, sov->range_end.s); ++ } else { ++ assert(sov->range_start.s < sov->range_end.s); ++ ++ string_output_append_range(sov, sov->range_start.s, ++ sov->range_end.s); ++ } ++ string_output_append(sov, *obj); ++ } ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ l = sov->ranges; ++ while (l) { ++ Range *r = l->data; ++ format_string(sov, r, l->next != NULL, false); ++ l = l->next; ++ } ++ ++ if (sov->human) { ++ l = sov->ranges; ++ g_string_append(sov->string, " ("); ++ while (l) { ++ Range *r = l->data; ++ format_string(sov, r, l->next != NULL, true); ++ l = l->next; ++ } ++ g_string_append(sov->string, ")"); ++ } ++ ++ return true; ++} ++ ++static bool print_type_uint64(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ /* FIXME: print_type_int64 mishandles values over INT64_MAX */ ++ int64_t i = *obj; ++ return print_type_int64(v, name, &i, errp); ++} ++ ++static bool print_type_size(Visitor *v, const char *name, uint64_t *obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ uint64_t val; ++ char *out, *psize; ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ if (!sov->human) { ++ out = g_strdup_printf("%"PRIu64, *obj); ++ string_output_set(sov, out); ++ return true; ++ } ++ ++ val = *obj; ++ psize = size_to_str(val); ++ out = g_strdup_printf("%"PRIu64" (%s)", val, psize); ++ string_output_set(sov, out); ++ ++ g_free(psize); ++ return true; ++} ++ ++static bool print_type_bool(Visitor *v, const char *name, bool *obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ string_output_set(sov, g_strdup(*obj ? "true" : "false")); ++ return true; ++} ++ ++static bool print_type_str(Visitor *v, const char *name, char **obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ char *out; ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ if (sov->human) { ++ out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup(""); ++ } else { ++ out = g_strdup(*obj ? *obj : ""); ++ } ++ string_output_set(sov, out); ++ return true; ++} ++ ++static bool print_type_number(Visitor *v, const char *name, double *obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ string_output_set(sov, g_strdup_printf("%.17g", *obj)); ++ return true; ++} ++ ++static bool print_type_null(Visitor *v, const char *name, QNull **obj, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ char *out; ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ if (sov->human) { ++ out = g_strdup(""); ++ } else { ++ out = g_strdup(""); ++ } ++ string_output_set(sov, out); ++ return true; ++} ++ ++static bool start_struct(Visitor *v, const char *name, void **obj, ++ size_t size, Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ sov->struct_nesting++; ++ return true; ++} ++ ++static void end_struct(Visitor *v, void **obj) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (--sov->struct_nesting) { ++ return; ++ } ++ ++ /* TODO actually print struct fields */ ++ string_output_set(sov, g_strdup("")); ++} ++ ++static bool ++start_list(Visitor *v, const char *name, GenericList **list, size_t size, ++ Error **errp) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->struct_nesting) { ++ return true; ++ } ++ ++ /* we can't traverse a list in a list */ ++ assert(sov->list_mode == LM_NONE); ++ /* We don't support visits without a list */ ++ assert(list); ++ sov->list = list; ++ /* List handling is only needed if there are at least two elements */ ++ if (*list && (*list)->next) { ++ sov->list_mode = LM_STARTED; ++ } ++ return true; ++} ++ ++static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ GenericList *ret = tail->next; ++ ++ if (sov->struct_nesting) { ++ return ret; ++ } ++ ++ if (ret && !ret->next) { ++ sov->list_mode = LM_END; ++ } ++ return ret; ++} ++ ++static void end_list(Visitor *v, void **obj) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->struct_nesting) { ++ return; ++ } ++ ++ assert(sov->list == obj); ++ assert(sov->list_mode == LM_STARTED || ++ sov->list_mode == LM_END || ++ sov->list_mode == LM_NONE || ++ sov->list_mode == LM_IN_PROGRESS); ++ sov->list_mode = LM_NONE; ++} ++ ++static void string_output_complete(Visitor *v, void *opaque) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ assert(opaque == sov->result); ++ *sov->result = g_string_free(sov->string, false); ++ sov->string = NULL; ++} ++ ++static void free_range(void *range, void *dummy) ++{ ++ g_free(range); ++} ++ ++static void string_output_free(Visitor *v) ++{ ++ StringOutputVisitor *sov = to_sov(v); ++ ++ if (sov->string) { ++ g_string_free(sov->string, true); ++ } ++ ++ g_list_foreach(sov->ranges, free_range, NULL); ++ g_list_free(sov->ranges); ++ g_free(sov); ++} ++ ++Visitor *string_output_visitor_new(bool human, char **result) ++{ ++ StringOutputVisitor *v; ++ ++ v = g_malloc0(sizeof(*v)); ++ ++ v->string = g_string_new(NULL); ++ v->human = human; ++ v->result = result; ++ *result = NULL; ++ ++ v->visitor.type = VISITOR_OUTPUT; ++ v->visitor.type_int64 = print_type_int64; ++ v->visitor.type_uint64 = print_type_uint64; ++ v->visitor.type_size = print_type_size; ++ v->visitor.type_bool = print_type_bool; ++ v->visitor.type_str = print_type_str; ++ v->visitor.type_number = print_type_number; ++ v->visitor.type_null = print_type_null; ++ v->visitor.start_struct = start_struct; ++ v->visitor.end_struct = end_struct; ++ v->visitor.start_list = start_list; ++ v->visitor.next_list = next_list; ++ v->visitor.end_list = end_list; ++ v->visitor.complete = string_output_complete; ++ v->visitor.free = string_output_free; ++ ++ return &v->visitor; ++} +diff --git a/qcow2/lib/qcow2-bitmap.c b/qcow2/lib/qcow2-bitmap.c +new file mode 100644 +index 00000000..256ec998 +--- /dev/null ++++ b/qcow2/lib/qcow2-bitmap.c +@@ -0,0 +1,1809 @@ ++/* ++ * Bitmaps for the QCOW version 2 format ++ * ++ * Copyright (c) 2014-2017 Vladimir Sementsov-Ogievskiy ++ * ++ * This file is derived from qcow2-snapshot.c, original copyright: ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block-io.h" ++#include "block/dirty-bitmap.h" ++#include "qapi/error.h" ++#include "qemu/cutils.h" ++ ++#include "qcow2.h" ++ ++/* NOTICE: BME here means Bitmaps Extension and used as a namespace for ++ * _internal_ constants. Please do not use this _internal_ abbreviation for ++ * other needs and/or outside of this file. */ ++ ++/* Bitmap directory entry constraints */ ++#define BME_MAX_TABLE_SIZE 0x8000000 ++#define BME_MAX_PHYS_SIZE 0x20000000 /* restrict BdrvDirtyBitmap size in RAM */ ++#define BME_MAX_GRANULARITY_BITS 31 ++#define BME_MIN_GRANULARITY_BITS 9 ++#define BME_MAX_NAME_SIZE 1023 ++ ++/* Size of bitmap table entries */ ++#define BME_TABLE_ENTRY_SIZE (sizeof(uint64_t)) ++ ++QEMU_BUILD_BUG_ON(BME_MAX_NAME_SIZE != BDRV_BITMAP_MAX_NAME_SIZE); ++ ++#if BME_MAX_TABLE_SIZE * 8ULL > INT_MAX ++#error In the code bitmap table physical size assumed to fit into int ++#endif ++ ++/* Bitmap directory entry flags */ ++#define BME_RESERVED_FLAGS 0xfffffffcU ++#define BME_FLAG_IN_USE (1U << 0) ++#define BME_FLAG_AUTO (1U << 1) ++ ++/* bits [1, 8] U [56, 63] are reserved */ ++#define BME_TABLE_ENTRY_RESERVED_MASK 0xff000000000001feULL ++#define BME_TABLE_ENTRY_OFFSET_MASK 0x00fffffffffffe00ULL ++#define BME_TABLE_ENTRY_FLAG_ALL_ONES (1ULL << 0) ++ ++typedef struct QEMU_PACKED Qcow2BitmapDirEntry { ++ /* header is 8 byte aligned */ ++ uint64_t bitmap_table_offset; ++ ++ uint32_t bitmap_table_size; ++ uint32_t flags; ++ ++ uint8_t type; ++ uint8_t granularity_bits; ++ uint16_t name_size; ++ uint32_t extra_data_size; ++ /* extra data follows */ ++ /* name follows */ ++} Qcow2BitmapDirEntry; ++ ++typedef struct Qcow2BitmapTable { ++ uint64_t offset; ++ uint32_t size; /* number of 64bit entries */ ++ QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry; ++} Qcow2BitmapTable; ++ ++typedef struct Qcow2Bitmap { ++ Qcow2BitmapTable table; ++ uint32_t flags; ++ uint8_t granularity_bits; ++ char *name; ++ ++ BdrvDirtyBitmap *dirty_bitmap; ++ ++ QSIMPLEQ_ENTRY(Qcow2Bitmap) entry; ++} Qcow2Bitmap; ++typedef QSIMPLEQ_HEAD(Qcow2BitmapList, Qcow2Bitmap) Qcow2BitmapList; ++ ++typedef enum BitmapType { ++ BT_DIRTY_TRACKING_BITMAP = 1 ++} BitmapType; ++ ++static inline bool can_write(BlockDriverState *bs) ++{ ++ return !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); ++} ++ ++static int GRAPH_RDLOCK update_header_sync(BlockDriverState *bs) ++{ ++ int ret; ++ ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return bdrv_flush(bs->file->bs); ++} ++ ++static inline void bitmap_table_bswap_be(uint64_t *bitmap_table, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; ++i) { ++ bitmap_table[i] = cpu_to_be64(bitmap_table[i]); ++ } ++} ++ ++static int check_table_entry(uint64_t entry, int cluster_size) ++{ ++ uint64_t offset; ++ ++ if (entry & BME_TABLE_ENTRY_RESERVED_MASK) { ++ return -EINVAL; ++ } ++ ++ offset = entry & BME_TABLE_ENTRY_OFFSET_MASK; ++ if (offset != 0) { ++ /* if offset specified, bit 0 is reserved */ ++ if (entry & BME_TABLE_ENTRY_FLAG_ALL_ONES) { ++ return -EINVAL; ++ } ++ ++ if (offset % cluster_size != 0) { ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++static int64_t get_bitmap_bytes_needed(int64_t len, uint32_t granularity) ++{ ++ int64_t num_bits = DIV_ROUND_UP(len, granularity); ++ ++ return DIV_ROUND_UP(num_bits, 8); ++} ++ ++static int GRAPH_RDLOCK ++check_constraints_on_bitmap(BlockDriverState *bs, const char *name, ++ uint32_t granularity, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int granularity_bits = ctz32(granularity); ++ int64_t len = bdrv_getlength(bs); ++ int64_t bitmap_bytes; ++ ++ assert(granularity > 0); ++ assert((granularity & (granularity - 1)) == 0); ++ ++ if (len < 0) { ++ error_setg_errno(errp, -len, "Failed to get size of '%s'", ++ bdrv_get_device_or_node_name(bs)); ++ return len; ++ } ++ ++ if (granularity_bits > BME_MAX_GRANULARITY_BITS) { ++ error_setg(errp, "Granularity exceeds maximum (%llu bytes)", ++ 1ULL << BME_MAX_GRANULARITY_BITS); ++ return -EINVAL; ++ } ++ if (granularity_bits < BME_MIN_GRANULARITY_BITS) { ++ error_setg(errp, "Granularity is under minimum (%llu bytes)", ++ 1ULL << BME_MIN_GRANULARITY_BITS); ++ return -EINVAL; ++ } ++ ++ bitmap_bytes = get_bitmap_bytes_needed(len, granularity); ++ if ((bitmap_bytes > (uint64_t)BME_MAX_PHYS_SIZE) || ++ (bitmap_bytes > (uint64_t)BME_MAX_TABLE_SIZE * s->cluster_size)) ++ { ++ error_setg(errp, "Too much space will be occupied by the bitmap. " ++ "Use larger granularity"); ++ return -EINVAL; ++ } ++ ++ if (strlen(name) > BME_MAX_NAME_SIZE) { ++ error_setg(errp, "Name length exceeds maximum (%u characters)", ++ BME_MAX_NAME_SIZE); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void GRAPH_RDLOCK ++clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table, ++ uint32_t bitmap_table_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ ++ for (i = 0; i < bitmap_table_size; ++i) { ++ uint64_t addr = bitmap_table[i] & BME_TABLE_ENTRY_OFFSET_MASK; ++ if (!addr) { ++ continue; ++ } ++ ++ qcow2_free_clusters(bs, addr, s->cluster_size, QCOW2_DISCARD_ALWAYS); ++ bitmap_table[i] = 0; ++ } ++} ++ ++static int GRAPH_RDLOCK ++bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, ++ uint64_t **bitmap_table) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ uint32_t i; ++ uint64_t *table; ++ ++ assert(tb->size != 0); ++ table = g_try_new(uint64_t, tb->size); ++ if (table == NULL) { ++ return -ENOMEM; ++ } ++ ++ assert(tb->size <= BME_MAX_TABLE_SIZE); ++ ret = bdrv_pread(bs->file, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE, ++ table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (i = 0; i < tb->size; ++i) { ++ table[i] = be64_to_cpu(table[i]); ++ ret = check_table_entry(table[i], s->cluster_size); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ *bitmap_table = table; ++ return 0; ++ ++fail: ++ g_free(table); ++ ++ return ret; ++} ++ ++static int GRAPH_RDLOCK ++free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) ++{ ++ int ret; ++ uint64_t *bitmap_table; ++ ++ ret = bitmap_table_load(bs, tb, &bitmap_table); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ clear_bitmap_table(bs, bitmap_table, tb->size); ++ qcow2_free_clusters(bs, tb->offset, tb->size * BME_TABLE_ENTRY_SIZE, ++ QCOW2_DISCARD_OTHER); ++ g_free(bitmap_table); ++ ++ tb->offset = 0; ++ tb->size = 0; ++ ++ return 0; ++} ++ ++/* load_bitmap_data ++ * @bitmap_table entries must satisfy specification constraints. ++ * @bitmap must be cleared */ ++static int coroutine_fn GRAPH_RDLOCK ++load_bitmap_data(BlockDriverState *bs, const uint64_t *bitmap_table, ++ uint32_t bitmap_table_size, BdrvDirtyBitmap *bitmap) ++{ ++ int ret = 0; ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t offset, limit; ++ uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); ++ uint8_t *buf = NULL; ++ uint64_t i, tab_size = ++ size_to_clusters(s, ++ bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size)); ++ ++ if (tab_size != bitmap_table_size || tab_size > BME_MAX_TABLE_SIZE) { ++ return -EINVAL; ++ } ++ ++ buf = g_malloc(s->cluster_size); ++ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); ++ for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { ++ uint64_t count = MIN(bm_size - offset, limit); ++ uint64_t entry = bitmap_table[i]; ++ uint64_t data_offset = entry & BME_TABLE_ENTRY_OFFSET_MASK; ++ ++ assert(check_table_entry(entry, s->cluster_size) == 0); ++ ++ if (data_offset == 0) { ++ if (entry & BME_TABLE_ENTRY_FLAG_ALL_ONES) { ++ bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, ++ false); ++ } else { ++ /* No need to deserialize zeros because the dirty bitmap is ++ * already cleared */ ++ } ++ } else { ++ ret = bdrv_co_pread(bs->file, data_offset, s->cluster_size, buf, 0); ++ if (ret < 0) { ++ goto finish; ++ } ++ bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count, ++ false); ++ } ++ } ++ ret = 0; ++ ++ bdrv_dirty_bitmap_deserialize_finish(bitmap); ++ ++finish: ++ g_free(buf); ++ ++ return ret; ++} ++ ++static coroutine_fn GRAPH_RDLOCK ++BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, ++ Qcow2Bitmap *bm, Error **errp) ++{ ++ int ret; ++ uint64_t *bitmap_table = NULL; ++ uint32_t granularity; ++ BdrvDirtyBitmap *bitmap = NULL; ++ ++ granularity = 1U << bm->granularity_bits; ++ bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp); ++ if (bitmap == NULL) { ++ goto fail; ++ } ++ ++ if (bm->flags & BME_FLAG_IN_USE) { ++ /* Data is unusable, skip loading it */ ++ return bitmap; ++ } ++ ++ ret = bitmap_table_load(bs, &bm->table, &bitmap_table); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Could not read bitmap_table table from image for " ++ "bitmap '%s'", bm->name); ++ goto fail; ++ } ++ ++ ret = load_bitmap_data(bs, bitmap_table, bm->table.size, bitmap); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read bitmap '%s' from image", ++ bm->name); ++ goto fail; ++ } ++ ++ g_free(bitmap_table); ++ return bitmap; ++ ++fail: ++ g_free(bitmap_table); ++ if (bitmap != NULL) { ++ bdrv_release_dirty_bitmap(bitmap); ++ } ++ ++ return NULL; ++} ++ ++/* ++ * Bitmap List ++ */ ++ ++/* ++ * Bitmap List private functions ++ * Only Bitmap List knows about bitmap directory structure in Qcow2. ++ */ ++ ++static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry) ++{ ++ entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset); ++ entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size); ++ entry->flags = be32_to_cpu(entry->flags); ++ entry->name_size = be16_to_cpu(entry->name_size); ++ entry->extra_data_size = be32_to_cpu(entry->extra_data_size); ++} ++ ++static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry) ++{ ++ entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset); ++ entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size); ++ entry->flags = cpu_to_be32(entry->flags); ++ entry->name_size = cpu_to_be16(entry->name_size); ++ entry->extra_data_size = cpu_to_be32(entry->extra_data_size); ++} ++ ++static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size) ++{ ++ int size = sizeof(Qcow2BitmapDirEntry) + name_size + extra_data_size; ++ return ROUND_UP(size, 8); ++} ++ ++static inline int dir_entry_size(Qcow2BitmapDirEntry *entry) ++{ ++ return calc_dir_entry_size(entry->name_size, entry->extra_data_size); ++} ++ ++static inline const char *dir_entry_name_field(Qcow2BitmapDirEntry *entry) ++{ ++ return (const char *)(entry + 1) + entry->extra_data_size; ++} ++ ++static inline char *dir_entry_copy_name(Qcow2BitmapDirEntry *entry) ++{ ++ const char *name_field = dir_entry_name_field(entry); ++ return g_strndup(name_field, entry->name_size); ++} ++ ++static inline Qcow2BitmapDirEntry *next_dir_entry(Qcow2BitmapDirEntry *entry) ++{ ++ return (Qcow2BitmapDirEntry *)((uint8_t *)entry + dir_entry_size(entry)); ++} ++ ++static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t phys_bitmap_bytes; ++ int64_t len; ++ ++ bool fail = (entry->bitmap_table_size == 0) || ++ (entry->bitmap_table_offset == 0) || ++ (entry->bitmap_table_offset % s->cluster_size) || ++ (entry->bitmap_table_size > BME_MAX_TABLE_SIZE) || ++ (entry->granularity_bits > BME_MAX_GRANULARITY_BITS) || ++ (entry->granularity_bits < BME_MIN_GRANULARITY_BITS) || ++ (entry->flags & BME_RESERVED_FLAGS) || ++ (entry->name_size > BME_MAX_NAME_SIZE) || ++ (entry->type != BT_DIRTY_TRACKING_BITMAP); ++ ++ if (fail) { ++ return -EINVAL; ++ } ++ ++ phys_bitmap_bytes = (uint64_t)entry->bitmap_table_size * s->cluster_size; ++ len = bdrv_getlength(bs); ++ ++ if (len < 0) { ++ return len; ++ } ++ ++ if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) { ++ return -EINVAL; ++ } ++ ++ if (!(entry->flags & BME_FLAG_IN_USE) && ++ (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) ++ { ++ /* ++ * We've loaded a valid bitmap (IN_USE not set) or we are going to ++ * store a valid bitmap, but the allocated bitmap table size is not ++ * enough to store this bitmap. ++ * ++ * Note, that it's OK to have an invalid bitmap with invalid size due ++ * to a bitmap that was not correctly saved after image resize. ++ */ ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static inline void bitmap_directory_to_be(uint8_t *dir, size_t size) ++{ ++ uint8_t *end = dir + size; ++ while (dir < end) { ++ Qcow2BitmapDirEntry *e = (Qcow2BitmapDirEntry *)dir; ++ dir += dir_entry_size(e); ++ ++ bitmap_dir_entry_to_be(e); ++ } ++} ++ ++/* ++ * Bitmap List public functions ++ */ ++ ++static void bitmap_free(Qcow2Bitmap *bm) ++{ ++ if (bm == NULL) { ++ return; ++ } ++ ++ g_free(bm->name); ++ g_free(bm); ++} ++ ++static void bitmap_list_free(Qcow2BitmapList *bm_list) ++{ ++ Qcow2Bitmap *bm; ++ ++ if (bm_list == NULL) { ++ return; ++ } ++ ++ while ((bm = QSIMPLEQ_FIRST(bm_list)) != NULL) { ++ QSIMPLEQ_REMOVE_HEAD(bm_list, entry); ++ bitmap_free(bm); ++ } ++ ++ g_free(bm_list); ++} ++ ++static Qcow2BitmapList *bitmap_list_new(void) ++{ ++ Qcow2BitmapList *bm_list = g_new(Qcow2BitmapList, 1); ++ QSIMPLEQ_INIT(bm_list); ++ ++ return bm_list; ++} ++ ++static uint32_t bitmap_list_count(Qcow2BitmapList *bm_list) ++{ ++ Qcow2Bitmap *bm; ++ uint32_t nb_bitmaps = 0; ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ nb_bitmaps++; ++ } ++ ++ return nb_bitmaps; ++} ++ ++/* bitmap_list_load ++ * Get bitmap list from qcow2 image. Actually reads bitmap directory, ++ * checks it and convert to bitmap list. ++ */ ++static Qcow2BitmapList * GRAPH_RDLOCK ++bitmap_list_load(BlockDriverState *bs, uint64_t offset, uint64_t size, ++ Error **errp) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ uint8_t *dir, *dir_end; ++ Qcow2BitmapDirEntry *e; ++ uint32_t nb_dir_entries = 0; ++ Qcow2BitmapList *bm_list = NULL; ++ ++ if (size == 0) { ++ error_setg(errp, "Requested bitmap directory size is zero"); ++ return NULL; ++ } ++ ++ if (size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ error_setg(errp, "Requested bitmap directory size is too big"); ++ return NULL; ++ } ++ ++ dir = g_try_malloc(size); ++ if (dir == NULL) { ++ error_setg(errp, "Failed to allocate space for bitmap directory"); ++ return NULL; ++ } ++ dir_end = dir + size; ++ ++ ret = bdrv_pread(bs->file, offset, size, dir, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read bitmap directory"); ++ goto fail; ++ } ++ ++ bm_list = bitmap_list_new(); ++ for (e = (Qcow2BitmapDirEntry *)dir; ++ e < (Qcow2BitmapDirEntry *)dir_end; ++ e = next_dir_entry(e)) ++ { ++ Qcow2Bitmap *bm; ++ ++ if ((uint8_t *)(e + 1) > dir_end) { ++ goto broken_dir; ++ } ++ ++ if (++nb_dir_entries > s->nb_bitmaps) { ++ error_setg(errp, "More bitmaps found than specified in header" ++ " extension"); ++ goto fail; ++ } ++ bitmap_dir_entry_to_cpu(e); ++ ++ if ((uint8_t *)next_dir_entry(e) > dir_end) { ++ goto broken_dir; ++ } ++ ++ if (e->extra_data_size != 0) { ++ error_setg(errp, "Bitmap extra data is not supported"); ++ goto fail; ++ } ++ ++ ret = check_dir_entry(bs, e); ++ if (ret < 0) { ++ error_setg(errp, "Bitmap '%.*s' doesn't satisfy the constraints", ++ e->name_size, dir_entry_name_field(e)); ++ goto fail; ++ } ++ ++ bm = g_new0(Qcow2Bitmap, 1); ++ bm->table.offset = e->bitmap_table_offset; ++ bm->table.size = e->bitmap_table_size; ++ bm->flags = e->flags; ++ bm->granularity_bits = e->granularity_bits; ++ bm->name = dir_entry_copy_name(e); ++ QSIMPLEQ_INSERT_TAIL(bm_list, bm, entry); ++ } ++ ++ if (nb_dir_entries != s->nb_bitmaps) { ++ error_setg(errp, "Less bitmaps found than specified in header" ++ " extension"); ++ goto fail; ++ } ++ ++ if ((uint8_t *)e != dir_end) { ++ goto broken_dir; ++ } ++ ++ g_free(dir); ++ return bm_list; ++ ++broken_dir: ++ error_setg(errp, "Broken bitmap directory"); ++ ++fail: ++ g_free(dir); ++ bitmap_list_free(bm_list); ++ ++ return NULL; ++} ++ ++int coroutine_fn ++qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ ++ if (s->nb_bitmaps == 0) { ++ return 0; ++ } ++ ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, ++ s->bitmap_directory_offset, ++ s->bitmap_directory_size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, NULL); ++ if (bm_list == NULL) { ++ res->corruptions++; ++ return -EINVAL; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ uint64_t *bitmap_table = NULL; ++ int i; ++ ++ ret = qcow2_inc_refcounts_imrt(bs, res, ++ refcount_table, refcount_table_size, ++ bm->table.offset, ++ bm->table.size * BME_TABLE_ENTRY_SIZE); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = bitmap_table_load(bs, &bm->table, &bitmap_table); ++ if (ret < 0) { ++ res->corruptions++; ++ goto out; ++ } ++ ++ for (i = 0; i < bm->table.size; ++i) { ++ uint64_t entry = bitmap_table[i]; ++ uint64_t offset = entry & BME_TABLE_ENTRY_OFFSET_MASK; ++ ++ if (check_table_entry(entry, s->cluster_size) < 0) { ++ res->corruptions++; ++ continue; ++ } ++ ++ if (offset == 0) { ++ continue; ++ } ++ ++ ret = qcow2_inc_refcounts_imrt(bs, res, ++ refcount_table, refcount_table_size, ++ offset, s->cluster_size); ++ if (ret < 0) { ++ g_free(bitmap_table); ++ goto out; ++ } ++ } ++ ++ g_free(bitmap_table); ++ } ++ ++out: ++ bitmap_list_free(bm_list); ++ ++ return ret; ++} ++ ++/* bitmap_list_store ++ * Store bitmap list to qcow2 image as a bitmap directory. ++ * Everything is checked. ++ */ ++static int GRAPH_RDLOCK ++bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, ++ uint64_t *offset, uint64_t *size, bool in_place) ++{ ++ int ret; ++ uint8_t *dir; ++ int64_t dir_offset = 0; ++ uint64_t dir_size = 0; ++ Qcow2Bitmap *bm; ++ Qcow2BitmapDirEntry *e; ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ dir_size += calc_dir_entry_size(strlen(bm->name), 0); ++ } ++ ++ if (dir_size == 0 || dir_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ return -EINVAL; ++ } ++ ++ if (in_place) { ++ if (*size != dir_size || *offset == 0) { ++ return -EINVAL; ++ } ++ ++ dir_offset = *offset; ++ } ++ ++ dir = g_try_malloc0(dir_size); ++ if (dir == NULL) { ++ return -ENOMEM; ++ } ++ ++ e = (Qcow2BitmapDirEntry *)dir; ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ e->bitmap_table_offset = bm->table.offset; ++ e->bitmap_table_size = bm->table.size; ++ e->flags = bm->flags; ++ e->type = BT_DIRTY_TRACKING_BITMAP; ++ e->granularity_bits = bm->granularity_bits; ++ e->name_size = strlen(bm->name); ++ e->extra_data_size = 0; ++ memcpy(e + 1, bm->name, e->name_size); ++ ++ if (check_dir_entry(bs, e) < 0) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ e = next_dir_entry(e); ++ } ++ ++ bitmap_directory_to_be(dir, dir_size); ++ ++ if (!in_place) { ++ dir_offset = qcow2_alloc_clusters(bs, dir_size); ++ if (dir_offset < 0) { ++ ret = dir_offset; ++ goto fail; ++ } ++ } ++ ++ /* Actually, even in the in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY ++ * is not necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating ++ * bitmap directory in-place (actually, turn-off the extension), which is ++ * checked in qcow2_check_metadata_overlap() */ ++ ret = qcow2_pre_write_overlap_check( ++ bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size, ++ false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite(bs->file, dir_offset, dir_size, dir, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ g_free(dir); ++ ++ if (!in_place) { ++ *size = dir_size; ++ *offset = dir_offset; ++ } ++ ++ return 0; ++ ++fail: ++ g_free(dir); ++ ++ if (!in_place && dir_offset > 0) { ++ qcow2_free_clusters(bs, dir_offset, dir_size, QCOW2_DISCARD_OTHER); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Bitmap List end ++ */ ++ ++static int GRAPH_RDLOCK ++update_ext_header_and_dir_in_place(BlockDriverState *bs, ++ Qcow2BitmapList *bm_list) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ if (!(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS) || ++ bm_list == NULL || QSIMPLEQ_EMPTY(bm_list) || ++ bitmap_list_count(bm_list) != s->nb_bitmaps) ++ { ++ return -EINVAL; ++ } ++ ++ s->autoclear_features &= ~(uint64_t)QCOW2_AUTOCLEAR_BITMAPS; ++ ret = update_header_sync(bs); ++ if (ret < 0) { ++ /* Two variants are possible here: ++ * 1. Autoclear flag is dropped, all bitmaps will be lost. ++ * 2. Autoclear flag is not dropped, old state is left. ++ */ ++ return ret; ++ } ++ ++ /* autoclear bit is not set, so we can safely update bitmap directory */ ++ ++ ret = bitmap_list_store(bs, bm_list, &s->bitmap_directory_offset, ++ &s->bitmap_directory_size, true); ++ if (ret < 0) { ++ /* autoclear bit is cleared, so all leaked clusters would be removed on ++ * qemu-img check */ ++ return ret; ++ } ++ ++ ret = update_header_sync(bs); ++ if (ret < 0) { ++ /* autoclear bit is cleared, so all leaked clusters would be removed on ++ * qemu-img check */ ++ return ret; ++ } ++ ++ s->autoclear_features |= QCOW2_AUTOCLEAR_BITMAPS; ++ return update_header_sync(bs); ++ /* If final update_header_sync() fails, two variants are possible: ++ * 1. Autoclear flag is not set, all bitmaps will be lost. ++ * 2. Autoclear flag is set, header and directory are successfully updated. ++ */ ++} ++ ++static int GRAPH_RDLOCK ++update_ext_header_and_dir(BlockDriverState *bs, Qcow2BitmapList *bm_list) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ uint64_t new_offset = 0; ++ uint64_t new_size = 0; ++ uint32_t new_nb_bitmaps = 0; ++ uint64_t old_offset = s->bitmap_directory_offset; ++ uint64_t old_size = s->bitmap_directory_size; ++ uint32_t old_nb_bitmaps = s->nb_bitmaps; ++ uint64_t old_autocl = s->autoclear_features; ++ ++ if (bm_list != NULL && !QSIMPLEQ_EMPTY(bm_list)) { ++ new_nb_bitmaps = bitmap_list_count(bm_list); ++ ++ if (new_nb_bitmaps > QCOW2_MAX_BITMAPS) { ++ return -EINVAL; ++ } ++ ++ ret = bitmap_list_store(bs, bm_list, &new_offset, &new_size, false); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = qcow2_flush_caches(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->autoclear_features |= QCOW2_AUTOCLEAR_BITMAPS; ++ } else { ++ s->autoclear_features &= ~(uint64_t)QCOW2_AUTOCLEAR_BITMAPS; ++ } ++ ++ s->bitmap_directory_offset = new_offset; ++ s->bitmap_directory_size = new_size; ++ s->nb_bitmaps = new_nb_bitmaps; ++ ++ ret = update_header_sync(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (old_size > 0) { ++ qcow2_free_clusters(bs, old_offset, old_size, QCOW2_DISCARD_OTHER); ++ } ++ ++ return 0; ++ ++fail: ++ if (new_offset > 0) { ++ qcow2_free_clusters(bs, new_offset, new_size, QCOW2_DISCARD_OTHER); ++ } ++ ++ s->bitmap_directory_offset = old_offset; ++ s->bitmap_directory_size = old_size; ++ s->nb_bitmaps = old_nb_bitmaps; ++ s->autoclear_features = old_autocl; ++ ++ return ret; ++} ++ ++/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */ ++static void release_dirty_bitmap_helper(gpointer bitmap, ++ gpointer bs) ++{ ++ bdrv_release_dirty_bitmap(bitmap); ++} ++ ++/* for g_slist_foreach for GSList of BdrvDirtyBitmap* elements */ ++static void set_readonly_helper(gpointer bitmap, gpointer value) ++{ ++ bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value); ++} ++ ++/* ++ * Return true on success, false on failure. ++ * If header_updated is not NULL then it is set appropriately regardless of ++ * the return value. ++ */ ++bool coroutine_fn ++qcow2_load_dirty_bitmaps(BlockDriverState *bs, ++ bool *header_updated, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ GSList *created_dirty_bitmaps = NULL; ++ bool needs_update = false; ++ ++ if (header_updated) { ++ *header_updated = false; ++ } ++ ++ if (s->nb_bitmaps == 0) { ++ /* No bitmaps - nothing to do */ ++ return true; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return false; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ BdrvDirtyBitmap *bitmap; ++ ++ if ((bm->flags & BME_FLAG_IN_USE) && ++ bdrv_find_dirty_bitmap(bs, bm->name)) ++ { ++ /* ++ * We already have corresponding BdrvDirtyBitmap, and bitmap in the ++ * image is marked IN_USE. Firstly, this state is valid, no reason ++ * to consider existing BdrvDirtyBitmap to be bad. Secondly it's ++ * absolutely possible, when we do migration with shared storage ++ * with dirty-bitmaps capability enabled: if the bitmap was loaded ++ * from this storage before migration start, the storage will ++ * of-course contain IN_USE outdated version of the bitmap, and we ++ * should not load it on migration target, as we already have this ++ * bitmap, being migrated. ++ */ ++ continue; ++ } ++ ++ bitmap = load_bitmap(bs, bm, errp); ++ if (bitmap == NULL) { ++ goto fail; ++ } ++ ++ bdrv_dirty_bitmap_set_persistence(bitmap, true); ++ if (bm->flags & BME_FLAG_IN_USE) { ++ bdrv_dirty_bitmap_set_inconsistent(bitmap); ++ } else { ++ /* NB: updated flags only get written if can_write(bs) is true. */ ++ bm->flags |= BME_FLAG_IN_USE; ++ needs_update = true; ++ } ++ if (!(bm->flags & BME_FLAG_AUTO)) { ++ bdrv_disable_dirty_bitmap(bitmap); ++ } ++ created_dirty_bitmaps = ++ g_slist_append(created_dirty_bitmaps, bitmap); ++ } ++ ++ if (needs_update && can_write(bs)) { ++ /* in_use flags must be updated */ ++ int ret = update_ext_header_and_dir_in_place(bs, bm_list); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Can't update bitmap directory"); ++ goto fail; ++ } ++ if (header_updated) { ++ *header_updated = true; ++ } ++ } ++ ++ if (!can_write(bs)) { ++ g_slist_foreach(created_dirty_bitmaps, set_readonly_helper, ++ (gpointer)true); ++ } ++ ++ g_slist_free(created_dirty_bitmaps); ++ bitmap_list_free(bm_list); ++ ++ return true; ++ ++fail: ++ g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs); ++ g_slist_free(created_dirty_bitmaps); ++ bitmap_list_free(bm_list); ++ ++ return false; ++} ++ ++ ++static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) ++{ ++ Qcow2BitmapInfoFlagsList *list = NULL; ++ Qcow2BitmapInfoFlagsList **tail = &list; ++ int i; ++ ++ static const struct { ++ int bme; /* Bitmap directory entry flags */ ++ int info; /* The flags to report to the user */ ++ } map[] = { ++ { BME_FLAG_IN_USE, QCOW2_BITMAP_INFO_FLAGS_IN_USE }, ++ { BME_FLAG_AUTO, QCOW2_BITMAP_INFO_FLAGS_AUTO }, ++ }; ++ ++ int map_size = ARRAY_SIZE(map); ++ ++ for (i = 0; i < map_size; ++i) { ++ if (flags & map[i].bme) { ++ QAPI_LIST_APPEND(tail, map[i].info); ++ flags &= ~map[i].bme; ++ } ++ } ++ /* Check if the BME_* mapping above is complete */ ++ assert(!flags); ++ ++ return list; ++} ++ ++/* ++ * qcow2_get_bitmap_info_list() ++ * Returns a list of QCOW2 bitmap details. ++ * On success return true with info_list set (note, that if there are no ++ * bitmaps, info_list is set to NULL). ++ * On failure return false with errp set. ++ */ ++bool qcow2_get_bitmap_info_list(BlockDriverState *bs, ++ Qcow2BitmapInfoList **info_list, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ Qcow2BitmapInfoList **tail; ++ ++ if (s->nb_bitmaps == 0) { ++ *info_list = NULL; ++ return true; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (!bm_list) { ++ return false; ++ } ++ ++ *info_list = NULL; ++ tail = info_list; ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1); ++ info->granularity = 1U << bm->granularity_bits; ++ info->name = g_strdup(bm->name); ++ info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS); ++ QAPI_LIST_APPEND(tail, info); ++ } ++ ++ bitmap_list_free(bm_list); ++ ++ return true; ++} ++ ++int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ GSList *ro_dirty_bitmaps = NULL; ++ int ret = -EINVAL; ++ bool need_header_update = false; ++ ++ if (s->nb_bitmaps == 0) { ++ /* No bitmaps - nothing to do */ ++ return 0; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return -EINVAL; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); ++ ++ if (!bitmap) { ++ error_setg(errp, "Unexpected bitmap '%s' in image '%s'", ++ bm->name, bs->filename); ++ goto out; ++ } ++ ++ if (!(bm->flags & BME_FLAG_IN_USE)) { ++ if (!bdrv_dirty_bitmap_readonly(bitmap)) { ++ error_setg(errp, "Corruption: bitmap '%s' is not marked IN_USE " ++ "in the image '%s' and not marked readonly in RAM", ++ bm->name, bs->filename); ++ goto out; ++ } ++ if (bdrv_dirty_bitmap_inconsistent(bitmap)) { ++ error_setg(errp, "Corruption: bitmap '%s' is inconsistent but " ++ "is not marked IN_USE in the image '%s'", bm->name, ++ bs->filename); ++ goto out; ++ } ++ ++ bm->flags |= BME_FLAG_IN_USE; ++ need_header_update = true; ++ } else { ++ /* ++ * What if flags already has BME_FLAG_IN_USE ? ++ * ++ * 1. if we are reopening RW -> RW it's OK, of course. ++ * 2. if we are reopening RO -> RW: ++ * 2.1 if @bitmap is inconsistent, it's OK. It means that it was ++ * inconsistent (IN_USE) when we loaded it ++ * 2.2 if @bitmap is not inconsistent. This seems to be impossible ++ * and implies third party interaction. Let's error-out for ++ * safety. ++ */ ++ if (bdrv_dirty_bitmap_readonly(bitmap) && ++ !bdrv_dirty_bitmap_inconsistent(bitmap)) ++ { ++ error_setg(errp, "Corruption: bitmap '%s' is marked IN_USE " ++ "in the image '%s' but it is readonly and " ++ "consistent in RAM", ++ bm->name, bs->filename); ++ goto out; ++ } ++ } ++ ++ if (bdrv_dirty_bitmap_readonly(bitmap)) { ++ ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap); ++ } ++ } ++ ++ if (need_header_update) { ++ if (!can_write(bs->file->bs) || !(bs->file->perm & BLK_PERM_WRITE)) { ++ error_setg(errp, "Failed to reopen bitmaps rw: no write access " ++ "the protocol file"); ++ goto out; ++ } ++ ++ /* in_use flags must be updated */ ++ ret = update_ext_header_and_dir_in_place(bs, bm_list); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Cannot update bitmap directory"); ++ goto out; ++ } ++ } ++ ++ g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, (gpointer)false); ++ ret = 0; ++ ++out: ++ g_slist_free(ro_dirty_bitmaps); ++ bitmap_list_free(bm_list); ++ ++ return ret; ++} ++ ++/* Checks to see if it's safe to resize bitmaps */ ++int coroutine_fn qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ int ret = 0; ++ ++ if (s->nb_bitmaps == 0) { ++ return 0; ++ } ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return -EINVAL; ++ } ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); ++ if (bitmap == NULL) { ++ /* ++ * We rely on all bitmaps being in-memory to be able to resize them, ++ * Otherwise, we'd need to resize them on disk explicitly ++ */ ++ error_setg(errp, "Cannot resize qcow2 with persistent bitmaps that " ++ "were not loaded into memory"); ++ ret = -ENOTSUP; ++ goto out; ++ } ++ ++ /* ++ * The checks against readonly and busy are redundant, but certainly ++ * do no harm. checks against inconsistent are crucial: ++ */ ++ if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { ++ ret = -ENOTSUP; ++ goto out; ++ } ++ } ++ ++out: ++ bitmap_list_free(bm_list); ++ return ret; ++} ++ ++/* store_bitmap_data() ++ * Store bitmap to image, filling bitmap table accordingly. ++ */ ++static uint64_t * GRAPH_RDLOCK ++store_bitmap_data(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, ++ uint32_t *bitmap_table_size, Error **errp) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ int64_t offset; ++ uint64_t limit; ++ uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); ++ const char *bm_name = bdrv_dirty_bitmap_name(bitmap); ++ uint8_t *buf = NULL; ++ uint64_t *tb; ++ uint64_t tb_size = ++ size_to_clusters(s, ++ bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size)); ++ ++ if (tb_size > BME_MAX_TABLE_SIZE || ++ tb_size * s->cluster_size > BME_MAX_PHYS_SIZE) ++ { ++ error_setg(errp, "Bitmap '%s' is too big", bm_name); ++ return NULL; ++ } ++ ++ tb = g_try_new0(uint64_t, tb_size); ++ if (tb == NULL) { ++ error_setg(errp, "No memory"); ++ return NULL; ++ } ++ ++ buf = g_malloc(s->cluster_size); ++ limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); ++ assert(DIV_ROUND_UP(bm_size, limit) == tb_size); ++ ++ offset = 0; ++ while ((offset = bdrv_dirty_bitmap_next_dirty(bitmap, offset, INT64_MAX)) ++ >= 0) ++ { ++ uint64_t cluster = offset / limit; ++ uint64_t end, write_size; ++ int64_t off; ++ ++ /* ++ * We found the first dirty offset, but want to write out the ++ * entire cluster of the bitmap that includes that offset, ++ * including any leading zero bits. ++ */ ++ offset = QEMU_ALIGN_DOWN(offset, limit); ++ end = MIN(bm_size, offset + limit); ++ write_size = bdrv_dirty_bitmap_serialization_size(bitmap, offset, ++ end - offset); ++ assert(write_size <= s->cluster_size); ++ ++ off = qcow2_alloc_clusters(bs, s->cluster_size); ++ if (off < 0) { ++ error_setg_errno(errp, -off, ++ "Failed to allocate clusters for bitmap '%s'", ++ bm_name); ++ goto fail; ++ } ++ tb[cluster] = off; ++ ++ bdrv_dirty_bitmap_serialize_part(bitmap, buf, offset, end - offset); ++ if (write_size < s->cluster_size) { ++ memset(buf + write_size, 0, s->cluster_size - write_size); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size, false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Qcow2 overlap check failed"); ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite(bs->file, off, s->cluster_size, buf, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", ++ bm_name); ++ goto fail; ++ } ++ ++ offset = end; ++ } ++ ++ *bitmap_table_size = tb_size; ++ g_free(buf); ++ ++ return tb; ++ ++fail: ++ clear_bitmap_table(bs, tb, tb_size); ++ g_free(buf); ++ g_free(tb); ++ ++ return NULL; ++} ++ ++/* store_bitmap() ++ * Store bm->dirty_bitmap to qcow2. ++ * Set bm->table_offset and bm->table_size accordingly. ++ */ ++static int GRAPH_RDLOCK ++store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp) ++{ ++ int ret; ++ uint64_t *tb; ++ int64_t tb_offset; ++ uint32_t tb_size; ++ BdrvDirtyBitmap *bitmap = bm->dirty_bitmap; ++ const char *bm_name; ++ ++ assert(bitmap != NULL); ++ ++ bm_name = bdrv_dirty_bitmap_name(bitmap); ++ ++ tb = store_bitmap_data(bs, bitmap, &tb_size, errp); ++ if (tb == NULL) { ++ return -EINVAL; ++ } ++ ++ assert(tb_size <= BME_MAX_TABLE_SIZE); ++ tb_offset = qcow2_alloc_clusters(bs, tb_size * sizeof(tb[0])); ++ if (tb_offset < 0) { ++ error_setg_errno(errp, -tb_offset, ++ "Failed to allocate clusters for bitmap '%s'", ++ bm_name); ++ ret = tb_offset; ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset, ++ tb_size * sizeof(tb[0]), false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Qcow2 overlap check failed"); ++ goto fail; ++ } ++ ++ bitmap_table_bswap_be(tb, tb_size); ++ ret = bdrv_pwrite(bs->file, tb_offset, tb_size * sizeof(tb[0]), tb, 0); ++ if (ret < 0) { ++ bitmap_table_bswap_be(tb, tb_size); ++ error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file", ++ bm_name); ++ goto fail; ++ } ++ ++ g_free(tb); ++ ++ bm->table.offset = tb_offset; ++ bm->table.size = tb_size; ++ ++ return 0; ++ ++fail: ++ clear_bitmap_table(bs, tb, tb_size); ++ ++ if (tb_offset > 0) { ++ qcow2_free_clusters(bs, tb_offset, tb_size * sizeof(tb[0]), ++ QCOW2_DISCARD_OTHER); ++ } ++ ++ g_free(tb); ++ ++ return ret; ++} ++ ++static Qcow2Bitmap *find_bitmap_by_name(Qcow2BitmapList *bm_list, ++ const char *name) ++{ ++ Qcow2Bitmap *bm; ++ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ if (strcmp(name, bm->name) == 0) { ++ return bm; ++ } ++ } ++ ++ return NULL; ++} ++ ++int coroutine_fn qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, ++ const char *name, ++ Error **errp) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2Bitmap *bm = NULL; ++ Qcow2BitmapList *bm_list; ++ ++ if (s->nb_bitmaps == 0) { ++ /* ++ * Absence of the bitmap is not an error: see explanation above ++ * bdrv_co_remove_persistent_dirty_bitmap() definition. ++ */ ++ return 0; ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ bm = find_bitmap_by_name(bm_list, name); ++ if (bm == NULL) { ++ /* Absence of the bitmap is not an error, see above. */ ++ ret = 0; ++ goto out; ++ } ++ ++ QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry); ++ ++ ret = update_ext_header_and_dir(bs, bm_list); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update bitmap extension"); ++ goto out; ++ } ++ ++ free_bitmap_clusters(bs, &bm->table); ++ ++out: ++ qemu_co_mutex_unlock(&s->lock); ++ ++ bitmap_free(bm); ++ bitmap_list_free(bm_list); ++ ++ return ret; ++} ++ ++/* ++ * qcow2_store_persistent_dirty_bitmaps ++ * ++ * Stores persistent BdrvDirtyBitmap objects. ++ * ++ * @release_stored: if true, release BdrvDirtyBitmap's after storing to the ++ * image. This is used in two cases, both via qcow2_inactivate: ++ * 1. bdrv_close: It's correct to remove bitmaps on close. ++ * 2. migration: If bitmaps are migrated through migration channel via ++ * 'dirty-bitmaps' migration capability they are not handled by this code. ++ * Otherwise, it's OK to drop BdrvDirtyBitmap's and reload them on ++ * invalidation. ++ * ++ * Anyway, it's correct to remove BdrvDirtyBitmap's on inactivation, as ++ * inactivation means that we lose control on disk, and therefore on bitmaps, ++ * we should sync them and do not touch more. ++ * ++ * Contrariwise, we don't want to release any bitmaps on just reopen-to-ro, ++ * when we need to store them, as image is still under our control, and it's ++ * good to keep all the bitmaps in read-only mode. Moreover, keeping them ++ * read-only is correct because this is what would happen if we opened the node ++ * readonly to begin with, and whether we opened directly or reopened to that ++ * state shouldn't matter for the state we get afterward. ++ */ ++bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, ++ bool release_stored, Error **errp) ++{ ++ ERRP_GUARD(); ++ BdrvDirtyBitmap *bitmap; ++ BDRVQcow2State *s = bs->opaque; ++ uint32_t new_nb_bitmaps = s->nb_bitmaps; ++ uint64_t new_dir_size = s->bitmap_directory_size; ++ int ret; ++ Qcow2BitmapList *bm_list; ++ Qcow2Bitmap *bm; ++ QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables; ++ Qcow2BitmapTable *tb, *tb_next; ++ bool need_write = false; ++ ++ QSIMPLEQ_INIT(&drop_tables); ++ ++ if (s->nb_bitmaps == 0) { ++ bm_list = bitmap_list_new(); ++ } else { ++ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, ++ s->bitmap_directory_size, errp); ++ if (bm_list == NULL) { ++ return false; ++ } ++ } ++ ++ /* check constraints and names */ ++ FOR_EACH_DIRTY_BITMAP(bs, bitmap) { ++ const char *name = bdrv_dirty_bitmap_name(bitmap); ++ uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap); ++ ++ if (!bdrv_dirty_bitmap_get_persistence(bitmap) || ++ bdrv_dirty_bitmap_inconsistent(bitmap)) { ++ continue; ++ } ++ ++ if (bdrv_dirty_bitmap_readonly(bitmap)) { ++ /* ++ * Store the bitmap in the associated Qcow2Bitmap so it ++ * can be released later ++ */ ++ bm = find_bitmap_by_name(bm_list, name); ++ if (bm) { ++ bm->dirty_bitmap = bitmap; ++ } ++ continue; ++ } ++ ++ need_write = true; ++ ++ if (check_constraints_on_bitmap(bs, name, granularity, errp) < 0) { ++ error_prepend(errp, "Bitmap '%s' doesn't satisfy the constraints: ", ++ name); ++ goto fail; ++ } ++ ++ bm = find_bitmap_by_name(bm_list, name); ++ if (bm == NULL) { ++ if (++new_nb_bitmaps > QCOW2_MAX_BITMAPS) { ++ error_setg(errp, "Too many persistent bitmaps"); ++ goto fail; ++ } ++ ++ new_dir_size += calc_dir_entry_size(strlen(name), 0); ++ if (new_dir_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ error_setg(errp, "Bitmap directory is too large"); ++ goto fail; ++ } ++ ++ bm = g_new0(Qcow2Bitmap, 1); ++ bm->name = g_strdup(name); ++ QSIMPLEQ_INSERT_TAIL(bm_list, bm, entry); ++ } else { ++ if (!(bm->flags & BME_FLAG_IN_USE)) { ++ error_setg(errp, "Bitmap '%s' already exists in the image", ++ name); ++ goto fail; ++ } ++ tb = g_memdup2(&bm->table, sizeof(bm->table)); ++ bm->table.offset = 0; ++ bm->table.size = 0; ++ QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry); ++ } ++ bm->flags = bdrv_dirty_bitmap_enabled(bitmap) ? BME_FLAG_AUTO : 0; ++ bm->granularity_bits = ctz32(bdrv_dirty_bitmap_granularity(bitmap)); ++ bm->dirty_bitmap = bitmap; ++ } ++ ++ if (!need_write) { ++ goto success; ++ } ++ ++ if (!can_write(bs)) { ++ error_setg(errp, "No write access"); ++ goto fail; ++ } ++ ++ /* allocate clusters and store bitmaps */ ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ bitmap = bm->dirty_bitmap; ++ ++ if (bitmap == NULL || bdrv_dirty_bitmap_readonly(bitmap)) { ++ continue; ++ } ++ ++ ret = store_bitmap(bs, bm, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ ret = update_ext_header_and_dir(bs, bm_list); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update bitmap extension"); ++ goto fail; ++ } ++ ++ /* Bitmap directory was successfully updated, so, old data can be dropped. ++ * TODO it is better to reuse these clusters */ ++ QSIMPLEQ_FOREACH_SAFE(tb, &drop_tables, entry, tb_next) { ++ free_bitmap_clusters(bs, tb); ++ g_free(tb); ++ } ++ ++success: ++ if (release_stored) { ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ if (bm->dirty_bitmap == NULL) { ++ continue; ++ } ++ ++ bdrv_release_dirty_bitmap(bm->dirty_bitmap); ++ } ++ } ++ ++ bitmap_list_free(bm_list); ++ return true; ++ ++fail: ++ QSIMPLEQ_FOREACH(bm, bm_list, entry) { ++ if (bm->dirty_bitmap == NULL || bm->table.offset == 0 || ++ bdrv_dirty_bitmap_readonly(bm->dirty_bitmap)) ++ { ++ continue; ++ } ++ ++ free_bitmap_clusters(bs, &bm->table); ++ } ++ ++ QSIMPLEQ_FOREACH_SAFE(tb, &drop_tables, entry, tb_next) { ++ g_free(tb); ++ } ++ ++ bitmap_list_free(bm_list); ++ return false; ++} ++ ++int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp) ++{ ++ BdrvDirtyBitmap *bitmap; ++ ++ if (!qcow2_store_persistent_dirty_bitmaps(bs, false, errp)) { ++ return -EINVAL; ++ } ++ ++ FOR_EACH_DIRTY_BITMAP(bs, bitmap) { ++ if (bdrv_dirty_bitmap_get_persistence(bitmap)) { ++ bdrv_dirty_bitmap_set_readonly(bitmap, true); ++ } ++ } ++ ++ return 0; ++} ++ ++bool coroutine_fn qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs, ++ const char *name, ++ uint32_t granularity, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ BDRVQcow2State *s = bs->opaque; ++ BdrvDirtyBitmap *bitmap; ++ uint64_t bitmap_directory_size = 0; ++ uint32_t nb_bitmaps = 0; ++ ++ if (bdrv_find_dirty_bitmap(bs, name)) { ++ error_setg(errp, "Bitmap already exists: %s", name); ++ return false; ++ } ++ ++ if (s->qcow_version < 3) { ++ /* Without autoclear_features, we would always have to assume ++ * that a program without persistent dirty bitmap support has ++ * accessed this qcow2 file when opening it, and would thus ++ * have to drop all dirty bitmaps (defeating their purpose). ++ */ ++ error_setg(errp, "Cannot store dirty bitmaps in qcow2 v2 files"); ++ goto fail; ++ } ++ ++ if (check_constraints_on_bitmap(bs, name, granularity, errp) != 0) { ++ goto fail; ++ } ++ ++ FOR_EACH_DIRTY_BITMAP(bs, bitmap) { ++ if (bdrv_dirty_bitmap_get_persistence(bitmap)) { ++ nb_bitmaps++; ++ bitmap_directory_size += ++ calc_dir_entry_size(strlen(bdrv_dirty_bitmap_name(bitmap)), 0); ++ } ++ } ++ nb_bitmaps++; ++ bitmap_directory_size += calc_dir_entry_size(strlen(name), 0); ++ ++ if (nb_bitmaps > QCOW2_MAX_BITMAPS) { ++ error_setg(errp, ++ "Maximum number of persistent bitmaps is already reached"); ++ goto fail; ++ } ++ ++ if (bitmap_directory_size > QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ error_setg(errp, "Not enough space in the bitmap directory"); ++ goto fail; ++ } ++ ++ return true; ++ ++fail: ++ error_prepend(errp, "Can't make bitmap '%s' persistent in '%s': ", ++ name, bdrv_get_device_or_node_name(bs)); ++ return false; ++} ++ ++bool qcow2_supports_persistent_dirty_bitmap(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ return s->qcow_version >= 3; ++} ++ ++/* ++ * Compute the space required to copy bitmaps from @in_bs. ++ * ++ * The computation is based as if copying to a new image with the ++ * given @cluster_size, which may differ from the cluster size in ++ * @in_bs; in fact, @in_bs might be something other than qcow2. ++ */ ++uint64_t qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *in_bs, ++ uint32_t cluster_size) ++{ ++ uint64_t bitmaps_size = 0; ++ BdrvDirtyBitmap *bm; ++ size_t bitmap_dir_size = 0; ++ ++ FOR_EACH_DIRTY_BITMAP(in_bs, bm) { ++ if (bdrv_dirty_bitmap_get_persistence(bm)) { ++ const char *name = bdrv_dirty_bitmap_name(bm); ++ uint32_t granularity = bdrv_dirty_bitmap_granularity(bm); ++ uint64_t bmbytes = ++ get_bitmap_bytes_needed(bdrv_dirty_bitmap_size(bm), ++ granularity); ++ uint64_t bmclusters = DIV_ROUND_UP(bmbytes, cluster_size); ++ ++ /* Assume the entire bitmap is allocated */ ++ bitmaps_size += bmclusters * cluster_size; ++ /* Also reserve space for the bitmap table entries */ ++ bitmaps_size += ROUND_UP(bmclusters * BME_TABLE_ENTRY_SIZE, ++ cluster_size); ++ /* And space for contribution to bitmap directory size */ ++ bitmap_dir_size += calc_dir_entry_size(strlen(name), 0); ++ } ++ } ++ bitmaps_size += ROUND_UP(bitmap_dir_size, cluster_size); ++ ++ return bitmaps_size; ++} +diff --git a/qcow2/lib/qcow2-cache.c b/qcow2/lib/qcow2-cache.c +new file mode 100644 +index 00000000..23d9588b +--- /dev/null ++++ b/qcow2/lib/qcow2-cache.c +@@ -0,0 +1,462 @@ ++/* ++ * L2/refcount table cache for the QCOW2 format ++ * ++ * Copyright (c) 2010 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block-io.h" ++#include "qemu/memalign.h" ++#include "qcow2.h" ++#include "trace.h" ++ ++typedef struct Qcow2CachedTable { ++ int64_t offset; ++ uint64_t lru_counter; ++ int ref; ++ bool dirty; ++} Qcow2CachedTable; ++ ++struct Qcow2Cache { ++ Qcow2CachedTable *entries; ++ struct Qcow2Cache *depends; ++ int size; ++ int table_size; ++ bool depends_on_flush; ++ void *table_array; ++ uint64_t lru_counter; ++ uint64_t cache_clean_lru_counter; ++}; ++ ++static inline void *qcow2_cache_get_table_addr(Qcow2Cache *c, int table) ++{ ++ return (uint8_t *) c->table_array + (size_t) table * c->table_size; ++} ++ ++static inline int qcow2_cache_get_table_idx(Qcow2Cache *c, void *table) ++{ ++ ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array; ++ int idx = table_offset / c->table_size; ++ assert(idx >= 0 && idx < c->size && table_offset % c->table_size == 0); ++ return idx; ++} ++ ++static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c) ++{ ++ if (c == s->refcount_block_cache) { ++ return "refcount block"; ++ } else if (c == s->l2_table_cache) { ++ return "L2 table"; ++ } else { ++ /* Do not abort, because this is not critical */ ++ return "unknown"; ++ } ++} ++ ++static void qcow2_cache_table_release(Qcow2Cache *c, int i, int num_tables) ++{ ++/* Using MADV_DONTNEED to discard memory is a Linux-specific feature */ ++#ifdef CONFIG_LINUX ++ void *t = qcow2_cache_get_table_addr(c, i); ++ int align = qemu_real_host_page_size(); ++ size_t mem_size = (size_t) c->table_size * num_tables; ++ size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; ++ size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); ++ if (mem_size > offset && length > 0) { ++ madvise((uint8_t *) t + offset, length, MADV_DONTNEED); ++ } ++#endif ++} ++ ++static inline bool can_clean_entry(Qcow2Cache *c, int i) ++{ ++ Qcow2CachedTable *t = &c->entries[i]; ++ return t->ref == 0 && !t->dirty && t->offset != 0 && ++ t->lru_counter <= c->cache_clean_lru_counter; ++} ++ ++void qcow2_cache_clean_unused(Qcow2Cache *c) ++{ ++ int i = 0; ++ while (i < c->size) { ++ int to_clean = 0; ++ ++ /* Skip the entries that we don't need to clean */ ++ while (i < c->size && !can_clean_entry(c, i)) { ++ i++; ++ } ++ ++ /* And count how many we can clean in a row */ ++ while (i < c->size && can_clean_entry(c, i)) { ++ c->entries[i].offset = 0; ++ c->entries[i].lru_counter = 0; ++ i++; ++ to_clean++; ++ } ++ ++ if (to_clean > 0) { ++ qcow2_cache_table_release(c, i - to_clean, to_clean); ++ } ++ } ++ ++ c->cache_clean_lru_counter = c->lru_counter; ++} ++ ++Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, ++ unsigned table_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2Cache *c; ++ ++ assert(num_tables > 0); ++ assert(is_power_of_2(table_size)); ++ assert(table_size >= (1 << MIN_CLUSTER_BITS)); ++ assert(table_size <= s->cluster_size); ++ ++ c = g_new0(Qcow2Cache, 1); ++ c->size = num_tables; ++ c->table_size = table_size; ++ c->entries = g_try_new0(Qcow2CachedTable, num_tables); ++ c->table_array = qemu_try_blockalign(bs->file->bs, ++ (size_t) num_tables * c->table_size); ++ ++ if (!c->entries || !c->table_array) { ++ qemu_vfree(c->table_array); ++ g_free(c->entries); ++ g_free(c); ++ c = NULL; ++ } ++ ++ return c; ++} ++ ++int qcow2_cache_destroy(Qcow2Cache *c) ++{ ++ int i; ++ ++ for (i = 0; i < c->size; i++) { ++ assert(c->entries[i].ref == 0); ++ } ++ ++ qemu_vfree(c->table_array); ++ g_free(c->entries); ++ g_free(c); ++ ++ return 0; ++} ++ ++static int GRAPH_RDLOCK ++qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) ++{ ++ int ret; ++ ++ ret = qcow2_cache_flush(bs, c->depends); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ c->depends = NULL; ++ c->depends_on_flush = false; ++ ++ return 0; ++} ++ ++static int GRAPH_RDLOCK ++qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret = 0; ++ ++ if (!c->entries[i].dirty || !c->entries[i].offset) { ++ return 0; ++ } ++ ++ trace_qcow2_cache_entry_flush(qemu_coroutine_self(), ++ c == s->l2_table_cache, i); ++ ++ if (c->depends) { ++ ret = qcow2_cache_flush_dependency(bs, c); ++ } else if (c->depends_on_flush) { ++ ret = bdrv_flush(bs->file->bs); ++ if (ret >= 0) { ++ c->depends_on_flush = false; ++ } ++ } ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (c == s->refcount_block_cache) { ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, ++ c->entries[i].offset, c->table_size, false); ++ } else if (c == s->l2_table_cache) { ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, ++ c->entries[i].offset, c->table_size, false); ++ } else { ++ ret = qcow2_pre_write_overlap_check(bs, 0, ++ c->entries[i].offset, c->table_size, false); ++ } ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (c == s->refcount_block_cache) { ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); ++ } else if (c == s->l2_table_cache) { ++ BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); ++ } ++ ++ ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->table_size, ++ qcow2_cache_get_table_addr(c, i), 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ c->entries[i].dirty = false; ++ ++ return 0; ++} ++ ++int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int result = 0; ++ int ret; ++ int i; ++ ++ trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); ++ ++ for (i = 0; i < c->size; i++) { ++ ret = qcow2_cache_entry_flush(bs, c, i); ++ if (ret < 0 && result != -ENOSPC) { ++ result = ret; ++ } ++ } ++ ++ return result; ++} ++ ++int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) ++{ ++ int result = qcow2_cache_write(bs, c); ++ ++ if (result == 0) { ++ int ret = bdrv_flush(bs->file->bs); ++ if (ret < 0) { ++ result = ret; ++ } ++ } ++ ++ return result; ++} ++ ++int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, ++ Qcow2Cache *dependency) ++{ ++ int ret; ++ ++ if (dependency->depends) { ++ ret = qcow2_cache_flush_dependency(bs, dependency); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (c->depends && (c->depends != dependency)) { ++ ret = qcow2_cache_flush_dependency(bs, c); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ c->depends = dependency; ++ return 0; ++} ++ ++void qcow2_cache_depends_on_flush(Qcow2Cache *c) ++{ ++ c->depends_on_flush = true; ++} ++ ++int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c) ++{ ++ int ret, i; ++ ++ ret = qcow2_cache_flush(bs, c); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ for (i = 0; i < c->size; i++) { ++ assert(c->entries[i].ref == 0); ++ c->entries[i].offset = 0; ++ c->entries[i].lru_counter = 0; ++ } ++ ++ qcow2_cache_table_release(c, 0, c->size); ++ ++ c->lru_counter = 0; ++ ++ return 0; ++} ++ ++static int GRAPH_RDLOCK ++qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table, bool read_from_disk) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ int ret; ++ int lookup_index; ++ uint64_t min_lru_counter = UINT64_MAX; ++ int min_lru_index = -1; ++ ++ assert(offset != 0); ++ ++ trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, ++ offset, read_from_disk); ++ ++ if (!QEMU_IS_ALIGNED(offset, c->table_size)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s " ++ "cache: Offset %#" PRIx64 " is unaligned", ++ qcow2_cache_get_name(s, c), offset); ++ return -EIO; ++ } ++ ++ /* Check if the table is already cached */ ++ i = lookup_index = (offset / c->table_size * 4) % c->size; ++ do { ++ const Qcow2CachedTable *t = &c->entries[i]; ++ if (t->offset == offset) { ++ goto found; ++ } ++ if (t->ref == 0 && t->lru_counter < min_lru_counter) { ++ min_lru_counter = t->lru_counter; ++ min_lru_index = i; ++ } ++ if (++i == c->size) { ++ i = 0; ++ } ++ } while (i != lookup_index); ++ ++ if (min_lru_index == -1) { ++ /* This can't happen in current synchronous code, but leave the check ++ * here as a reminder for whoever starts using AIO with the cache */ ++ abort(); ++ } ++ ++ /* Cache miss: write a table back and replace it */ ++ i = min_lru_index; ++ trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), ++ c == s->l2_table_cache, i); ++ ++ ret = qcow2_cache_entry_flush(bs, c, i); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ trace_qcow2_cache_get_read(qemu_coroutine_self(), ++ c == s->l2_table_cache, i); ++ c->entries[i].offset = 0; ++ if (read_from_disk) { ++ if (c == s->l2_table_cache) { ++ BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); ++ } ++ ++ ret = bdrv_pread(bs->file, offset, c->table_size, ++ qcow2_cache_get_table_addr(c, i), 0); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ c->entries[i].offset = offset; ++ ++ /* And return the right table */ ++found: ++ c->entries[i].ref++; ++ *table = qcow2_cache_get_table_addr(c, i); ++ ++ trace_qcow2_cache_get_done(qemu_coroutine_self(), ++ c == s->l2_table_cache, i); ++ ++ return 0; ++} ++ ++int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table) ++{ ++ return qcow2_cache_do_get(bs, c, offset, table, true); ++} ++ ++int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, ++ void **table) ++{ ++ return qcow2_cache_do_get(bs, c, offset, table, false); ++} ++ ++void qcow2_cache_put(Qcow2Cache *c, void **table) ++{ ++ int i = qcow2_cache_get_table_idx(c, *table); ++ ++ c->entries[i].ref--; ++ *table = NULL; ++ ++ if (c->entries[i].ref == 0) { ++ c->entries[i].lru_counter = ++c->lru_counter; ++ } ++ ++ assert(c->entries[i].ref >= 0); ++} ++ ++void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) ++{ ++ int i = qcow2_cache_get_table_idx(c, table); ++ assert(c->entries[i].offset != 0); ++ c->entries[i].dirty = true; ++} ++ ++void *qcow2_cache_is_table_offset(Qcow2Cache *c, uint64_t offset) ++{ ++ int i; ++ ++ for (i = 0; i < c->size; i++) { ++ if (c->entries[i].offset == offset) { ++ return qcow2_cache_get_table_addr(c, i); ++ } ++ } ++ return NULL; ++} ++ ++void qcow2_cache_discard(Qcow2Cache *c, void *table) ++{ ++ int i = qcow2_cache_get_table_idx(c, table); ++ ++ assert(c->entries[i].ref == 0); ++ ++ c->entries[i].offset = 0; ++ c->entries[i].lru_counter = 0; ++ c->entries[i].dirty = false; ++ ++ qcow2_cache_table_release(c, i, 1); ++} +diff --git a/qcow2/lib/qcow2-cluster.c b/qcow2/lib/qcow2-cluster.c +new file mode 100644 +index 00000000..ce8c0076 +--- /dev/null ++++ b/qcow2/lib/qcow2-cluster.c +@@ -0,0 +1,2562 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include ++ ++#include "block/block-io.h" ++#include "qapi/error.h" ++#include "qcow2.h" ++#include "qemu/bswap.h" ++#include "qemu/memalign.h" ++#include "trace.h" ++ ++int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, ++ uint64_t exact_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int new_l1_size, i, ret; ++ ++ if (exact_size >= s->l1_size) { ++ return 0; ++ } ++ ++ new_l1_size = exact_size; ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "shrink l1_table from %d to %d\n", s->l1_size, new_l1_size); ++#endif ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_L1_SHRINK_WRITE_TABLE); ++ 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_co_flush(bs->file->bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_L1_SHRINK_FREE_L2_CLUSTERS); ++ for (i = s->l1_size - 1; i > new_l1_size - 1; i--) { ++ if ((s->l1_table[i] & L1E_OFFSET_MASK) == 0) { ++ continue; ++ } ++ qcow2_free_clusters(bs, s->l1_table[i] & L1E_OFFSET_MASK, ++ s->cluster_size, QCOW2_DISCARD_ALWAYS); ++ s->l1_table[i] = 0; ++ } ++ return 0; ++ ++fail: ++ /* ++ * If the write in the l1_table failed the image may contain a partially ++ * overwritten l1_table. In this case it would be better to clear the ++ * l1_table in memory to avoid possible image corruption. ++ */ ++ memset(s->l1_table + new_l1_size, 0, ++ (s->l1_size - new_l1_size) * L1E_SIZE); ++ return ret; ++} ++ ++int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, ++ bool exact_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int new_l1_size2, ret, i; ++ uint64_t *new_l1_table; ++ int64_t old_l1_table_offset, old_l1_size; ++ int64_t new_l1_table_offset, new_l1_size; ++ uint8_t data[12]; ++ ++ if (min_size <= s->l1_size) ++ return 0; ++ ++ /* Do a sanity check on min_size before trying to calculate new_l1_size ++ * (this prevents overflows during the while loop for the calculation of ++ * new_l1_size) */ ++ if (min_size > INT_MAX / L1E_SIZE) { ++ return -EFBIG; ++ } ++ ++ if (exact_size) { ++ new_l1_size = min_size; ++ } else { ++ /* Bump size up to reduce the number of times we have to grow */ ++ new_l1_size = s->l1_size; ++ if (new_l1_size == 0) { ++ new_l1_size = 1; ++ } ++ while (min_size > new_l1_size) { ++ new_l1_size = DIV_ROUND_UP(new_l1_size * 3, 2); ++ } ++ } ++ ++ QEMU_BUILD_BUG_ON(QCOW_MAX_L1_SIZE > INT_MAX); ++ if (new_l1_size > QCOW_MAX_L1_SIZE / L1E_SIZE) { ++ return -EFBIG; ++ } ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "grow l1_table from %d to %" PRId64 "\n", ++ s->l1_size, new_l1_size); ++#endif ++ ++ new_l1_size2 = L1E_SIZE * new_l1_size; ++ new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_size2); ++ if (new_l1_table == NULL) { ++ return -ENOMEM; ++ } ++ memset(new_l1_table, 0, new_l1_size2); ++ ++ if (s->l1_size) { ++ memcpy(new_l1_table, s->l1_table, s->l1_size * L1E_SIZE); ++ } ++ ++ /* write new table (align to cluster) */ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE); ++ new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); ++ if (new_l1_table_offset < 0) { ++ qemu_vfree(new_l1_table); ++ return new_l1_table_offset; ++ } ++ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* the L1 position has not yet been updated, so these clusters must ++ * indeed be completely free */ ++ ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset, ++ new_l1_size2, false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); ++ for(i = 0; i < s->l1_size; i++) ++ new_l1_table[i] = cpu_to_be64(new_l1_table[i]); ++ ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_size2, ++ new_l1_table, 0); ++ if (ret < 0) ++ goto fail; ++ for(i = 0; i < s->l1_size; i++) ++ new_l1_table[i] = be64_to_cpu(new_l1_table[i]); ++ ++ /* set new table */ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); ++ stl_be_p(data, new_l1_size); ++ stq_be_p(data + 4, new_l1_table_offset); ++ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), ++ sizeof(data), data, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ qemu_vfree(s->l1_table); ++ old_l1_table_offset = s->l1_table_offset; ++ s->l1_table_offset = new_l1_table_offset; ++ s->l1_table = new_l1_table; ++ old_l1_size = s->l1_size; ++ s->l1_size = new_l1_size; ++ qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * L1E_SIZE, ++ QCOW2_DISCARD_OTHER); ++ return 0; ++ fail: ++ qemu_vfree(new_l1_table); ++ qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2, ++ QCOW2_DISCARD_OTHER); ++ return ret; ++} ++ ++/* ++ * l2_load ++ * ++ * @bs: The BlockDriverState ++ * @offset: A guest offset, used to calculate what slice of the L2 ++ * table to load. ++ * @l2_offset: Offset to the L2 table in the image file. ++ * @l2_slice: Location to store the pointer to the L2 slice. ++ * ++ * Loads a L2 slice into memory (L2 slices are the parts of L2 tables ++ * that are loaded by the qcow2 cache). If the slice is in the cache, ++ * the cache is used; otherwise the L2 slice is loaded from the image ++ * file. ++ */ ++static int GRAPH_RDLOCK ++l2_load(BlockDriverState *bs, uint64_t offset, ++ uint64_t l2_offset, uint64_t **l2_slice) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int start_of_slice = l2_entry_size(s) * ++ (offset_to_l2_index(s, offset) - offset_to_l2_slice_index(s, offset)); ++ ++ return qcow2_cache_get(bs, s->l2_table_cache, l2_offset + start_of_slice, ++ (void **)l2_slice); ++} ++ ++/* ++ * Writes an L1 entry to disk (note that depending on the alignment ++ * requirements this function may write more that just one entry in ++ * order to prevent bdrv_pwrite from performing a read-modify-write) ++ */ ++int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int l1_start_index; ++ int i, ret; ++ int bufsize = MAX(L1E_SIZE, ++ MIN(bs->file->bs->bl.request_alignment, s->cluster_size)); ++ int nentries = bufsize / L1E_SIZE; ++ g_autofree uint64_t *buf = g_try_new0(uint64_t, nentries); ++ ++ if (buf == NULL) { ++ return -ENOMEM; ++ } ++ ++ l1_start_index = QEMU_ALIGN_DOWN(l1_index, nentries); ++ for (i = 0; i < MIN(nentries, s->l1_size - l1_start_index); i++) { ++ buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, ++ s->l1_table_offset + L1E_SIZE * l1_start_index, bufsize, false); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ++ ret = bdrv_pwrite_sync(bs->file, ++ s->l1_table_offset + L1E_SIZE * l1_start_index, ++ bufsize, buf, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * l2_allocate ++ * ++ * Allocate a new l2 entry in the file. If l1_index points to an already ++ * used entry in the L2 table (i.e. we are doing a copy on write for the L2 ++ * table) copy the contents of the old L2 table into the newly allocated one. ++ * Otherwise the new table is initialized with zeros. ++ * ++ */ ++ ++static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t old_l2_offset; ++ uint64_t *l2_slice = NULL; ++ unsigned slice, slice_size2, n_slices; ++ int64_t l2_offset; ++ int ret; ++ ++ old_l2_offset = s->l1_table[l1_index]; ++ ++ trace_qcow2_l2_allocate(bs, l1_index); ++ ++ /* allocate a new l2 entry */ ++ ++ l2_offset = qcow2_alloc_clusters(bs, s->l2_size * l2_entry_size(s)); ++ if (l2_offset < 0) { ++ ret = l2_offset; ++ goto fail; ++ } ++ ++ /* The offset must fit in the offset field of the L1 table entry */ ++ assert((l2_offset & L1E_OFFSET_MASK) == l2_offset); ++ ++ /* If we're allocating the table at offset 0 then something is wrong */ ++ if (l2_offset == 0) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " ++ "allocation of L2 table at offset 0"); ++ ret = -EIO; ++ goto fail; ++ } ++ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* allocate a new entry in the l2 cache */ ++ ++ slice_size2 = s->l2_slice_size * l2_entry_size(s); ++ n_slices = s->cluster_size / slice_size2; ++ ++ trace_qcow2_l2_allocate_get_empty(bs, l1_index); ++ for (slice = 0; slice < n_slices; slice++) { ++ ret = qcow2_cache_get_empty(bs, s->l2_table_cache, ++ l2_offset + slice * slice_size2, ++ (void **) &l2_slice); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if ((old_l2_offset & L1E_OFFSET_MASK) == 0) { ++ /* if there was no old l2 table, clear the new slice */ ++ memset(l2_slice, 0, slice_size2); ++ } else { ++ uint64_t *old_slice; ++ uint64_t old_l2_slice_offset = ++ (old_l2_offset & L1E_OFFSET_MASK) + slice * slice_size2; ++ ++ /* if there was an old l2 table, read a slice from the disk */ ++ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); ++ ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_slice_offset, ++ (void **) &old_slice); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ memcpy(l2_slice, old_slice, slice_size2); ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &old_slice); ++ } ++ ++ /* write the l2 slice to the file */ ++ BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); ++ ++ trace_qcow2_l2_allocate_write_l2(bs, l1_index); ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ ++ ret = qcow2_cache_flush(bs, s->l2_table_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* update the L1 entry */ ++ trace_qcow2_l2_allocate_write_l1(bs, l1_index); ++ s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; ++ ret = qcow2_write_l1_entry(bs, l1_index); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ trace_qcow2_l2_allocate_done(bs, l1_index, 0); ++ return 0; ++ ++fail: ++ trace_qcow2_l2_allocate_done(bs, l1_index, ret); ++ if (l2_slice != NULL) { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ s->l1_table[l1_index] = old_l2_offset; ++ if (l2_offset > 0) { ++ qcow2_free_clusters(bs, l2_offset, s->l2_size * l2_entry_size(s), ++ QCOW2_DISCARD_ALWAYS); ++ } ++ return ret; ++} ++ ++/* ++ * For a given L2 entry, count the number of contiguous subclusters of ++ * the same type starting from @sc_from. Compressed clusters are ++ * treated as if they were divided into subclusters of size ++ * s->subcluster_size. ++ * ++ * Return the number of contiguous subclusters and set @type to the ++ * subcluster type. ++ * ++ * If the L2 entry is invalid return -errno and set @type to ++ * QCOW2_SUBCLUSTER_INVALID. ++ */ ++static int GRAPH_RDLOCK ++qcow2_get_subcluster_range_type(BlockDriverState *bs, uint64_t l2_entry, ++ uint64_t l2_bitmap, unsigned sc_from, ++ QCow2SubclusterType *type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint32_t val; ++ ++ *type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_from); ++ ++ if (*type == QCOW2_SUBCLUSTER_INVALID) { ++ return -EINVAL; ++ } else if (!has_subclusters(s) || *type == QCOW2_SUBCLUSTER_COMPRESSED) { ++ return s->subclusters_per_cluster - sc_from; ++ } ++ ++ switch (*type) { ++ case QCOW2_SUBCLUSTER_NORMAL: ++ val = l2_bitmap | QCOW_OFLAG_SUB_ALLOC_RANGE(0, sc_from); ++ return cto32(val) - sc_from; ++ ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ val = (l2_bitmap | QCOW_OFLAG_SUB_ZERO_RANGE(0, sc_from)) >> 32; ++ return cto32(val) - sc_from; ++ ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ val = ((l2_bitmap >> 32) | l2_bitmap) ++ & ~QCOW_OFLAG_SUB_ALLOC_RANGE(0, sc_from); ++ return ctz32(val) - sc_from; ++ ++ default: ++ g_assert_not_reached(); ++ } ++} ++ ++/* ++ * Return the number of contiguous subclusters of the exact same type ++ * in a given L2 slice, starting from cluster @l2_index, subcluster ++ * @sc_index. Allocated subclusters are required to be contiguous in ++ * the image file. ++ * At most @nb_clusters are checked (note that this means clusters, ++ * not subclusters). ++ * Compressed clusters are always processed one by one but for the ++ * purpose of this count they are treated as if they were divided into ++ * subclusters of size s->subcluster_size. ++ * On failure return -errno and update @l2_index to point to the ++ * invalid entry. ++ */ ++static int GRAPH_RDLOCK ++count_contiguous_subclusters(BlockDriverState *bs, int nb_clusters, ++ unsigned sc_index, uint64_t *l2_slice, ++ unsigned *l2_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i, count = 0; ++ bool check_offset = false; ++ uint64_t expected_offset = 0; ++ QCow2SubclusterType expected_type = QCOW2_SUBCLUSTER_NORMAL, type; ++ ++ assert(*l2_index + nb_clusters <= s->l2_slice_size); ++ ++ for (i = 0; i < nb_clusters; i++) { ++ unsigned first_sc = (i == 0) ? sc_index : 0; ++ uint64_t l2_entry = get_l2_entry(s, l2_slice, *l2_index + i); ++ uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, *l2_index + i); ++ int ret = qcow2_get_subcluster_range_type(bs, l2_entry, l2_bitmap, ++ first_sc, &type); ++ if (ret < 0) { ++ *l2_index += i; /* Point to the invalid entry */ ++ return -EIO; ++ } ++ if (i == 0) { ++ if (type == QCOW2_SUBCLUSTER_COMPRESSED) { ++ /* Compressed clusters are always processed one by one */ ++ return ret; ++ } ++ expected_type = type; ++ expected_offset = l2_entry & L2E_OFFSET_MASK; ++ check_offset = (type == QCOW2_SUBCLUSTER_NORMAL || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC || ++ type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC); ++ } else if (type != expected_type) { ++ break; ++ } else if (check_offset) { ++ expected_offset += s->cluster_size; ++ if (expected_offset != (l2_entry & L2E_OFFSET_MASK)) { ++ break; ++ } ++ } ++ count += ret; ++ /* Stop if there are type changes before the end of the cluster */ ++ if (first_sc + ret < s->subclusters_per_cluster) { ++ break; ++ } ++ } ++ ++ return count; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++do_perform_cow_read(BlockDriverState *bs, uint64_t src_cluster_offset, ++ unsigned offset_in_cluster, QEMUIOVector *qiov) ++{ ++ int ret; ++ ++ if (qiov->size == 0) { ++ return 0; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_COW_READ); ++ ++ if (!bs->drv) { ++ return -ENOMEDIUM; ++ } ++ ++ /* ++ * We never deal with requests that don't satisfy ++ * bdrv_check_qiov_request(), and aligning requests to clusters never ++ * breaks this condition. So, do some assertions before calling ++ * bs->drv->bdrv_co_preadv_part() which has int64_t arguments. ++ */ ++ assert(src_cluster_offset <= INT64_MAX); ++ assert(src_cluster_offset + offset_in_cluster <= INT64_MAX); ++ /* Cast qiov->size to uint64_t to silence a compiler warning on -m32 */ ++ assert((uint64_t)qiov->size <= INT64_MAX); ++ bdrv_check_qiov_request(src_cluster_offset + offset_in_cluster, qiov->size, ++ qiov, 0, &error_abort); ++ /* ++ * Call .bdrv_co_readv() directly instead of using the public block-layer ++ * interface. This avoids double I/O throttling and request tracking, ++ * which can lead to deadlock when block layer copy-on-read is enabled. ++ */ ++ ret = bs->drv->bdrv_co_preadv_part(bs, ++ src_cluster_offset + offset_in_cluster, ++ qiov->size, qiov, 0, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++do_perform_cow_write(BlockDriverState *bs, uint64_t cluster_offset, ++ unsigned offset_in_cluster, QEMUIOVector *qiov) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ if (qiov->size == 0) { ++ return 0; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, ++ cluster_offset + offset_in_cluster, qiov->size, true); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_COW_WRITE); ++ ret = bdrv_co_pwritev(s->data_file, cluster_offset + offset_in_cluster, ++ qiov->size, qiov, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++/* ++ * get_host_offset ++ * ++ * For a given offset of the virtual disk find the equivalent host ++ * offset in the qcow2 file and store it in *host_offset. Neither ++ * offset needs to be aligned to a cluster boundary. ++ * ++ * If the cluster is unallocated then *host_offset will be 0. ++ * If the cluster is compressed then *host_offset will contain the l2 entry. ++ * ++ * On entry, *bytes is the maximum number of contiguous bytes starting at ++ * offset that we are interested in. ++ * ++ * On exit, *bytes is the number of bytes starting at offset that have the same ++ * subcluster type and (if applicable) are stored contiguously in the image ++ * file. The subcluster type is stored in *subcluster_type. ++ * Compressed clusters are always processed one by one. ++ * ++ * Returns 0 on success, -errno in error cases. ++ */ ++int qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, ++ unsigned int *bytes, uint64_t *host_offset, ++ QCow2SubclusterType *subcluster_type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int l2_index, sc_index; ++ uint64_t l1_index, l2_offset, *l2_slice, l2_entry, l2_bitmap; ++ int sc; ++ unsigned int offset_in_cluster; ++ uint64_t bytes_available, bytes_needed, nb_clusters; ++ QCow2SubclusterType type; ++ int ret; ++ ++ offset_in_cluster = offset_into_cluster(s, offset); ++ bytes_needed = (uint64_t) *bytes + offset_in_cluster; ++ ++ /* compute how many bytes there are between the start of the cluster ++ * containing offset and the end of the l2 slice that contains ++ * the entry pointing to it */ ++ bytes_available = ++ ((uint64_t) (s->l2_slice_size - offset_to_l2_slice_index(s, offset))) ++ << s->cluster_bits; ++ ++ if (bytes_needed > bytes_available) { ++ bytes_needed = bytes_available; ++ } ++ ++ *host_offset = 0; ++ ++ /* seek to the l2 offset in the l1 table */ ++ ++ l1_index = offset_to_l1_index(s, offset); ++ if (l1_index >= s->l1_size) { ++ type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; ++ goto out; ++ } ++ ++ l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; ++ if (!l2_offset) { ++ type = QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN; ++ goto out; ++ } ++ ++ if (offset_into_cluster(s, l2_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64 ++ " unaligned (L1 index: %#" PRIx64 ")", ++ l2_offset, l1_index); ++ return -EIO; ++ } ++ ++ /* load the l2 slice in memory */ ++ ++ ret = l2_load(bs, offset, l2_offset, &l2_slice); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* find the cluster offset for the given disk offset */ ++ ++ l2_index = offset_to_l2_slice_index(s, offset); ++ sc_index = offset_to_sc_index(s, offset); ++ l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); ++ ++ nb_clusters = size_to_clusters(s, bytes_needed); ++ /* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned ++ * integers; the minimum cluster size is 512, so this assertion is always ++ * true */ ++ assert(nb_clusters <= INT_MAX); ++ ++ type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); ++ if (s->qcow_version < 3 && (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found" ++ " in pre-v3 image (L2 offset: %#" PRIx64 ++ ", L2 index: %#x)", l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ switch (type) { ++ case QCOW2_SUBCLUSTER_INVALID: ++ break; /* This is handled by count_contiguous_subclusters() below */ ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ if (has_data_file(bs)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster " ++ "entry found in image with external data " ++ "file (L2 offset: %#" PRIx64 ", L2 index: " ++ "%#x)", l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ *host_offset = l2_entry; ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_NORMAL: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: { ++ uint64_t host_cluster_offset = l2_entry & L2E_OFFSET_MASK; ++ *host_offset = host_cluster_offset + offset_in_cluster; ++ if (offset_into_cluster(s, host_cluster_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, ++ "Cluster allocation offset %#" ++ PRIx64 " unaligned (L2 offset: %#" PRIx64 ++ ", L2 index: %#x)", host_cluster_offset, ++ l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ if (has_data_file(bs) && *host_offset != offset) { ++ qcow2_signal_corruption(bs, true, -1, -1, ++ "External data file host cluster offset %#" ++ PRIx64 " does not match guest cluster " ++ "offset: %#" PRIx64 ++ ", L2 index: %#x)", host_cluster_offset, ++ offset - offset_in_cluster, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ break; ++ } ++ default: ++ abort(); ++ } ++ ++ sc = count_contiguous_subclusters(bs, nb_clusters, sc_index, ++ l2_slice, &l2_index); ++ if (sc < 0) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Invalid cluster entry found " ++ " (L2 offset: %#" PRIx64 ", L2 index: %#x)", ++ l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ bytes_available = ((int64_t)sc + sc_index) << s->subcluster_bits; ++ ++out: ++ if (bytes_available > bytes_needed) { ++ bytes_available = bytes_needed; ++ } ++ ++ /* bytes_available <= bytes_needed <= *bytes + offset_in_cluster; ++ * subtracting offset_in_cluster will therefore definitely yield something ++ * not exceeding UINT_MAX */ ++ assert(bytes_available - offset_in_cluster <= UINT_MAX); ++ *bytes = bytes_available - offset_in_cluster; ++ ++ *subcluster_type = type; ++ ++ return 0; ++ ++fail: ++ qcow2_cache_put(s->l2_table_cache, (void **)&l2_slice); ++ return ret; ++} ++ ++/* ++ * get_cluster_table ++ * ++ * for a given disk offset, load (and allocate if needed) ++ * the appropriate slice of its l2 table. ++ * ++ * the cluster index in the l2 slice is given to the caller. ++ * ++ * Returns 0 on success, -errno in failure case ++ */ ++static int GRAPH_RDLOCK ++get_cluster_table(BlockDriverState *bs, uint64_t offset, ++ uint64_t **new_l2_slice, int *new_l2_index) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int l2_index; ++ uint64_t l1_index, l2_offset; ++ uint64_t *l2_slice = NULL; ++ int ret; ++ ++ /* seek to the l2 offset in the l1 table */ ++ ++ l1_index = offset_to_l1_index(s, offset); ++ if (l1_index >= s->l1_size) { ++ ret = qcow2_grow_l1_table(bs, l1_index + 1, false); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ assert(l1_index < s->l1_size); ++ l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; ++ if (offset_into_cluster(s, l2_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" PRIx64 ++ " unaligned (L1 index: %#" PRIx64 ")", ++ l2_offset, l1_index); ++ return -EIO; ++ } ++ ++ if (!(s->l1_table[l1_index] & QCOW_OFLAG_COPIED)) { ++ /* First allocate a new L2 table (and do COW if needed) */ ++ ret = l2_allocate(bs, l1_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Then decrease the refcount of the old table */ ++ if (l2_offset) { ++ qcow2_free_clusters(bs, l2_offset, s->l2_size * l2_entry_size(s), ++ QCOW2_DISCARD_OTHER); ++ } ++ ++ /* Get the offset of the newly-allocated l2 table */ ++ l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; ++ assert(offset_into_cluster(s, l2_offset) == 0); ++ } ++ ++ /* load the l2 slice in memory */ ++ ret = l2_load(bs, offset, l2_offset, &l2_slice); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* find the cluster offset for the given disk offset */ ++ ++ l2_index = offset_to_l2_slice_index(s, offset); ++ ++ *new_l2_slice = l2_slice; ++ *new_l2_index = l2_index; ++ ++ return 0; ++} ++ ++/* ++ * alloc_compressed_cluster_offset ++ * ++ * For a given offset on the virtual disk, allocate a new compressed cluster ++ * and put the host offset of the cluster into *host_offset. If a cluster is ++ * already allocated at the offset, return an error. ++ * ++ * Return 0 on success and -errno in error cases ++ */ ++int coroutine_fn GRAPH_RDLOCK ++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; ++ uint64_t *l2_slice; ++ int64_t cluster_offset; ++ int nb_csectors; ++ ++ if (has_data_file(bs)) { ++ return 0; ++ } ++ ++ ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Compression can't overwrite anything. Fail if the cluster was already ++ * allocated. */ ++ cluster_offset = get_l2_entry(s, l2_slice, l2_index); ++ if (cluster_offset & L2E_OFFSET_MASK) { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ return -EIO; ++ } ++ ++ cluster_offset = qcow2_alloc_bytes(bs, compressed_size); ++ if (cluster_offset < 0) { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ return cluster_offset; ++ } ++ ++ nb_csectors = ++ (cluster_offset + compressed_size - 1) / QCOW2_COMPRESSED_SECTOR_SIZE - ++ (cluster_offset / QCOW2_COMPRESSED_SECTOR_SIZE); ++ ++ /* The offset and size must fit in their fields of the L2 table entry */ ++ assert((cluster_offset & s->cluster_offset_mask) == cluster_offset); ++ assert((nb_csectors & s->csize_mask) == nb_csectors); ++ ++ cluster_offset |= QCOW_OFLAG_COMPRESSED | ++ ((uint64_t)nb_csectors << s->csize_shift); ++ ++ /* update L2 table */ ++ ++ /* compressed clusters never have the copied flag */ ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ set_l2_entry(s, l2_slice, l2_index, cluster_offset); ++ if (has_subclusters(s)) { ++ set_l2_bitmap(s, l2_slice, l2_index, 0); ++ } ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ *host_offset = cluster_offset & s->cluster_offset_mask; ++ return 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++perform_cow(BlockDriverState *bs, QCowL2Meta *m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2COWRegion *start = &m->cow_start; ++ Qcow2COWRegion *end = &m->cow_end; ++ unsigned buffer_size; ++ unsigned data_bytes = end->offset - (start->offset + start->nb_bytes); ++ bool merge_reads; ++ uint8_t *start_buffer, *end_buffer; ++ QEMUIOVector qiov; ++ int ret; ++ ++ assert(start->nb_bytes <= UINT_MAX - end->nb_bytes); ++ assert(start->nb_bytes + end->nb_bytes <= UINT_MAX - data_bytes); ++ assert(start->offset + start->nb_bytes <= end->offset); ++ ++ if ((start->nb_bytes == 0 && end->nb_bytes == 0) || m->skip_cow) { ++ return 0; ++ } ++ ++ /* If we have to read both the start and end COW regions and the ++ * middle region is not too large then perform just one read ++ * operation */ ++ merge_reads = start->nb_bytes && end->nb_bytes && data_bytes <= 16384; ++ if (merge_reads) { ++ buffer_size = start->nb_bytes + data_bytes + end->nb_bytes; ++ } else { ++ /* If we have to do two reads, add some padding in the middle ++ * if necessary to make sure that the end region is optimally ++ * aligned. */ ++ size_t align = bdrv_opt_mem_align(bs); ++ assert(align > 0 && align <= UINT_MAX); ++ assert(QEMU_ALIGN_UP(start->nb_bytes, align) <= ++ UINT_MAX - end->nb_bytes); ++ buffer_size = QEMU_ALIGN_UP(start->nb_bytes, align) + end->nb_bytes; ++ } ++ ++ /* Reserve a buffer large enough to store all the data that we're ++ * going to read */ ++ start_buffer = qemu_try_blockalign(bs, buffer_size); ++ if (start_buffer == NULL) { ++ return -ENOMEM; ++ } ++ /* The part of the buffer where the end region is located */ ++ end_buffer = start_buffer + buffer_size - end->nb_bytes; ++ ++ qemu_iovec_init(&qiov, 2 + (m->data_qiov ? ++ qemu_iovec_subvec_niov(m->data_qiov, ++ m->data_qiov_offset, ++ data_bytes) ++ : 0)); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ /* First we read the existing data from both COW regions. We ++ * either read the whole region in one go, or the start and end ++ * regions separately. */ ++ if (merge_reads) { ++ qemu_iovec_add(&qiov, start_buffer, buffer_size); ++ ret = do_perform_cow_read(bs, m->offset, start->offset, &qiov); ++ } else { ++ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); ++ ret = do_perform_cow_read(bs, m->offset, start->offset, &qiov); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ qemu_iovec_reset(&qiov); ++ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); ++ ret = do_perform_cow_read(bs, m->offset, end->offset, &qiov); ++ } ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Encrypt the data if necessary before writing it */ ++ if (bs->encrypted) { ++ ret = qcow2_co_encrypt(bs, ++ m->alloc_offset + start->offset, ++ m->offset + start->offset, ++ start_buffer, start->nb_bytes); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_co_encrypt(bs, ++ m->alloc_offset + end->offset, ++ m->offset + end->offset, ++ end_buffer, end->nb_bytes); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* And now we can write everything. If we have the guest data we ++ * can write everything in one single operation */ ++ if (m->data_qiov) { ++ qemu_iovec_reset(&qiov); ++ if (start->nb_bytes) { ++ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); ++ } ++ qemu_iovec_concat(&qiov, m->data_qiov, m->data_qiov_offset, data_bytes); ++ if (end->nb_bytes) { ++ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); ++ } ++ /* NOTE: we have a write_aio blkdebug event here followed by ++ * a cow_write one in do_perform_cow_write(), but there's only ++ * one single I/O operation */ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ++ ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov); ++ } else { ++ /* If there's no guest data then write both COW regions separately */ ++ qemu_iovec_reset(&qiov); ++ qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); ++ ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ qemu_iovec_reset(&qiov); ++ qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); ++ ret = do_perform_cow_write(bs, m->alloc_offset, end->offset, &qiov); ++ } ++ ++fail: ++ qemu_co_mutex_lock(&s->lock); ++ ++ /* ++ * Before we update the L2 table to actually point to the new cluster, we ++ * need to be sure that the refcounts have been increased and COW was ++ * handled. ++ */ ++ if (ret == 0) { ++ qcow2_cache_depends_on_flush(s->l2_table_cache); ++ } ++ ++ qemu_vfree(start_buffer); ++ qemu_iovec_destroy(&qiov); ++ return ret; ++} ++ ++int coroutine_fn qcow2_alloc_cluster_link_l2(BlockDriverState *bs, ++ QCowL2Meta *m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i, j = 0, l2_index, ret; ++ uint64_t *old_cluster, *l2_slice; ++ uint64_t cluster_offset = m->alloc_offset; ++ ++ trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); ++ assert(m->nb_clusters > 0); ++ ++ old_cluster = g_try_new(uint64_t, m->nb_clusters); ++ if (old_cluster == NULL) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ /* copy content of unmodified sectors */ ++ ret = perform_cow(bs, m); ++ if (ret < 0) { ++ goto err; ++ } ++ ++ /* Update L2 table. */ ++ if (s->use_lazy_refcounts) { ++ qcow2_mark_dirty(bs); ++ } ++ if (qcow2_need_accurate_refcounts(s)) { ++ qcow2_cache_set_dependency(bs, s->l2_table_cache, ++ s->refcount_block_cache); ++ } ++ ++ ret = get_cluster_table(bs, m->offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ goto err; ++ } ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ ++ assert(l2_index + m->nb_clusters <= s->l2_slice_size); ++ assert(m->cow_end.offset + m->cow_end.nb_bytes <= ++ m->nb_clusters << s->cluster_bits); ++ for (i = 0; i < m->nb_clusters; i++) { ++ uint64_t offset = cluster_offset + ((uint64_t)i << s->cluster_bits); ++ /* if two concurrent writes happen to the same unallocated cluster ++ * each write allocates separate cluster and writes data concurrently. ++ * The first one to complete updates l2 table with pointer to its ++ * cluster the second one has to do RMW (which is done above by ++ * perform_cow()), update l2 table with its cluster pointer and free ++ * old cluster. This is what this loop does */ ++ if (get_l2_entry(s, l2_slice, l2_index + i) != 0) { ++ old_cluster[j++] = get_l2_entry(s, l2_slice, l2_index + i); ++ } ++ ++ /* The offset must fit in the offset field of the L2 table entry */ ++ assert((offset & L2E_OFFSET_MASK) == offset); ++ ++ set_l2_entry(s, l2_slice, l2_index + i, offset | QCOW_OFLAG_COPIED); ++ ++ /* Update bitmap with the subclusters that were just written */ ++ if (has_subclusters(s) && !m->prealloc) { ++ uint64_t l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); ++ unsigned written_from = m->cow_start.offset; ++ unsigned written_to = m->cow_end.offset + m->cow_end.nb_bytes; ++ int first_sc, last_sc; ++ /* Narrow written_from and written_to down to the current cluster */ ++ written_from = MAX(written_from, i << s->cluster_bits); ++ written_to = MIN(written_to, (i + 1) << s->cluster_bits); ++ assert(written_from < written_to); ++ first_sc = offset_to_sc_index(s, written_from); ++ last_sc = offset_to_sc_index(s, written_to - 1); ++ l2_bitmap |= QCOW_OFLAG_SUB_ALLOC_RANGE(first_sc, last_sc + 1); ++ l2_bitmap &= ~QCOW_OFLAG_SUB_ZERO_RANGE(first_sc, last_sc + 1); ++ set_l2_bitmap(s, l2_slice, l2_index + i, l2_bitmap); ++ } ++ } ++ ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ /* ++ * If this was a COW, we need to decrease the refcount of the old cluster. ++ * ++ * Don't discard clusters that reach a refcount of 0 (e.g. compressed ++ * clusters), the next write will reuse them anyway. ++ */ ++ if (!m->keep_old_clusters && j != 0) { ++ for (i = 0; i < j; i++) { ++ qcow2_free_any_cluster(bs, old_cluster[i], QCOW2_DISCARD_NEVER); ++ } ++ } ++ ++ ret = 0; ++err: ++ g_free(old_cluster); ++ return ret; ++ } ++ ++/** ++ * Frees the allocated clusters because the request failed and they won't ++ * actually be linked. ++ */ ++void coroutine_fn qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ if (!has_data_file(bs) && !m->keep_old_clusters) { ++ qcow2_free_clusters(bs, m->alloc_offset, ++ m->nb_clusters << s->cluster_bits, ++ QCOW2_DISCARD_NEVER); ++ } ++} ++ ++/* ++ * For a given write request, create a new QCowL2Meta structure, add ++ * it to @m and the BDRVQcow2State.cluster_allocs list. If the write ++ * request does not need copy-on-write or changes to the L2 metadata ++ * then this function does nothing. ++ * ++ * @host_cluster_offset points to the beginning of the first cluster. ++ * ++ * @guest_offset and @bytes indicate the offset and length of the ++ * request. ++ * ++ * @l2_slice contains the L2 entries of all clusters involved in this ++ * write request. ++ * ++ * If @keep_old is true it means that the clusters were already ++ * allocated and will be overwritten. If false then the clusters are ++ * new and we have to decrease the reference count of the old ones. ++ * ++ * Returns 0 on success, -errno on failure. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++calculate_l2_meta(BlockDriverState *bs, uint64_t host_cluster_offset, ++ uint64_t guest_offset, unsigned bytes, uint64_t *l2_slice, ++ QCowL2Meta **m, bool keep_old) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int sc_index, l2_index = offset_to_l2_slice_index(s, guest_offset); ++ uint64_t l2_entry, l2_bitmap; ++ unsigned cow_start_from, cow_end_to; ++ unsigned cow_start_to = offset_into_cluster(s, guest_offset); ++ unsigned cow_end_from = cow_start_to + bytes; ++ unsigned nb_clusters = size_to_clusters(s, cow_end_from); ++ QCowL2Meta *old_m = *m; ++ QCow2SubclusterType type; ++ int i; ++ bool skip_cow = keep_old; ++ ++ assert(nb_clusters <= s->l2_slice_size - l2_index); ++ ++ /* Check the type of all affected subclusters */ ++ for (i = 0; i < nb_clusters; i++) { ++ l2_entry = get_l2_entry(s, l2_slice, l2_index + i); ++ l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); ++ if (skip_cow) { ++ unsigned write_from = MAX(cow_start_to, i << s->cluster_bits); ++ unsigned write_to = MIN(cow_end_from, (i + 1) << s->cluster_bits); ++ int first_sc = offset_to_sc_index(s, write_from); ++ int last_sc = offset_to_sc_index(s, write_to - 1); ++ int cnt = qcow2_get_subcluster_range_type(bs, l2_entry, l2_bitmap, ++ first_sc, &type); ++ /* Is any of the subclusters of type != QCOW2_SUBCLUSTER_NORMAL ? */ ++ if (type != QCOW2_SUBCLUSTER_NORMAL || first_sc + cnt <= last_sc) { ++ skip_cow = false; ++ } ++ } else { ++ /* If we can't skip the cow we can still look for invalid entries */ ++ type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, 0); ++ } ++ if (type == QCOW2_SUBCLUSTER_INVALID) { ++ int l1_index = offset_to_l1_index(s, guest_offset); ++ uint64_t l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK; ++ qcow2_signal_corruption(bs, true, -1, -1, "Invalid cluster " ++ "entry found (L2 offset: %#" PRIx64 ++ ", L2 index: %#x)", ++ l2_offset, l2_index + i); ++ return -EIO; ++ } ++ } ++ ++ if (skip_cow) { ++ return 0; ++ } ++ ++ /* Get the L2 entry of the first cluster */ ++ l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); ++ sc_index = offset_to_sc_index(s, guest_offset); ++ type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); ++ ++ if (!keep_old) { ++ switch (type) { ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ cow_start_from = 0; ++ break; ++ case QCOW2_SUBCLUSTER_NORMAL: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ if (has_subclusters(s)) { ++ /* Skip all leading zero and unallocated subclusters */ ++ uint32_t alloc_bitmap = l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC; ++ cow_start_from = ++ MIN(sc_index, ctz32(alloc_bitmap)) << s->subcluster_bits; ++ } else { ++ cow_start_from = 0; ++ } ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ cow_start_from = sc_index << s->subcluster_bits; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } else { ++ switch (type) { ++ case QCOW2_SUBCLUSTER_NORMAL: ++ cow_start_from = cow_start_to; ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ cow_start_from = sc_index << s->subcluster_bits; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ /* Get the L2 entry of the last cluster */ ++ l2_index += nb_clusters - 1; ++ l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); ++ sc_index = offset_to_sc_index(s, guest_offset + bytes - 1); ++ type = qcow2_get_subcluster_type(bs, l2_entry, l2_bitmap, sc_index); ++ ++ if (!keep_old) { ++ switch (type) { ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ cow_end_to = ROUND_UP(cow_end_from, s->cluster_size); ++ break; ++ case QCOW2_SUBCLUSTER_NORMAL: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ cow_end_to = ROUND_UP(cow_end_from, s->cluster_size); ++ if (has_subclusters(s)) { ++ /* Skip all trailing zero and unallocated subclusters */ ++ uint32_t alloc_bitmap = l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC; ++ cow_end_to -= ++ MIN(s->subclusters_per_cluster - sc_index - 1, ++ clz32(alloc_bitmap)) << s->subcluster_bits; ++ } ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ cow_end_to = ROUND_UP(cow_end_from, s->subcluster_size); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } else { ++ switch (type) { ++ case QCOW2_SUBCLUSTER_NORMAL: ++ cow_end_to = cow_end_from; ++ break; ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ cow_end_to = ROUND_UP(cow_end_from, s->subcluster_size); ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } ++ ++ *m = g_malloc0(sizeof(**m)); ++ **m = (QCowL2Meta) { ++ .next = old_m, ++ ++ .alloc_offset = host_cluster_offset, ++ .offset = start_of_cluster(s, guest_offset), ++ .nb_clusters = nb_clusters, ++ ++ .keep_old_clusters = keep_old, ++ ++ .cow_start = { ++ .offset = cow_start_from, ++ .nb_bytes = cow_start_to - cow_start_from, ++ }, ++ .cow_end = { ++ .offset = cow_end_from, ++ .nb_bytes = cow_end_to - cow_end_from, ++ }, ++ }; ++ ++ qemu_co_queue_init(&(*m)->dependent_requests); ++ QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight); ++ ++ return 0; ++} ++ ++/* ++ * Returns true if writing to the cluster pointed to by @l2_entry ++ * requires a new allocation (that is, if the cluster is unallocated ++ * or has refcount > 1 and therefore cannot be written in-place). ++ */ ++static bool GRAPH_RDLOCK ++cluster_needs_new_alloc(BlockDriverState *bs, uint64_t l2_entry) ++{ ++ switch (qcow2_get_cluster_type(bs, l2_entry)) { ++ case QCOW2_CLUSTER_NORMAL: ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ if (l2_entry & QCOW_OFLAG_COPIED) { ++ return false; ++ } ++ /* fallthrough */ ++ case QCOW2_CLUSTER_UNALLOCATED: ++ case QCOW2_CLUSTER_COMPRESSED: ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ return true; ++ default: ++ abort(); ++ } ++} ++ ++/* ++ * Returns the number of contiguous clusters that can be written to ++ * using one single write request, starting from @l2_index. ++ * At most @nb_clusters are checked. ++ * ++ * If @new_alloc is true this counts clusters that are either ++ * unallocated, or allocated but with refcount > 1 (so they need to be ++ * newly allocated and COWed). ++ * ++ * If @new_alloc is false this counts clusters that are already ++ * allocated and can be overwritten in-place (this includes clusters ++ * of type QCOW2_CLUSTER_ZERO_ALLOC). ++ */ ++static int GRAPH_RDLOCK ++count_single_write_clusters(BlockDriverState *bs, int nb_clusters, ++ uint64_t *l2_slice, int l2_index, bool new_alloc) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ uint64_t expected_offset = l2_entry & L2E_OFFSET_MASK; ++ int i; ++ ++ for (i = 0; i < nb_clusters; i++) { ++ l2_entry = get_l2_entry(s, l2_slice, l2_index + i); ++ if (cluster_needs_new_alloc(bs, l2_entry) != new_alloc) { ++ break; ++ } ++ if (!new_alloc) { ++ if (expected_offset != (l2_entry & L2E_OFFSET_MASK)) { ++ break; ++ } ++ expected_offset += s->cluster_size; ++ } ++ } ++ ++ assert(i <= nb_clusters); ++ return i; ++} ++ ++/* ++ * Check if there already is an AIO write request in flight which allocates ++ * the same cluster. In this case we need to wait until the previous ++ * request has completed and updated the L2 table accordingly. ++ * ++ * Returns: ++ * 0 if there was no dependency. *cur_bytes indicates the number of ++ * bytes from guest_offset that can be read before the next ++ * dependency must be processed (or the request is complete) ++ * ++ * -EAGAIN if we had to wait for another request, previously gathered ++ * information on cluster allocation may be invalid now. The caller ++ * must start over anyway, so consider *cur_bytes undefined. ++ */ ++static int coroutine_fn handle_dependencies(BlockDriverState *bs, ++ uint64_t guest_offset, ++ uint64_t *cur_bytes, QCowL2Meta **m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowL2Meta *old_alloc; ++ uint64_t bytes = *cur_bytes; ++ ++ QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) { ++ ++ uint64_t start = guest_offset; ++ uint64_t end = start + bytes; ++ uint64_t old_start = start_of_cluster(s, l2meta_cow_start(old_alloc)); ++ uint64_t old_end = ROUND_UP(l2meta_cow_end(old_alloc), s->cluster_size); ++ ++ if (end <= old_start || start >= old_end) { ++ /* No intersection */ ++ continue; ++ } ++ ++ if (old_alloc->keep_old_clusters && ++ (end <= l2meta_cow_start(old_alloc) || ++ start >= l2meta_cow_end(old_alloc))) ++ { ++ /* ++ * Clusters intersect but COW areas don't. And cluster itself is ++ * already allocated. So, there is no actual conflict. ++ */ ++ continue; ++ } ++ ++ /* Conflict */ ++ ++ if (start < old_start) { ++ /* Stop at the start of a running allocation */ ++ bytes = old_start - start; ++ } else { ++ bytes = 0; ++ } ++ ++ /* ++ * Stop if an l2meta already exists. After yielding, it wouldn't ++ * be valid any more, so we'd have to clean up the old L2Metas ++ * and deal with requests depending on them before starting to ++ * gather new ones. Not worth the trouble. ++ */ ++ if (bytes == 0 && *m) { ++ *cur_bytes = 0; ++ return 0; ++ } ++ ++ if (bytes == 0) { ++ /* ++ * Wait for the dependency to complete. We need to recheck ++ * the free/allocated clusters when we continue. ++ */ ++ qemu_co_queue_wait(&old_alloc->dependent_requests, &s->lock); ++ return -EAGAIN; ++ } ++ } ++ ++ /* Make sure that existing clusters and new allocations are only used up to ++ * the next dependency if we shortened the request above */ ++ *cur_bytes = bytes; ++ ++ return 0; ++} ++ ++/* ++ * Checks how many already allocated clusters that don't require a new ++ * allocation there are at the given guest_offset (up to *bytes). ++ * If *host_offset is not INV_OFFSET, only physically contiguous clusters ++ * beginning at this host offset are counted. ++ * ++ * Note that guest_offset may not be cluster aligned. In this case, the ++ * returned *host_offset points to exact byte referenced by guest_offset and ++ * therefore isn't cluster aligned as well. ++ * ++ * Returns: ++ * 0: if no allocated clusters are available at the given offset. ++ * *bytes is normally unchanged. It is set to 0 if the cluster ++ * is allocated and can be overwritten in-place but doesn't have ++ * the right physical offset. ++ * ++ * 1: if allocated clusters that can be overwritten in place are ++ * available at the requested offset. *bytes may have decreased ++ * and describes the length of the area that can be written to. ++ * ++ * -errno: in error cases ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++handle_copied(BlockDriverState *bs, uint64_t guest_offset, ++ uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int l2_index; ++ uint64_t l2_entry, cluster_offset; ++ uint64_t *l2_slice; ++ uint64_t nb_clusters; ++ unsigned int keep_clusters; ++ int ret; ++ ++ trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, ++ *bytes); ++ ++ assert(*host_offset == INV_OFFSET || offset_into_cluster(s, guest_offset) ++ == offset_into_cluster(s, *host_offset)); ++ ++ /* ++ * Calculate the number of clusters to look for. We stop at L2 slice ++ * boundaries to keep things simple. ++ */ ++ nb_clusters = ++ size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); ++ ++ l2_index = offset_to_l2_slice_index(s, guest_offset); ++ nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); ++ /* Limit total byte count to BDRV_REQUEST_MAX_BYTES */ ++ nb_clusters = MIN(nb_clusters, BDRV_REQUEST_MAX_BYTES >> s->cluster_bits); ++ ++ /* Find L2 entry for the first involved cluster */ ++ ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ l2_entry = get_l2_entry(s, l2_slice, l2_index); ++ cluster_offset = l2_entry & L2E_OFFSET_MASK; ++ ++ if (!cluster_needs_new_alloc(bs, l2_entry)) { ++ if (offset_into_cluster(s, cluster_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "%s cluster offset " ++ "%#" PRIx64 " unaligned (guest offset: %#" ++ PRIx64 ")", l2_entry & QCOW_OFLAG_ZERO ? ++ "Preallocated zero" : "Data", ++ cluster_offset, guest_offset); ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* If a specific host_offset is required, check it */ ++ if (*host_offset != INV_OFFSET && cluster_offset != *host_offset) { ++ *bytes = 0; ++ ret = 0; ++ goto out; ++ } ++ ++ /* We keep all QCOW_OFLAG_COPIED clusters */ ++ keep_clusters = count_single_write_clusters(bs, nb_clusters, l2_slice, ++ l2_index, false); ++ assert(keep_clusters <= nb_clusters); ++ ++ *bytes = MIN(*bytes, ++ keep_clusters * s->cluster_size ++ - offset_into_cluster(s, guest_offset)); ++ assert(*bytes != 0); ++ ++ ret = calculate_l2_meta(bs, cluster_offset, guest_offset, ++ *bytes, l2_slice, m, true); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = 1; ++ } else { ++ ret = 0; ++ } ++ ++ /* Cleanup */ ++out: ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ /* Only return a host offset if we actually made progress. Otherwise we ++ * would make requirements for handle_alloc() that it can't fulfill */ ++ if (ret > 0) { ++ *host_offset = cluster_offset + offset_into_cluster(s, guest_offset); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Allocates new clusters for the given guest_offset. ++ * ++ * At most *nb_clusters are allocated, and on return *nb_clusters is updated to ++ * contain the number of clusters that have been allocated and are contiguous ++ * in the image file. ++ * ++ * If *host_offset is not INV_OFFSET, it specifies the offset in the image file ++ * at which the new clusters must start. *nb_clusters can be 0 on return in ++ * this case if the cluster at host_offset is already in use. If *host_offset ++ * is INV_OFFSET, the clusters can be allocated anywhere in the image file. ++ * ++ * *host_offset is updated to contain the offset into the image file at which ++ * the first allocated cluster starts. ++ * ++ * Return 0 on success and -errno in error cases. -EAGAIN means that the ++ * function has been waiting for another request and the allocation must be ++ * restarted, but the whole request should not be failed. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, ++ uint64_t *host_offset, uint64_t *nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, ++ *host_offset, *nb_clusters); ++ ++ if (has_data_file(bs)) { ++ assert(*host_offset == INV_OFFSET || ++ *host_offset == start_of_cluster(s, guest_offset)); ++ *host_offset = start_of_cluster(s, guest_offset); ++ return 0; ++ } ++ ++ /* Allocate new clusters */ ++ trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); ++ if (*host_offset == INV_OFFSET) { ++ int64_t cluster_offset = ++ qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size); ++ if (cluster_offset < 0) { ++ return cluster_offset; ++ } ++ *host_offset = cluster_offset; ++ return 0; ++ } else { ++ int64_t ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters); ++ if (ret < 0) { ++ return ret; ++ } ++ *nb_clusters = ret; ++ return 0; ++ } ++} ++ ++/* ++ * Allocates new clusters for an area that is either still unallocated or ++ * cannot be overwritten in-place. If *host_offset is not INV_OFFSET, ++ * clusters are only allocated if the new allocation can match the specified ++ * host offset. ++ * ++ * Note that guest_offset may not be cluster aligned. In this case, the ++ * returned *host_offset points to exact byte referenced by guest_offset and ++ * therefore isn't cluster aligned as well. ++ * ++ * Returns: ++ * 0: if no clusters could be allocated. *bytes is set to 0, ++ * *host_offset is left unchanged. ++ * ++ * 1: if new clusters were allocated. *bytes may be decreased if the ++ * new allocation doesn't cover all of the requested area. ++ * *host_offset is updated to contain the host offset of the first ++ * newly allocated cluster. ++ * ++ * -errno: in error cases ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++handle_alloc(BlockDriverState *bs, uint64_t guest_offset, ++ uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int l2_index; ++ uint64_t *l2_slice; ++ uint64_t nb_clusters; ++ int ret; ++ ++ uint64_t alloc_cluster_offset; ++ ++ trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, ++ *bytes); ++ assert(*bytes > 0); ++ ++ /* ++ * Calculate the number of clusters to look for. We stop at L2 slice ++ * boundaries to keep things simple. ++ */ ++ nb_clusters = ++ size_to_clusters(s, offset_into_cluster(s, guest_offset) + *bytes); ++ ++ l2_index = offset_to_l2_slice_index(s, guest_offset); ++ nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); ++ /* Limit total allocation byte count to BDRV_REQUEST_MAX_BYTES */ ++ nb_clusters = MIN(nb_clusters, BDRV_REQUEST_MAX_BYTES >> s->cluster_bits); ++ ++ /* Find L2 entry for the first involved cluster */ ++ ret = get_cluster_table(bs, guest_offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ nb_clusters = count_single_write_clusters(bs, nb_clusters, ++ l2_slice, l2_index, true); ++ ++ /* This function is only called when there were no non-COW clusters, so if ++ * we can't find any unallocated or COW clusters either, something is ++ * wrong with our code. */ ++ assert(nb_clusters > 0); ++ ++ /* Allocate at a given offset in the image file */ ++ alloc_cluster_offset = *host_offset == INV_OFFSET ? INV_OFFSET : ++ start_of_cluster(s, *host_offset); ++ ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset, ++ &nb_clusters); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ /* Can't extend contiguous allocation */ ++ if (nb_clusters == 0) { ++ *bytes = 0; ++ ret = 0; ++ goto out; ++ } ++ ++ assert(alloc_cluster_offset != INV_OFFSET); ++ ++ /* ++ * Save info needed for meta data update. ++ * ++ * requested_bytes: Number of bytes from the start of the first ++ * newly allocated cluster to the end of the (possibly shortened ++ * before) write request. ++ * ++ * avail_bytes: Number of bytes from the start of the first ++ * newly allocated to the end of the last newly allocated cluster. ++ * ++ * nb_bytes: The number of bytes from the start of the first ++ * newly allocated cluster to the end of the area that the write ++ * request actually writes to (excluding COW at the end) ++ */ ++ uint64_t requested_bytes = *bytes + offset_into_cluster(s, guest_offset); ++ int avail_bytes = nb_clusters << s->cluster_bits; ++ int nb_bytes = MIN(requested_bytes, avail_bytes); ++ ++ *host_offset = alloc_cluster_offset + offset_into_cluster(s, guest_offset); ++ *bytes = MIN(*bytes, nb_bytes - offset_into_cluster(s, guest_offset)); ++ assert(*bytes != 0); ++ ++ ret = calculate_l2_meta(bs, alloc_cluster_offset, guest_offset, *bytes, ++ l2_slice, m, false); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ ret = 1; ++ ++out: ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ return ret; ++} ++ ++/* ++ * For a given area on the virtual disk defined by @offset and @bytes, ++ * find the corresponding area on the qcow2 image, allocating new ++ * clusters (or subclusters) if necessary. The result can span a ++ * combination of allocated and previously unallocated clusters. ++ * ++ * Note that offset may not be cluster aligned. In this case, the returned ++ * *host_offset points to exact byte referenced by offset and therefore ++ * isn't cluster aligned as well. ++ * ++ * On return, @host_offset is set to the beginning of the requested ++ * area. This area is guaranteed to be contiguous on the qcow2 file ++ * but it can be smaller than initially requested. In this case @bytes ++ * is updated with the actual size. ++ * ++ * If any clusters or subclusters were allocated then @m contains a ++ * list with the information of all the affected regions. Note that ++ * this can happen regardless of whether this function succeeds or ++ * not. The caller is responsible for updating the L2 metadata of the ++ * allocated clusters (on success) or freeing them (on failure), and ++ * for clearing the contents of @m afterwards in both cases. ++ * ++ * If the request conflicts with another write request in flight, the coroutine ++ * is queued and will be reentered when the dependency has completed. ++ * ++ * Return 0 on success and -errno in error cases ++ */ ++int coroutine_fn qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, ++ unsigned int *bytes, ++ uint64_t *host_offset, ++ QCowL2Meta **m) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t start, remaining; ++ uint64_t cluster_offset; ++ uint64_t cur_bytes; ++ int ret; ++ ++ trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *bytes); ++ ++again: ++ start = offset; ++ remaining = *bytes; ++ cluster_offset = INV_OFFSET; ++ *host_offset = INV_OFFSET; ++ cur_bytes = 0; ++ *m = NULL; ++ ++ while (true) { ++ ++ if (*host_offset == INV_OFFSET && cluster_offset != INV_OFFSET) { ++ *host_offset = cluster_offset; ++ } ++ ++ assert(remaining >= cur_bytes); ++ ++ start += cur_bytes; ++ remaining -= cur_bytes; ++ ++ if (cluster_offset != INV_OFFSET) { ++ cluster_offset += cur_bytes; ++ } ++ ++ if (remaining == 0) { ++ break; ++ } ++ ++ cur_bytes = remaining; ++ ++ /* ++ * Now start gathering as many contiguous clusters as possible: ++ * ++ * 1. Check for overlaps with in-flight allocations ++ * ++ * a) Overlap not in the first cluster -> shorten this request and ++ * let the caller handle the rest in its next loop iteration. ++ * ++ * b) Real overlaps of two requests. Yield and restart the search ++ * for contiguous clusters (the situation could have changed ++ * while we were sleeping) ++ * ++ * c) TODO: Request starts in the same cluster as the in-flight ++ * allocation ends. Shorten the COW of the in-fight allocation, ++ * set cluster_offset to write to the same cluster and set up ++ * the right synchronisation between the in-flight request and ++ * the new one. ++ */ ++ ret = handle_dependencies(bs, start, &cur_bytes, m); ++ if (ret == -EAGAIN) { ++ /* Currently handle_dependencies() doesn't yield if we already had ++ * an allocation. If it did, we would have to clean up the L2Meta ++ * structs before starting over. */ ++ assert(*m == NULL); ++ goto again; ++ } else if (ret < 0) { ++ return ret; ++ } else if (cur_bytes == 0) { ++ break; ++ } else { ++ /* handle_dependencies() may have decreased cur_bytes (shortened ++ * the allocations below) so that the next dependency is processed ++ * correctly during the next loop iteration. */ ++ } ++ ++ /* ++ * 2. Count contiguous COPIED clusters. ++ */ ++ ret = handle_copied(bs, start, &cluster_offset, &cur_bytes, m); ++ if (ret < 0) { ++ return ret; ++ } else if (ret) { ++ continue; ++ } else if (cur_bytes == 0) { ++ break; ++ } ++ ++ /* ++ * 3. If the request still hasn't completed, allocate new clusters, ++ * considering any cluster_offset of steps 1c or 2. ++ */ ++ ret = handle_alloc(bs, start, &cluster_offset, &cur_bytes, m); ++ if (ret < 0) { ++ return ret; ++ } else if (ret) { ++ continue; ++ } else { ++ assert(cur_bytes == 0); ++ break; ++ } ++ } ++ ++ *bytes -= remaining; ++ assert(*bytes > 0); ++ assert(*host_offset != INV_OFFSET); ++ assert(offset_into_cluster(s, *host_offset) == ++ offset_into_cluster(s, offset)); ++ ++ return 0; ++} ++ ++/* ++ * This discards as many clusters of nb_clusters as possible at once (i.e. ++ * all clusters in the same L2 slice) and returns the number of discarded ++ * clusters. ++ */ ++static int GRAPH_RDLOCK ++discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, uint64_t nb_clusters, ++ enum qcow2_discard_type type, bool full_discard) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l2_slice; ++ int l2_index; ++ int ret; ++ int i; ++ ++ ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Limit nb_clusters to one L2 slice */ ++ nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); ++ assert(nb_clusters <= INT_MAX); ++ ++ for (i = 0; i < nb_clusters; i++) { ++ uint64_t old_l2_entry = get_l2_entry(s, l2_slice, l2_index + i); ++ uint64_t old_l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); ++ uint64_t new_l2_entry = old_l2_entry; ++ uint64_t new_l2_bitmap = old_l2_bitmap; ++ QCow2ClusterType cluster_type = ++ qcow2_get_cluster_type(bs, old_l2_entry); ++ bool keep_reference = (cluster_type != QCOW2_CLUSTER_COMPRESSED) && ++ !full_discard && ++ (s->discard_no_unref && ++ type == QCOW2_DISCARD_REQUEST); ++ ++ /* ++ * If full_discard is true, the cluster should not read back as zeroes, ++ * but rather fall through to the backing file. ++ * ++ * If full_discard is false, make sure that a discarded area reads back ++ * as zeroes for v3 images (we cannot do it for v2 without actually ++ * writing a zero-filled buffer). We can skip the operation if the ++ * cluster is already marked as zero, or if it's unallocated and we ++ * don't have a backing file. ++ * ++ * TODO We might want to use bdrv_block_status(bs) here, but we're ++ * holding s->lock, so that doesn't work today. ++ */ ++ if (full_discard) { ++ new_l2_entry = new_l2_bitmap = 0; ++ } else if (bs->backing || qcow2_cluster_is_allocated(cluster_type)) { ++ if (has_subclusters(s)) { ++ if (keep_reference) { ++ new_l2_entry = old_l2_entry; ++ } else { ++ new_l2_entry = 0; ++ } ++ new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; ++ } else { ++ if (s->qcow_version >= 3) { ++ if (keep_reference) { ++ new_l2_entry |= QCOW_OFLAG_ZERO; ++ } else { ++ new_l2_entry = QCOW_OFLAG_ZERO; ++ } ++ } else { ++ new_l2_entry = 0; ++ } ++ } ++ } ++ ++ if (old_l2_entry == new_l2_entry && old_l2_bitmap == new_l2_bitmap) { ++ continue; ++ } ++ ++ /* First remove L2 entries */ ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ set_l2_entry(s, l2_slice, l2_index + i, new_l2_entry); ++ if (has_subclusters(s)) { ++ set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); ++ } ++ if (!keep_reference) { ++ /* Then decrease the refcount */ ++ qcow2_free_any_cluster(bs, old_l2_entry, type); ++ } else if (s->discard_passthrough[type] && ++ (cluster_type == QCOW2_CLUSTER_NORMAL || ++ cluster_type == QCOW2_CLUSTER_ZERO_ALLOC)) { ++ /* If we keep the reference, pass on the discard still */ ++ bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, ++ s->cluster_size); ++ } ++ } ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ return nb_clusters; ++} ++ ++int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, ++ uint64_t bytes, enum qcow2_discard_type type, ++ bool full_discard) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t end_offset = offset + bytes; ++ uint64_t nb_clusters; ++ int64_t cleared; ++ int ret; ++ ++ /* Caller must pass aligned values, except at image end */ ++ assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); ++ assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) || ++ end_offset == bs->total_sectors << BDRV_SECTOR_BITS); ++ ++ nb_clusters = size_to_clusters(s, bytes); ++ ++ s->cache_discards = true; ++ ++ /* Each L2 slice is handled by its own loop iteration */ ++ while (nb_clusters > 0) { ++ cleared = discard_in_l2_slice(bs, offset, nb_clusters, type, ++ full_discard); ++ if (cleared < 0) { ++ ret = cleared; ++ goto fail; ++ } ++ ++ nb_clusters -= cleared; ++ offset += (cleared * s->cluster_size); ++ } ++ ++ ret = 0; ++fail: ++ s->cache_discards = false; ++ qcow2_process_discards(bs, ret); ++ ++ return ret; ++} ++ ++/* ++ * This zeroes as many clusters of nb_clusters as possible at once (i.e. ++ * all clusters in the same L2 slice) and returns the number of zeroed ++ * clusters. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, ++ uint64_t nb_clusters, int flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l2_slice; ++ int l2_index; ++ int ret; ++ int i; ++ ++ ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Limit nb_clusters to one L2 slice */ ++ nb_clusters = MIN(nb_clusters, s->l2_slice_size - l2_index); ++ assert(nb_clusters <= INT_MAX); ++ ++ for (i = 0; i < nb_clusters; i++) { ++ uint64_t old_l2_entry = get_l2_entry(s, l2_slice, l2_index + i); ++ uint64_t old_l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i); ++ QCow2ClusterType type = qcow2_get_cluster_type(bs, old_l2_entry); ++ bool unmap = (type == QCOW2_CLUSTER_COMPRESSED) || ++ ((flags & BDRV_REQ_MAY_UNMAP) && qcow2_cluster_is_allocated(type)); ++ bool keep_reference = ++ (s->discard_no_unref && type != QCOW2_CLUSTER_COMPRESSED); ++ uint64_t new_l2_entry = old_l2_entry; ++ uint64_t new_l2_bitmap = old_l2_bitmap; ++ ++ if (unmap && !keep_reference) { ++ new_l2_entry = 0; ++ } ++ ++ if (has_subclusters(s)) { ++ new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES; ++ } else { ++ new_l2_entry |= QCOW_OFLAG_ZERO; ++ } ++ ++ if (old_l2_entry == new_l2_entry && old_l2_bitmap == new_l2_bitmap) { ++ continue; ++ } ++ ++ /* First update L2 entries */ ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ set_l2_entry(s, l2_slice, l2_index + i, new_l2_entry); ++ if (has_subclusters(s)) { ++ set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap); ++ } ++ ++ if (unmap) { ++ if (!keep_reference) { ++ /* Then decrease the refcount */ ++ qcow2_free_any_cluster(bs, old_l2_entry, QCOW2_DISCARD_REQUEST); ++ } else if (s->discard_passthrough[QCOW2_DISCARD_REQUEST] && ++ (type == QCOW2_CLUSTER_NORMAL || ++ type == QCOW2_CLUSTER_ZERO_ALLOC)) { ++ /* If we keep the reference, pass on the discard still */ ++ bdrv_pdiscard(s->data_file, old_l2_entry & L2E_OFFSET_MASK, ++ s->cluster_size); ++ } ++ } ++ } ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ return nb_clusters; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++zero_l2_subclusters(BlockDriverState *bs, uint64_t offset, ++ unsigned nb_subclusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l2_slice; ++ uint64_t old_l2_bitmap, l2_bitmap; ++ int l2_index, ret, sc = offset_to_sc_index(s, offset); ++ ++ /* For full clusters use zero_in_l2_slice() instead */ ++ assert(nb_subclusters > 0 && nb_subclusters < s->subclusters_per_cluster); ++ assert(sc + nb_subclusters <= s->subclusters_per_cluster); ++ assert(offset_into_subcluster(s, offset) == 0); ++ ++ ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ switch (qcow2_get_cluster_type(bs, get_l2_entry(s, l2_slice, l2_index))) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ ret = -ENOTSUP; /* We cannot partially zeroize compressed clusters */ ++ goto out; ++ case QCOW2_CLUSTER_NORMAL: ++ case QCOW2_CLUSTER_UNALLOCATED: ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ old_l2_bitmap = l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index); ++ ++ l2_bitmap |= QCOW_OFLAG_SUB_ZERO_RANGE(sc, sc + nb_subclusters); ++ l2_bitmap &= ~QCOW_OFLAG_SUB_ALLOC_RANGE(sc, sc + nb_subclusters); ++ ++ if (old_l2_bitmap != l2_bitmap) { ++ set_l2_bitmap(s, l2_slice, l2_index, l2_bitmap); ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ } ++ ++ ret = 0; ++out: ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ ++ return ret; ++} ++ ++int coroutine_fn qcow2_subcluster_zeroize(BlockDriverState *bs, uint64_t offset, ++ uint64_t bytes, int flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t end_offset = offset + bytes; ++ uint64_t nb_clusters; ++ unsigned head, tail; ++ int64_t cleared; ++ int ret; ++ ++ /* If we have to stay in sync with an external data file, zero out ++ * s->data_file first. */ ++ if (data_file_is_raw(bs)) { ++ assert(has_data_file(bs)); ++ ret = bdrv_co_pwrite_zeroes(s->data_file, offset, bytes, flags); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* Caller must pass aligned values, except at image end */ ++ assert(offset_into_subcluster(s, offset) == 0); ++ assert(offset_into_subcluster(s, end_offset) == 0 || ++ end_offset >= bs->total_sectors << BDRV_SECTOR_BITS); ++ ++ /* ++ * The zero flag is only supported by version 3 and newer. However, if we ++ * have no backing file, we can resort to discard in version 2. ++ */ ++ if (s->qcow_version < 3) { ++ if (!bs->backing) { ++ return qcow2_cluster_discard(bs, offset, bytes, ++ QCOW2_DISCARD_REQUEST, false); ++ } ++ return -ENOTSUP; ++ } ++ ++ head = MIN(end_offset, ROUND_UP(offset, s->cluster_size)) - offset; ++ offset += head; ++ ++ tail = (end_offset >= bs->total_sectors << BDRV_SECTOR_BITS) ? 0 : ++ end_offset - MAX(offset, start_of_cluster(s, end_offset)); ++ end_offset -= tail; ++ ++ s->cache_discards = true; ++ ++ if (head) { ++ ret = zero_l2_subclusters(bs, offset - head, ++ size_to_subclusters(s, head)); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* Each L2 slice is handled by its own loop iteration */ ++ nb_clusters = size_to_clusters(s, end_offset - offset); ++ ++ while (nb_clusters > 0) { ++ cleared = zero_in_l2_slice(bs, offset, nb_clusters, flags); ++ if (cleared < 0) { ++ ret = cleared; ++ goto fail; ++ } ++ ++ nb_clusters -= cleared; ++ offset += (cleared * s->cluster_size); ++ } ++ ++ if (tail) { ++ ret = zero_l2_subclusters(bs, end_offset, size_to_subclusters(s, tail)); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ ret = 0; ++fail: ++ s->cache_discards = false; ++ qcow2_process_discards(bs, ret); ++ ++ return ret; ++} ++ ++/* ++ * Expands all zero clusters in a specific L1 table (or deallocates them, for ++ * non-backed non-pre-allocated zero clusters). ++ * ++ * l1_entries and *visited_l1_entries are used to keep track of progress for ++ * status_cb(). l1_entries contains the total number of L1 entries and ++ * *visited_l1_entries counts all visited L1 entries. ++ */ ++static int GRAPH_RDLOCK ++expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, ++ int l1_size, int64_t *visited_l1_entries, ++ int64_t l1_entries, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ bool is_active_l1 = (l1_table == s->l1_table); ++ uint64_t *l2_slice = NULL; ++ unsigned slice, slice_size2, n_slices; ++ int ret; ++ int i, j; ++ ++ /* qcow2_downgrade() is not allowed in images with subclusters */ ++ assert(!has_subclusters(s)); ++ ++ slice_size2 = s->l2_slice_size * l2_entry_size(s); ++ n_slices = s->cluster_size / slice_size2; ++ ++ if (!is_active_l1) { ++ /* inactive L2 tables require a buffer to be stored in when loading ++ * them from disk */ ++ l2_slice = qemu_try_blockalign(bs->file->bs, slice_size2); ++ if (l2_slice == NULL) { ++ return -ENOMEM; ++ } ++ } ++ ++ for (i = 0; i < l1_size; i++) { ++ uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK; ++ uint64_t l2_refcount; ++ ++ if (!l2_offset) { ++ /* unallocated */ ++ (*visited_l1_entries)++; ++ if (status_cb) { ++ status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque); ++ } ++ continue; ++ } ++ ++ if (offset_into_cluster(s, l2_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" ++ PRIx64 " unaligned (L1 index: %#x)", ++ l2_offset, i); ++ ret = -EIO; ++ goto fail; ++ } ++ ++ ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, ++ &l2_refcount); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (slice = 0; slice < n_slices; slice++) { ++ uint64_t slice_offset = l2_offset + slice * slice_size2; ++ bool l2_dirty = false; ++ if (is_active_l1) { ++ /* get active L2 tables from cache */ ++ ret = qcow2_cache_get(bs, s->l2_table_cache, slice_offset, ++ (void **)&l2_slice); ++ } else { ++ /* load inactive L2 tables from disk */ ++ ret = bdrv_pread(bs->file, slice_offset, slice_size2, ++ l2_slice, 0); ++ } ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (j = 0; j < s->l2_slice_size; j++) { ++ uint64_t l2_entry = get_l2_entry(s, l2_slice, j); ++ int64_t offset = l2_entry & L2E_OFFSET_MASK; ++ QCow2ClusterType cluster_type = ++ qcow2_get_cluster_type(bs, l2_entry); ++ ++ if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN && ++ cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) { ++ continue; ++ } ++ ++ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { ++ if (!bs->backing) { ++ /* ++ * not backed; therefore we can simply deallocate the ++ * cluster. No need to call set_l2_bitmap(), this ++ * function doesn't support images with subclusters. ++ */ ++ set_l2_entry(s, l2_slice, j, 0); ++ l2_dirty = true; ++ continue; ++ } ++ ++ offset = qcow2_alloc_clusters(bs, s->cluster_size); ++ if (offset < 0) { ++ ret = offset; ++ goto fail; ++ } ++ ++ /* The offset must fit in the offset field */ ++ assert((offset & L2E_OFFSET_MASK) == offset); ++ ++ if (l2_refcount > 1) { ++ /* For shared L2 tables, set the refcount accordingly ++ * (it is already 1 and needs to be l2_refcount) */ ++ ret = qcow2_update_cluster_refcount( ++ bs, offset >> s->cluster_bits, ++ refcount_diff(1, l2_refcount), false, ++ QCOW2_DISCARD_OTHER); ++ if (ret < 0) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_OTHER); ++ goto fail; ++ } ++ } ++ } ++ ++ if (offset_into_cluster(s, offset)) { ++ int l2_index = slice * s->l2_slice_size + j; ++ qcow2_signal_corruption( ++ bs, true, -1, -1, ++ "Cluster allocation offset " ++ "%#" PRIx64 " unaligned (L2 offset: %#" ++ PRIx64 ", L2 index: %#x)", offset, ++ l2_offset, l2_index); ++ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_ALWAYS); ++ } ++ ret = -EIO; ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, offset, ++ s->cluster_size, true); ++ if (ret < 0) { ++ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_ALWAYS); ++ } ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite_zeroes(s->data_file, offset, ++ s->cluster_size, 0); ++ if (ret < 0) { ++ if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_ALWAYS); ++ } ++ goto fail; ++ } ++ ++ if (l2_refcount == 1) { ++ set_l2_entry(s, l2_slice, j, offset | QCOW_OFLAG_COPIED); ++ } else { ++ set_l2_entry(s, l2_slice, j, offset); ++ } ++ /* ++ * No need to call set_l2_bitmap() after set_l2_entry() because ++ * this function doesn't support images with subclusters. ++ */ ++ l2_dirty = true; ++ } ++ ++ if (is_active_l1) { ++ if (l2_dirty) { ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); ++ qcow2_cache_depends_on_flush(s->l2_table_cache); ++ } ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } else { ++ if (l2_dirty) { ++ ret = qcow2_pre_write_overlap_check( ++ bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, ++ slice_offset, slice_size2, false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite(bs->file, slice_offset, slice_size2, ++ l2_slice, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ } ++ } ++ ++ (*visited_l1_entries)++; ++ if (status_cb) { ++ status_cb(bs, *visited_l1_entries, l1_entries, cb_opaque); ++ } ++ } ++ ++ ret = 0; ++ ++fail: ++ if (l2_slice) { ++ if (!is_active_l1) { ++ qemu_vfree(l2_slice); ++ } else { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ } ++ return ret; ++} ++ ++/* ++ * For backed images, expands all zero clusters on the image. For non-backed ++ * images, deallocates all non-pre-allocated zero clusters (and claims the ++ * allocation for pre-allocated ones). This is important for downgrading to a ++ * qcow2 version which doesn't yet support metadata zero clusters. ++ */ ++int qcow2_expand_zero_clusters(BlockDriverState *bs, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l1_table = NULL; ++ int64_t l1_entries = 0, visited_l1_entries = 0; ++ int ret; ++ int i, j; ++ ++ if (status_cb) { ++ l1_entries = s->l1_size; ++ for (i = 0; i < s->nb_snapshots; i++) { ++ l1_entries += s->snapshots[i].l1_size; ++ } ++ } ++ ++ ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size, ++ &visited_l1_entries, l1_entries, ++ status_cb, cb_opaque); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Inactive L1 tables may point to active L2 tables - therefore it is ++ * necessary to flush the L2 table cache before trying to access the L2 ++ * tables pointed to by inactive L1 entries (else we might try to expand ++ * zero clusters that have already been expanded); furthermore, it is also ++ * necessary to empty the L2 table cache, since it may contain tables which ++ * are now going to be modified directly on disk, bypassing the cache. ++ * qcow2_cache_empty() does both for us. */ ++ ret = qcow2_cache_empty(bs, s->l2_table_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (i = 0; i < s->nb_snapshots; i++) { ++ int l1_size2; ++ uint64_t *new_l1_table; ++ Error *local_err = NULL; ++ ++ ret = qcow2_validate_table(bs, s->snapshots[i].l1_table_offset, ++ s->snapshots[i].l1_size, L1E_SIZE, ++ QCOW_MAX_L1_SIZE, "Snapshot L1 table", ++ &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ ++ l1_size2 = s->snapshots[i].l1_size * L1E_SIZE; ++ new_l1_table = g_try_realloc(l1_table, l1_size2); ++ ++ if (!new_l1_table) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ l1_table = new_l1_table; ++ ++ ret = bdrv_pread(bs->file, s->snapshots[i].l1_table_offset, l1_size2, ++ l1_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (j = 0; j < s->snapshots[i].l1_size; j++) { ++ be64_to_cpus(&l1_table[j]); ++ } ++ ++ ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size, ++ &visited_l1_entries, l1_entries, ++ status_cb, cb_opaque); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ ret = 0; ++ ++fail: ++ g_free(l1_table); ++ return ret; ++} ++ ++void qcow2_parse_compressed_l2_entry(BlockDriverState *bs, uint64_t l2_entry, ++ uint64_t *coffset, int *csize) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int nb_csectors; ++ ++ assert(qcow2_get_cluster_type(bs, l2_entry) == QCOW2_CLUSTER_COMPRESSED); ++ ++ *coffset = l2_entry & s->cluster_offset_mask; ++ ++ nb_csectors = ((l2_entry >> s->csize_shift) & s->csize_mask) + 1; ++ *csize = nb_csectors * QCOW2_COMPRESSED_SECTOR_SIZE - ++ (*coffset & (QCOW2_COMPRESSED_SECTOR_SIZE - 1)); ++} +diff --git a/qcow2/lib/qcow2-refcount.c b/qcow2/lib/qcow2-refcount.c +new file mode 100644 +index 00000000..0266542c +--- /dev/null ++++ b/qcow2/lib/qcow2-refcount.c +@@ -0,0 +1,3750 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block-io.h" ++#include "qapi/error.h" ++#include "qcow2.h" ++#include "qemu/range.h" ++#include "qemu/bswap.h" ++#include "qemu/cutils.h" ++#include "qemu/memalign.h" ++#include "trace.h" ++ ++static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, ++ uint64_t max); ++ ++G_GNUC_WARN_UNUSED_RESULT ++static int update_refcount(BlockDriverState *bs, ++ int64_t offset, int64_t length, uint64_t addend, ++ bool decrease, enum qcow2_discard_type type); ++ ++static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index); ++static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index); ++ ++static void set_refcount_ro0(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro1(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro2(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro3(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro4(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro5(void *refcount_array, uint64_t index, ++ uint64_t value); ++static void set_refcount_ro6(void *refcount_array, uint64_t index, ++ uint64_t value); ++ ++ ++static Qcow2GetRefcountFunc *const get_refcount_funcs[] = { ++ &get_refcount_ro0, ++ &get_refcount_ro1, ++ &get_refcount_ro2, ++ &get_refcount_ro3, ++ &get_refcount_ro4, ++ &get_refcount_ro5, ++ &get_refcount_ro6 ++}; ++ ++static Qcow2SetRefcountFunc *const set_refcount_funcs[] = { ++ &set_refcount_ro0, ++ &set_refcount_ro1, ++ &set_refcount_ro2, ++ &set_refcount_ro3, ++ &set_refcount_ro4, ++ &set_refcount_ro5, ++ &set_refcount_ro6 ++}; ++ ++ ++/*********************************************************/ ++/* refcount handling */ ++ ++static void update_max_refcount_table_index(BDRVQcow2State *s) ++{ ++ unsigned i = s->refcount_table_size - 1; ++ while (i > 0 && (s->refcount_table[i] & REFT_OFFSET_MASK) == 0) { ++ i--; ++ } ++ /* Set s->max_refcount_table_index to the index of the last used entry */ ++ s->max_refcount_table_index = i; ++} ++ ++int coroutine_fn qcow2_refcount_init(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int refcount_table_size2, i; ++ int ret; ++ ++ assert(s->refcount_order >= 0 && s->refcount_order <= 6); ++ ++ s->get_refcount = get_refcount_funcs[s->refcount_order]; ++ s->set_refcount = set_refcount_funcs[s->refcount_order]; ++ ++ assert(s->refcount_table_size <= INT_MAX / REFTABLE_ENTRY_SIZE); ++ refcount_table_size2 = s->refcount_table_size * REFTABLE_ENTRY_SIZE; ++ s->refcount_table = g_try_malloc(refcount_table_size2); ++ ++ if (s->refcount_table_size > 0) { ++ if (s->refcount_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); ++ ret = bdrv_co_pread(bs->file, s->refcount_table_offset, ++ refcount_table_size2, s->refcount_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ for(i = 0; i < s->refcount_table_size; i++) ++ be64_to_cpus(&s->refcount_table[i]); ++ update_max_refcount_table_index(s); ++ } ++ return 0; ++ fail: ++ return ret; ++} ++ ++void qcow2_refcount_close(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ g_free(s->refcount_table); ++} ++ ++ ++static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index) ++{ ++ return (((const uint8_t *)refcount_array)[index / 8] >> (index % 8)) & 0x1; ++} ++ ++static void set_refcount_ro0(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 1)); ++ ((uint8_t *)refcount_array)[index / 8] &= ~(0x1 << (index % 8)); ++ ((uint8_t *)refcount_array)[index / 8] |= value << (index % 8); ++} ++ ++static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index) ++{ ++ return (((const uint8_t *)refcount_array)[index / 4] >> (2 * (index % 4))) ++ & 0x3; ++} ++ ++static void set_refcount_ro1(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 2)); ++ ((uint8_t *)refcount_array)[index / 4] &= ~(0x3 << (2 * (index % 4))); ++ ((uint8_t *)refcount_array)[index / 4] |= value << (2 * (index % 4)); ++} ++ ++static uint64_t get_refcount_ro2(const void *refcount_array, uint64_t index) ++{ ++ return (((const uint8_t *)refcount_array)[index / 2] >> (4 * (index % 2))) ++ & 0xf; ++} ++ ++static void set_refcount_ro2(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 4)); ++ ((uint8_t *)refcount_array)[index / 2] &= ~(0xf << (4 * (index % 2))); ++ ((uint8_t *)refcount_array)[index / 2] |= value << (4 * (index % 2)); ++} ++ ++static uint64_t get_refcount_ro3(const void *refcount_array, uint64_t index) ++{ ++ return ((const uint8_t *)refcount_array)[index]; ++} ++ ++static void set_refcount_ro3(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 8)); ++ ((uint8_t *)refcount_array)[index] = value; ++} ++ ++static uint64_t get_refcount_ro4(const void *refcount_array, uint64_t index) ++{ ++ return be16_to_cpu(((const uint16_t *)refcount_array)[index]); ++} ++ ++static void set_refcount_ro4(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 16)); ++ ((uint16_t *)refcount_array)[index] = cpu_to_be16(value); ++} ++ ++static uint64_t get_refcount_ro5(const void *refcount_array, uint64_t index) ++{ ++ return be32_to_cpu(((const uint32_t *)refcount_array)[index]); ++} ++ ++static void set_refcount_ro5(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ assert(!(value >> 32)); ++ ((uint32_t *)refcount_array)[index] = cpu_to_be32(value); ++} ++ ++static uint64_t get_refcount_ro6(const void *refcount_array, uint64_t index) ++{ ++ return be64_to_cpu(((const uint64_t *)refcount_array)[index]); ++} ++ ++static void set_refcount_ro6(void *refcount_array, uint64_t index, ++ uint64_t value) ++{ ++ ((uint64_t *)refcount_array)[index] = cpu_to_be64(value); ++} ++ ++ ++static int GRAPH_RDLOCK ++load_refcount_block(BlockDriverState *bs, int64_t refcount_block_offset, ++ void **refcount_block) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); ++ return qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, ++ refcount_block); ++} ++ ++/* ++ * Retrieves the refcount of the cluster given by its index and stores it in ++ * *refcount. Returns 0 on success and -errno on failure. ++ */ ++int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index, ++ uint64_t *refcount) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t refcount_table_index, block_index; ++ int64_t refcount_block_offset; ++ int ret; ++ void *refcount_block; ++ ++ refcount_table_index = cluster_index >> s->refcount_block_bits; ++ if (refcount_table_index >= s->refcount_table_size) { ++ *refcount = 0; ++ return 0; ++ } ++ refcount_block_offset = ++ s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; ++ if (!refcount_block_offset) { ++ *refcount = 0; ++ return 0; ++ } ++ ++ if (offset_into_cluster(s, refcount_block_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" PRIx64 ++ " unaligned (reftable index: %#" PRIx64 ")", ++ refcount_block_offset, refcount_table_index); ++ return -EIO; ++ } ++ ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, ++ &refcount_block); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ block_index = cluster_index & (s->refcount_block_size - 1); ++ *refcount = s->get_refcount(refcount_block, block_index); ++ ++ qcow2_cache_put(s->refcount_block_cache, &refcount_block); ++ ++ return 0; ++} ++ ++/* Checks if two offsets are described by the same refcount block */ ++static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a, ++ uint64_t offset_b) ++{ ++ uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits); ++ uint64_t block_b = offset_b >> (s->cluster_bits + s->refcount_block_bits); ++ ++ return (block_a == block_b); ++} ++ ++/* ++ * Loads a refcount block. If it doesn't exist yet, it is allocated first ++ * (including growing the refcount table if needed). ++ * ++ * Returns 0 on success or -errno in error case ++ */ ++static int GRAPH_RDLOCK ++alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index, ++ void **refcount_block) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int refcount_table_index; ++ int64_t ret; ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC); ++ ++ /* Find the refcount block for the given cluster */ ++ refcount_table_index = cluster_index >> s->refcount_block_bits; ++ ++ if (refcount_table_index < s->refcount_table_size) { ++ ++ uint64_t refcount_block_offset = ++ s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK; ++ ++ /* If it's already there, we're done */ ++ if (refcount_block_offset) { ++ if (offset_into_cluster(s, refcount_block_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" ++ PRIx64 " unaligned (reftable index: " ++ "%#x)", refcount_block_offset, ++ refcount_table_index); ++ return -EIO; ++ } ++ ++ return load_refcount_block(bs, refcount_block_offset, ++ refcount_block); ++ } ++ } ++ ++ /* ++ * If we came here, we need to allocate something. Something is at least ++ * a cluster for the new refcount block. It may also include a new refcount ++ * table if the old refcount table is too small. ++ * ++ * Note that allocating clusters here needs some special care: ++ * ++ * - We can't use the normal qcow2_alloc_clusters(), it would try to ++ * increase the refcount and very likely we would end up with an endless ++ * recursion. Instead we must place the refcount blocks in a way that ++ * they can describe them themselves. ++ * ++ * - We need to consider that at this point we are inside update_refcounts ++ * and potentially doing an initial refcount increase. This means that ++ * some clusters have already been allocated by the caller, but their ++ * refcount isn't accurate yet. If we allocate clusters for metadata, we ++ * need to return -EAGAIN to signal the caller that it needs to restart ++ * the search for free clusters. ++ * ++ * - alloc_clusters_noref and qcow2_free_clusters may load a different ++ * refcount block into the cache ++ */ ++ ++ *refcount_block = NULL; ++ ++ /* We write to the refcount table, so we might depend on L2 tables */ ++ ret = qcow2_cache_flush(bs, s->l2_table_cache); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Allocate the refcount block itself and mark it as used */ ++ int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX); ++ if (new_block < 0) { ++ return new_block; ++ } ++ ++ /* The offset must fit in the offset field of the refcount table entry */ ++ assert((new_block & REFT_OFFSET_MASK) == new_block); ++ ++ /* If we're allocating the block at offset 0 then something is wrong */ ++ if (new_block == 0) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " ++ "allocation of refcount block at offset 0"); ++ return -EIO; ++ } ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64 ++ " at %" PRIx64 "\n", ++ refcount_table_index, cluster_index << s->cluster_bits, new_block); ++#endif ++ ++ if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { ++ /* Zero the new refcount block before updating it */ ++ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, ++ refcount_block); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ memset(*refcount_block, 0, s->cluster_size); ++ ++ /* The block describes itself, need to update the cache */ ++ int block_index = (new_block >> s->cluster_bits) & ++ (s->refcount_block_size - 1); ++ s->set_refcount(*refcount_block, block_index, 1); ++ } else { ++ /* Described somewhere else. This can recurse at most twice before we ++ * arrive at a block that describes itself. */ ++ ret = update_refcount(bs, new_block, s->cluster_size, 1, false, ++ QCOW2_DISCARD_NEVER); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Initialize the new refcount block only after updating its refcount, ++ * update_refcount uses the refcount cache itself */ ++ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, ++ refcount_block); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ memset(*refcount_block, 0, s->cluster_size); ++ } ++ ++ /* Now the new refcount block needs to be written to disk */ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* If the refcount table is big enough, just hook the block up there */ ++ if (refcount_table_index < s->refcount_table_size) { ++ uint64_t data64 = cpu_to_be64(new_block); ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); ++ ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset + ++ refcount_table_index * REFTABLE_ENTRY_SIZE, ++ sizeof(data64), &data64, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->refcount_table[refcount_table_index] = new_block; ++ /* If there's a hole in s->refcount_table then it can happen ++ * that refcount_table_index < s->max_refcount_table_index */ ++ s->max_refcount_table_index = ++ MAX(s->max_refcount_table_index, refcount_table_index); ++ ++ /* The new refcount block may be where the caller intended to put its ++ * data, so let it restart the search. */ ++ return -EAGAIN; ++ } ++ ++ qcow2_cache_put(s->refcount_block_cache, refcount_block); ++ ++ /* ++ * If we come here, we need to grow the refcount table. Again, a new ++ * refcount table needs some space and we can't simply allocate to avoid ++ * endless recursion. ++ * ++ * Therefore let's grab new refcount blocks at the end of the image, which ++ * will describe themselves and the new refcount table. This way we can ++ * reference them only in the new table and do the switch to the new ++ * refcount table at once without producing an inconsistent state in ++ * between. ++ */ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW); ++ ++ /* Calculate the number of refcount blocks needed so far; this will be the ++ * basis for calculating the index of the first cluster used for the ++ * self-describing refcount structures which we are about to create. ++ * ++ * Because we reached this point, there cannot be any refcount entries for ++ * cluster_index or higher indices yet. However, because new_block has been ++ * allocated to describe that cluster (and it will assume this role later ++ * on), we cannot use that index; also, new_block may actually have a higher ++ * cluster index than cluster_index, so it needs to be taken into account ++ * here (and 1 needs to be added to its value because that cluster is used). ++ */ ++ uint64_t blocks_used = DIV_ROUND_UP(MAX(cluster_index + 1, ++ (new_block >> s->cluster_bits) + 1), ++ s->refcount_block_size); ++ ++ /* Create the new refcount table and blocks */ ++ uint64_t meta_offset = (blocks_used * s->refcount_block_size) * ++ s->cluster_size; ++ ++ ret = qcow2_refcount_area(bs, meta_offset, 0, false, ++ refcount_table_index, new_block); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ ret = load_refcount_block(bs, new_block, refcount_block); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* If we were trying to do the initial refcount update for some cluster ++ * allocation, we might have used the same clusters to store newly ++ * allocated metadata. Make the caller search some new space. */ ++ return -EAGAIN; ++ ++fail: ++ if (*refcount_block != NULL) { ++ qcow2_cache_put(s->refcount_block_cache, refcount_block); ++ } ++ return ret; ++} ++ ++/* ++ * Starting at @start_offset, this function creates new self-covering refcount ++ * structures: A new refcount table and refcount blocks which cover all of ++ * themselves, and a number of @additional_clusters beyond their end. ++ * @start_offset must be at the end of the image file, that is, there must be ++ * only empty space beyond it. ++ * If @exact_size is false, the refcount table will have 50 % more entries than ++ * necessary so it will not need to grow again soon. ++ * If @new_refblock_offset is not zero, it contains the offset of a refcount ++ * block that should be entered into the new refcount table at index ++ * @new_refblock_index. ++ * ++ * Returns: The offset after the new refcount structures (i.e. where the ++ * @additional_clusters may be placed) on success, -errno on error. ++ */ ++int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset, ++ uint64_t additional_clusters, bool exact_size, ++ int new_refblock_index, ++ uint64_t new_refblock_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t total_refblock_count_u64, additional_refblock_count; ++ int total_refblock_count, table_size, area_reftable_index, table_clusters; ++ int i; ++ uint64_t table_offset, block_offset, end_offset; ++ int ret; ++ uint64_t *new_table; ++ ++ assert(!(start_offset % s->cluster_size)); ++ ++ qcow2_refcount_metadata_size(start_offset / s->cluster_size + ++ additional_clusters, ++ s->cluster_size, s->refcount_order, ++ !exact_size, &total_refblock_count_u64); ++ if (total_refblock_count_u64 > QCOW_MAX_REFTABLE_SIZE) { ++ return -EFBIG; ++ } ++ total_refblock_count = total_refblock_count_u64; ++ ++ /* Index in the refcount table of the first refcount block to cover the area ++ * of refcount structures we are about to create; we know that ++ * @total_refblock_count can cover @start_offset, so this will definitely ++ * fit into an int. */ ++ area_reftable_index = (start_offset / s->cluster_size) / ++ s->refcount_block_size; ++ ++ if (exact_size) { ++ table_size = total_refblock_count; ++ } else { ++ table_size = total_refblock_count + ++ DIV_ROUND_UP(total_refblock_count, 2); ++ } ++ /* The qcow2 file can only store the reftable size in number of clusters */ ++ table_size = ROUND_UP(table_size, s->cluster_size / REFTABLE_ENTRY_SIZE); ++ table_clusters = (table_size * REFTABLE_ENTRY_SIZE) / s->cluster_size; ++ ++ if (table_size > QCOW_MAX_REFTABLE_SIZE) { ++ return -EFBIG; ++ } ++ ++ new_table = g_try_new0(uint64_t, table_size); ++ ++ assert(table_size > 0); ++ if (new_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ /* Fill the new refcount table */ ++ if (table_size > s->max_refcount_table_index) { ++ /* We're actually growing the reftable */ ++ memcpy(new_table, s->refcount_table, ++ (s->max_refcount_table_index + 1) * REFTABLE_ENTRY_SIZE); ++ } else { ++ /* Improbable case: We're shrinking the reftable. However, the caller ++ * has assured us that there is only empty space beyond @start_offset, ++ * so we can simply drop all of the refblocks that won't fit into the ++ * new reftable. */ ++ memcpy(new_table, s->refcount_table, table_size * REFTABLE_ENTRY_SIZE); ++ } ++ ++ if (new_refblock_offset) { ++ assert(new_refblock_index < total_refblock_count); ++ new_table[new_refblock_index] = new_refblock_offset; ++ } ++ ++ /* Count how many new refblocks we have to create */ ++ additional_refblock_count = 0; ++ for (i = area_reftable_index; i < total_refblock_count; i++) { ++ if (!new_table[i]) { ++ additional_refblock_count++; ++ } ++ } ++ ++ table_offset = start_offset + additional_refblock_count * s->cluster_size; ++ end_offset = table_offset + table_clusters * s->cluster_size; ++ ++ /* Fill the refcount blocks, and create new ones, if necessary */ ++ block_offset = start_offset; ++ for (i = area_reftable_index; i < total_refblock_count; i++) { ++ void *refblock_data; ++ uint64_t first_offset_covered; ++ ++ /* Reuse an existing refblock if possible, create a new one otherwise */ ++ if (new_table[i]) { ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, new_table[i], ++ &refblock_data); ++ if (ret < 0) { ++ goto fail; ++ } ++ } else { ++ ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, ++ block_offset, &refblock_data); ++ if (ret < 0) { ++ goto fail; ++ } ++ memset(refblock_data, 0, s->cluster_size); ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, ++ refblock_data); ++ ++ new_table[i] = block_offset; ++ block_offset += s->cluster_size; ++ } ++ ++ /* First host offset covered by this refblock */ ++ first_offset_covered = (uint64_t)i * s->refcount_block_size * ++ s->cluster_size; ++ if (first_offset_covered < end_offset) { ++ int j, end_index; ++ ++ /* Set the refcount of all of the new refcount structures to 1 */ ++ ++ if (first_offset_covered < start_offset) { ++ assert(i == area_reftable_index); ++ j = (start_offset - first_offset_covered) / s->cluster_size; ++ assert(j < s->refcount_block_size); ++ } else { ++ j = 0; ++ } ++ ++ end_index = MIN((end_offset - first_offset_covered) / ++ s->cluster_size, ++ s->refcount_block_size); ++ ++ for (; j < end_index; j++) { ++ /* The caller guaranteed us this space would be empty */ ++ assert(s->get_refcount(refblock_data, j) == 0); ++ s->set_refcount(refblock_data, j, 1); ++ } ++ ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, ++ refblock_data); ++ } ++ ++ qcow2_cache_put(s->refcount_block_cache, &refblock_data); ++ } ++ ++ assert(block_offset == table_offset); ++ ++ /* Write refcount blocks to disk */ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS); ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Write refcount table to disk */ ++ for (i = 0; i < total_refblock_count; i++) { ++ cpu_to_be64s(&new_table[i]); ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); ++ ret = bdrv_pwrite_sync(bs->file, table_offset, ++ table_size * REFTABLE_ENTRY_SIZE, new_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (i = 0; i < total_refblock_count; i++) { ++ be64_to_cpus(&new_table[i]); ++ } ++ ++ /* Hook up the new refcount table in the qcow2 header */ ++ struct QEMU_PACKED { ++ uint64_t d64; ++ uint32_t d32; ++ } data; ++ data.d64 = cpu_to_be64(table_offset); ++ data.d32 = cpu_to_be32(table_clusters); ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); ++ ret = bdrv_pwrite_sync(bs->file, ++ offsetof(QCowHeader, refcount_table_offset), ++ sizeof(data), &data, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* And switch it in memory */ ++ uint64_t old_table_offset = s->refcount_table_offset; ++ uint64_t old_table_size = s->refcount_table_size; ++ ++ g_free(s->refcount_table); ++ s->refcount_table = new_table; ++ s->refcount_table_size = table_size; ++ s->refcount_table_offset = table_offset; ++ update_max_refcount_table_index(s); ++ ++ /* Free old table. */ ++ qcow2_free_clusters(bs, old_table_offset, ++ old_table_size * REFTABLE_ENTRY_SIZE, ++ QCOW2_DISCARD_OTHER); ++ ++ return end_offset; ++ ++fail: ++ g_free(new_table); ++ return ret; ++} ++ ++void qcow2_process_discards(BlockDriverState *bs, int ret) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2DiscardRegion *d, *next; ++ ++ QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) { ++ QTAILQ_REMOVE(&s->discards, d, next); ++ ++ /* Discard is optional, ignore the return value */ ++ if (ret >= 0) { ++ int r2 = bdrv_pdiscard(bs->file, d->offset, d->bytes); ++ if (r2 < 0) { ++ trace_qcow2_process_discards_failed_region(d->offset, d->bytes, ++ r2); ++ } ++ } ++ ++ g_free(d); ++ } ++} ++ ++static void update_refcount_discard(BlockDriverState *bs, ++ uint64_t offset, uint64_t length) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2DiscardRegion *d, *p, *next; ++ ++ QTAILQ_FOREACH(d, &s->discards, next) { ++ uint64_t new_start = MIN(offset, d->offset); ++ uint64_t new_end = MAX(offset + length, d->offset + d->bytes); ++ ++ if (new_end - new_start <= length + d->bytes) { ++ /* There can't be any overlap, areas ending up here have no ++ * references any more and therefore shouldn't get freed another ++ * time. */ ++ assert(d->bytes + length == new_end - new_start); ++ d->offset = new_start; ++ d->bytes = new_end - new_start; ++ goto found; ++ } ++ } ++ ++ d = g_malloc(sizeof(*d)); ++ *d = (Qcow2DiscardRegion) { ++ .bs = bs, ++ .offset = offset, ++ .bytes = length, ++ }; ++ QTAILQ_INSERT_TAIL(&s->discards, d, next); ++ ++found: ++ /* Merge discard requests if they are adjacent now */ ++ QTAILQ_FOREACH_SAFE(p, &s->discards, next, next) { ++ if (p == d ++ || p->offset > d->offset + d->bytes ++ || d->offset > p->offset + p->bytes) ++ { ++ continue; ++ } ++ ++ /* Still no overlap possible */ ++ assert(p->offset == d->offset + d->bytes ++ || d->offset == p->offset + p->bytes); ++ ++ QTAILQ_REMOVE(&s->discards, p, next); ++ d->offset = MIN(d->offset, p->offset); ++ d->bytes += p->bytes; ++ g_free(p); ++ } ++} ++ ++/* XXX: cache several refcount block clusters ? */ ++/* @addend is the absolute value of the addend; if @decrease is set, @addend ++ * will be subtracted from the current refcount, otherwise it will be added */ ++static int GRAPH_RDLOCK ++update_refcount(BlockDriverState *bs, int64_t offset, int64_t length, ++ uint64_t addend, bool decrease, enum qcow2_discard_type type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t start, last, cluster_offset; ++ void *refcount_block = NULL; ++ int64_t old_table_index = -1; ++ int ret; ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "update_refcount: offset=%" PRId64 " size=%" PRId64 ++ " addend=%s%" PRIu64 "\n", offset, length, decrease ? "-" : "", ++ addend); ++#endif ++ if (length < 0) { ++ return -EINVAL; ++ } else if (length == 0) { ++ return 0; ++ } ++ ++ if (decrease) { ++ qcow2_cache_set_dependency(bs, s->refcount_block_cache, ++ s->l2_table_cache); ++ } ++ ++ start = start_of_cluster(s, offset); ++ last = start_of_cluster(s, offset + length - 1); ++ for(cluster_offset = start; cluster_offset <= last; ++ cluster_offset += s->cluster_size) ++ { ++ int block_index; ++ uint64_t refcount; ++ int64_t cluster_index = cluster_offset >> s->cluster_bits; ++ int64_t table_index = cluster_index >> s->refcount_block_bits; ++ ++ /* Load the refcount block and allocate it if needed */ ++ if (table_index != old_table_index) { ++ if (refcount_block) { ++ qcow2_cache_put(s->refcount_block_cache, &refcount_block); ++ } ++ ret = alloc_refcount_block(bs, cluster_index, &refcount_block); ++ /* If the caller needs to restart the search for free clusters, ++ * try the same ones first to see if they're still free. */ ++ if (ret == -EAGAIN) { ++ if (s->free_cluster_index > (start >> s->cluster_bits)) { ++ s->free_cluster_index = (start >> s->cluster_bits); ++ } ++ } ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ old_table_index = table_index; ++ ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); ++ ++ /* we can update the count and save it */ ++ block_index = cluster_index & (s->refcount_block_size - 1); ++ ++ refcount = s->get_refcount(refcount_block, block_index); ++ if (decrease ? (refcount - addend > refcount) ++ : (refcount + addend < refcount || ++ refcount + addend > s->refcount_max)) ++ { ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (decrease) { ++ refcount -= addend; ++ } else { ++ refcount += addend; ++ } ++ if (refcount == 0 && cluster_index < s->free_cluster_index) { ++ s->free_cluster_index = cluster_index; ++ } ++ s->set_refcount(refcount_block, block_index, refcount); ++ ++ if (refcount == 0) { ++ void *table; ++ ++ table = qcow2_cache_is_table_offset(s->refcount_block_cache, ++ offset); ++ if (table != NULL) { ++ qcow2_cache_put(s->refcount_block_cache, &refcount_block); ++ old_table_index = -1; ++ qcow2_cache_discard(s->refcount_block_cache, table); ++ } ++ ++ table = qcow2_cache_is_table_offset(s->l2_table_cache, offset); ++ if (table != NULL) { ++ qcow2_cache_discard(s->l2_table_cache, table); ++ } ++ ++ if (s->discard_passthrough[type]) { ++ update_refcount_discard(bs, cluster_offset, s->cluster_size); ++ } ++ } ++ } ++ ++ ret = 0; ++fail: ++ if (!s->cache_discards) { ++ qcow2_process_discards(bs, ret); ++ } ++ ++ /* Write last changed block to disk */ ++ if (refcount_block) { ++ qcow2_cache_put(s->refcount_block_cache, &refcount_block); ++ } ++ ++ /* ++ * Try do undo any updates if an error is returned (This may succeed in ++ * some cases like ENOSPC for allocating a new refcount block) ++ */ ++ if (ret < 0) { ++ int dummy; ++ dummy = update_refcount(bs, offset, cluster_offset - offset, addend, ++ !decrease, QCOW2_DISCARD_NEVER); ++ (void)dummy; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Increases or decreases the refcount of a given cluster. ++ * ++ * @addend is the absolute value of the addend; if @decrease is set, @addend ++ * will be subtracted from the current refcount, otherwise it will be added. ++ * ++ * On success 0 is returned; on failure -errno is returned. ++ */ ++int qcow2_update_cluster_refcount(BlockDriverState *bs, ++ int64_t cluster_index, ++ uint64_t addend, bool decrease, ++ enum qcow2_discard_type type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend, ++ decrease, type); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++ ++ ++/*********************************************************/ ++/* cluster allocation functions */ ++ ++ ++ ++/* return < 0 if error */ ++static int64_t GRAPH_RDLOCK ++alloc_clusters_noref(BlockDriverState *bs, uint64_t size, uint64_t max) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t i, nb_clusters, refcount; ++ int ret; ++ ++ /* We can't allocate clusters if they may still be queued for discard. */ ++ if (s->cache_discards) { ++ qcow2_process_discards(bs, 0); ++ } ++ ++ nb_clusters = size_to_clusters(s, size); ++retry: ++ for(i = 0; i < nb_clusters; i++) { ++ uint64_t next_cluster_index = s->free_cluster_index++; ++ ret = qcow2_get_refcount(bs, next_cluster_index, &refcount); ++ ++ if (ret < 0) { ++ return ret; ++ } else if (refcount != 0) { ++ goto retry; ++ } ++ } ++ ++ /* Make sure that all offsets in the "allocated" range are representable ++ * in the requested max */ ++ if (s->free_cluster_index > 0 && ++ s->free_cluster_index - 1 > (max >> s->cluster_bits)) ++ { ++ return -EFBIG; ++ } ++ ++#ifdef DEBUG_ALLOC2 ++ fprintf(stderr, "alloc_clusters: size=%" PRId64 " -> %" PRId64 "\n", ++ size, ++ (s->free_cluster_index - nb_clusters) << s->cluster_bits); ++#endif ++ return (s->free_cluster_index - nb_clusters) << s->cluster_bits; ++} ++ ++int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) ++{ ++ int64_t offset; ++ int ret; ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); ++ do { ++ offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET); ++ if (offset < 0) { ++ return offset; ++ } ++ ++ ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); ++ } while (ret == -EAGAIN); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return offset; ++} ++ ++int64_t coroutine_fn qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, ++ int64_t nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t cluster_index, refcount; ++ uint64_t i; ++ int ret; ++ ++ assert(nb_clusters >= 0); ++ if (nb_clusters == 0) { ++ return 0; ++ } ++ ++ do { ++ /* Check how many clusters there are free */ ++ cluster_index = offset >> s->cluster_bits; ++ for(i = 0; i < nb_clusters; i++) { ++ ret = qcow2_get_refcount(bs, cluster_index++, &refcount); ++ if (ret < 0) { ++ return ret; ++ } else if (refcount != 0) { ++ break; ++ } ++ } ++ ++ /* And then allocate them */ ++ ret = update_refcount(bs, offset, i << s->cluster_bits, 1, false, ++ QCOW2_DISCARD_NEVER); ++ } while (ret == -EAGAIN); ++ ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return i; ++} ++ ++/* only used to allocate compressed sectors. We try to allocate ++ contiguous sectors. size must be <= cluster_size */ ++int64_t coroutine_fn GRAPH_RDLOCK qcow2_alloc_bytes(BlockDriverState *bs, int size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t offset; ++ size_t free_in_cluster; ++ int ret; ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES); ++ assert(size > 0 && size <= s->cluster_size); ++ assert(!s->free_byte_offset || offset_into_cluster(s, s->free_byte_offset)); ++ ++ offset = s->free_byte_offset; ++ ++ if (offset) { ++ uint64_t refcount; ++ ret = qcow2_get_refcount(bs, offset >> s->cluster_bits, &refcount); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (refcount == s->refcount_max) { ++ offset = 0; ++ } ++ } ++ ++ free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); ++ do { ++ if (!offset || free_in_cluster < size) { ++ int64_t new_cluster; ++ ++ new_cluster = alloc_clusters_noref(bs, s->cluster_size, ++ MIN(s->cluster_offset_mask, ++ QCOW_MAX_CLUSTER_OFFSET)); ++ if (new_cluster < 0) { ++ return new_cluster; ++ } ++ ++ if (new_cluster == 0) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " ++ "allocation of compressed cluster " ++ "at offset 0"); ++ return -EIO; ++ } ++ ++ if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) { ++ offset = new_cluster; ++ free_in_cluster = s->cluster_size; ++ } else { ++ free_in_cluster += s->cluster_size; ++ } ++ } ++ ++ assert(offset); ++ ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER); ++ if (ret < 0) { ++ offset = 0; ++ } ++ } while (ret == -EAGAIN); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* The cluster refcount was incremented; refcount blocks must be flushed ++ * before the caller's L2 table updates. */ ++ qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); ++ ++ s->free_byte_offset = offset + size; ++ if (!offset_into_cluster(s, s->free_byte_offset)) { ++ s->free_byte_offset = 0; ++ } ++ ++ return offset; ++} ++ ++void qcow2_free_clusters(BlockDriverState *bs, ++ int64_t offset, int64_t size, ++ enum qcow2_discard_type type) ++{ ++ int ret; ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE); ++ ret = update_refcount(bs, offset, size, 1, true, type); ++ if (ret < 0) { ++ fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret)); ++ /* TODO Remember the clusters to free them later and avoid leaking */ ++ } ++} ++ ++/* ++ * Free a cluster using its L2 entry (handles clusters of all types, e.g. ++ * normal cluster, compressed cluster, etc.) ++ */ ++void qcow2_free_any_cluster(BlockDriverState *bs, uint64_t l2_entry, ++ enum qcow2_discard_type type) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCow2ClusterType ctype = qcow2_get_cluster_type(bs, l2_entry); ++ ++ if (has_data_file(bs)) { ++ if (s->discard_passthrough[type] && ++ (ctype == QCOW2_CLUSTER_NORMAL || ++ ctype == QCOW2_CLUSTER_ZERO_ALLOC)) ++ { ++ bdrv_pdiscard(s->data_file, l2_entry & L2E_OFFSET_MASK, ++ s->cluster_size); ++ } ++ return; ++ } ++ ++ switch (ctype) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ { ++ uint64_t coffset; ++ int csize; ++ ++ qcow2_parse_compressed_l2_entry(bs, l2_entry, &coffset, &csize); ++ qcow2_free_clusters(bs, coffset, csize, type); ++ } ++ break; ++ case QCOW2_CLUSTER_NORMAL: ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ if (offset_into_cluster(s, l2_entry & L2E_OFFSET_MASK)) { ++ qcow2_signal_corruption(bs, false, -1, -1, ++ "Cannot free unaligned cluster %#llx", ++ l2_entry & L2E_OFFSET_MASK); ++ } else { ++ qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK, ++ s->cluster_size, type); ++ } ++ break; ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ case QCOW2_CLUSTER_UNALLOCATED: ++ break; ++ default: ++ abort(); ++ } ++} ++ ++int qcow2_write_caches(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ ret = qcow2_cache_write(bs, s->l2_table_cache); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (qcow2_need_accurate_refcounts(s)) { ++ ret = qcow2_cache_write(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++int qcow2_flush_caches(BlockDriverState *bs) ++{ ++ int ret = qcow2_write_caches(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return bdrv_flush(bs->file->bs); ++} ++ ++/*********************************************************/ ++/* snapshots and image creation */ ++ ++ ++ ++/* update the refcounts of snapshots and the copied flag */ ++int qcow2_update_snapshot_refcount(BlockDriverState *bs, ++ int64_t l1_table_offset, int l1_size, int addend) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l1_table, *l2_slice, l2_offset, entry, l1_size2, refcount; ++ bool l1_allocated = false; ++ int64_t old_entry, old_l2_offset; ++ unsigned slice, slice_size2, n_slices; ++ int i, j, l1_modified = 0; ++ int ret; ++ ++ assert(addend >= -1 && addend <= 1); ++ ++ l2_slice = NULL; ++ l1_table = NULL; ++ l1_size2 = l1_size * L1E_SIZE; ++ slice_size2 = s->l2_slice_size * l2_entry_size(s); ++ n_slices = s->cluster_size / slice_size2; ++ ++ s->cache_discards = true; ++ ++ /* WARNING: qcow2_snapshot_goto relies on this function not using the ++ * l1_table_offset when it is the current s->l1_table_offset! Be careful ++ * when changing this! */ ++ if (l1_table_offset != s->l1_table_offset) { ++ l1_table = g_try_malloc0(l1_size2); ++ if (l1_size2 && l1_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ l1_allocated = true; ++ ++ ret = bdrv_pread(bs->file, l1_table_offset, l1_size2, l1_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (i = 0; i < l1_size; i++) { ++ be64_to_cpus(&l1_table[i]); ++ } ++ } else { ++ assert(l1_size == s->l1_size); ++ l1_table = s->l1_table; ++ l1_allocated = false; ++ } ++ ++ for (i = 0; i < l1_size; i++) { ++ l2_offset = l1_table[i]; ++ if (l2_offset) { ++ old_l2_offset = l2_offset; ++ l2_offset &= L1E_OFFSET_MASK; ++ ++ if (offset_into_cluster(s, l2_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "L2 table offset %#" ++ PRIx64 " unaligned (L1 index: %#x)", ++ l2_offset, i); ++ ret = -EIO; ++ goto fail; ++ } ++ ++ for (slice = 0; slice < n_slices; slice++) { ++ ret = qcow2_cache_get(bs, s->l2_table_cache, ++ l2_offset + slice * slice_size2, ++ (void **) &l2_slice); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ for (j = 0; j < s->l2_slice_size; j++) { ++ uint64_t cluster_index; ++ uint64_t offset; ++ ++ entry = get_l2_entry(s, l2_slice, j); ++ old_entry = entry; ++ entry &= ~QCOW_OFLAG_COPIED; ++ offset = entry & L2E_OFFSET_MASK; ++ ++ switch (qcow2_get_cluster_type(bs, entry)) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ if (addend != 0) { ++ uint64_t coffset; ++ int csize; ++ ++ qcow2_parse_compressed_l2_entry(bs, entry, ++ &coffset, &csize); ++ ret = update_refcount( ++ bs, coffset, csize, ++ abs(addend), addend < 0, ++ QCOW2_DISCARD_SNAPSHOT); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ /* compressed clusters are never modified */ ++ refcount = 2; ++ break; ++ ++ case QCOW2_CLUSTER_NORMAL: ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ if (offset_into_cluster(s, offset)) { ++ /* Here l2_index means table (not slice) index */ ++ int l2_index = slice * s->l2_slice_size + j; ++ qcow2_signal_corruption( ++ bs, true, -1, -1, "Cluster " ++ "allocation offset %#" PRIx64 ++ " unaligned (L2 offset: %#" ++ PRIx64 ", L2 index: %#x)", ++ offset, l2_offset, l2_index); ++ ret = -EIO; ++ goto fail; ++ } ++ ++ cluster_index = offset >> s->cluster_bits; ++ assert(cluster_index); ++ if (addend != 0) { ++ ret = qcow2_update_cluster_refcount( ++ bs, cluster_index, abs(addend), addend < 0, ++ QCOW2_DISCARD_SNAPSHOT); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ ret = qcow2_get_refcount(bs, cluster_index, &refcount); ++ if (ret < 0) { ++ goto fail; ++ } ++ break; ++ ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ case QCOW2_CLUSTER_UNALLOCATED: ++ refcount = 0; ++ break; ++ ++ default: ++ abort(); ++ } ++ ++ if (refcount == 1) { ++ entry |= QCOW_OFLAG_COPIED; ++ } ++ if (entry != old_entry) { ++ if (addend > 0) { ++ qcow2_cache_set_dependency(bs, s->l2_table_cache, ++ s->refcount_block_cache); ++ } ++ set_l2_entry(s, l2_slice, j, entry); ++ qcow2_cache_entry_mark_dirty(s->l2_table_cache, ++ l2_slice); ++ } ++ } ++ ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ ++ if (addend != 0) { ++ ret = qcow2_update_cluster_refcount(bs, l2_offset >> ++ s->cluster_bits, ++ abs(addend), addend < 0, ++ QCOW2_DISCARD_SNAPSHOT); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, ++ &refcount); ++ if (ret < 0) { ++ goto fail; ++ } else if (refcount == 1) { ++ l2_offset |= QCOW_OFLAG_COPIED; ++ } ++ if (l2_offset != old_l2_offset) { ++ l1_table[i] = l2_offset; ++ l1_modified = 1; ++ } ++ } ++ } ++ ++ ret = bdrv_flush(bs); ++fail: ++ if (l2_slice) { ++ qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); ++ } ++ ++ s->cache_discards = false; ++ qcow2_process_discards(bs, ret); ++ ++ /* Update L1 only if it isn't deleted anyway (addend = -1) */ ++ if (ret == 0 && addend >= 0 && l1_modified) { ++ for (i = 0; i < l1_size; i++) { ++ cpu_to_be64s(&l1_table[i]); ++ } ++ ++ ret = bdrv_pwrite_sync(bs->file, l1_table_offset, l1_size2, l1_table, ++ 0); ++ ++ for (i = 0; i < l1_size; i++) { ++ be64_to_cpus(&l1_table[i]); ++ } ++ } ++ if (l1_allocated) ++ g_free(l1_table); ++ return ret; ++} ++ ++ ++ ++ ++/*********************************************************/ ++/* refcount checking functions */ ++ ++ ++static uint64_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries) ++{ ++ /* This assertion holds because there is no way we can address more than ++ * 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because ++ * offsets have to be representable in bytes); due to every cluster ++ * corresponding to one refcount entry, we are well below that limit */ ++ assert(entries < (UINT64_C(1) << (64 - 9))); ++ ++ /* Thanks to the assertion this will not overflow, because ++ * s->refcount_order < 7. ++ * (note: x << s->refcount_order == x * s->refcount_bits) */ ++ return DIV_ROUND_UP(entries << s->refcount_order, 8); ++} ++ ++/** ++ * Reallocates *array so that it can hold new_size entries. *size must contain ++ * the current number of entries in *array. If the reallocation fails, *array ++ * and *size will not be modified and -errno will be returned. If the ++ * reallocation is successful, *array will be set to the new buffer, *size ++ * will be set to new_size and 0 will be returned. The size of the reallocated ++ * refcount array buffer will be aligned to a cluster boundary, and the newly ++ * allocated area will be zeroed. ++ */ ++static int realloc_refcount_array(BDRVQcow2State *s, void **array, ++ int64_t *size, int64_t new_size) ++{ ++ int64_t old_byte_size, new_byte_size; ++ void *new_ptr; ++ ++ /* Round to clusters so the array can be directly written to disk */ ++ old_byte_size = size_to_clusters(s, refcount_array_byte_size(s, *size)) ++ * s->cluster_size; ++ new_byte_size = size_to_clusters(s, refcount_array_byte_size(s, new_size)) ++ * s->cluster_size; ++ ++ if (new_byte_size == old_byte_size) { ++ *size = new_size; ++ return 0; ++ } ++ ++ assert(new_byte_size > 0); ++ ++ if (new_byte_size > SIZE_MAX) { ++ return -ENOMEM; ++ } ++ ++ new_ptr = g_try_realloc(*array, new_byte_size); ++ if (!new_ptr) { ++ return -ENOMEM; ++ } ++ ++ if (new_byte_size > old_byte_size) { ++ memset((char *)new_ptr + old_byte_size, 0, ++ new_byte_size - old_byte_size); ++ } ++ ++ *array = new_ptr; ++ *size = new_size; ++ ++ return 0; ++} ++ ++/* ++ * Increases the refcount for a range of clusters in a given refcount table. ++ * This is used to construct a temporary refcount table out of L1 and L2 tables ++ * which can be compared to the refcount table saved in the image. ++ * ++ * Modifies the number of errors in res. ++ */ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size, ++ int64_t offset, int64_t size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t start, last, cluster_offset, k, refcount; ++ int64_t file_len; ++ int ret; ++ ++ if (size <= 0) { ++ return 0; ++ } ++ ++ file_len = bdrv_co_getlength(bs->file->bs); ++ if (file_len < 0) { ++ return file_len; ++ } ++ ++ /* ++ * Last cluster of qcow2 image may be semi-allocated, so it may be OK to ++ * reference some space after file end but it should be less than one ++ * cluster. ++ */ ++ if (offset + size - file_len >= s->cluster_size) { ++ fprintf(stderr, "ERROR: counting reference for region exceeding the " ++ "end of the file by one cluster or more: offset 0x%" PRIx64 ++ " size 0x%" PRIx64 "\n", offset, size); ++ res->corruptions++; ++ return 0; ++ } ++ ++ start = start_of_cluster(s, offset); ++ last = start_of_cluster(s, offset + size - 1); ++ for(cluster_offset = start; cluster_offset <= last; ++ cluster_offset += s->cluster_size) { ++ k = cluster_offset >> s->cluster_bits; ++ if (k >= *refcount_table_size) { ++ ret = realloc_refcount_array(s, refcount_table, ++ refcount_table_size, k + 1); ++ if (ret < 0) { ++ res->check_errors++; ++ return ret; ++ } ++ } ++ ++ refcount = s->get_refcount(*refcount_table, k); ++ if (refcount == s->refcount_max) { ++ fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 ++ "\n", cluster_offset); ++ fprintf(stderr, "Use qemu-img amend to increase the refcount entry " ++ "width or qemu-img convert to create a clean copy if the " ++ "image cannot be opened for writing\n"); ++ res->corruptions++; ++ continue; ++ } ++ s->set_refcount(*refcount_table, k, refcount + 1); ++ } ++ ++ return 0; ++} ++ ++/* Flags for check_refcounts_l1() and check_refcounts_l2() */ ++enum { ++ CHECK_FRAG_INFO = 0x2, /* update BlockFragInfo counters */ ++}; ++ ++/* ++ * Fix L2 entry by making it QCOW2_CLUSTER_ZERO_PLAIN (or making all its present ++ * subclusters QCOW2_SUBCLUSTER_ZERO_PLAIN). ++ * ++ * This function decrements res->corruptions on success, so the caller is ++ * responsible to increment res->corruptions prior to the call. ++ * ++ * On failure in-memory @l2_table may be modified. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++fix_l2_entry_by_zero(BlockDriverState *bs, BdrvCheckResult *res, ++ uint64_t l2_offset, uint64_t *l2_table, ++ int l2_index, bool active, ++ bool *metadata_overlap) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ int idx = l2_index * (l2_entry_size(s) / sizeof(uint64_t)); ++ uint64_t l2e_offset = l2_offset + (uint64_t)l2_index * l2_entry_size(s); ++ int ign = active ? QCOW2_OL_ACTIVE_L2 : QCOW2_OL_INACTIVE_L2; ++ ++ if (has_subclusters(s)) { ++ uint64_t l2_bitmap = get_l2_bitmap(s, l2_table, l2_index); ++ ++ /* Allocated subclusters become zero */ ++ l2_bitmap |= l2_bitmap << 32; ++ l2_bitmap &= QCOW_L2_BITMAP_ALL_ZEROES; ++ ++ set_l2_bitmap(s, l2_table, l2_index, l2_bitmap); ++ set_l2_entry(s, l2_table, l2_index, 0); ++ } else { ++ set_l2_entry(s, l2_table, l2_index, QCOW_OFLAG_ZERO); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, ign, l2e_offset, l2_entry_size(s), ++ false); ++ if (metadata_overlap) { ++ *metadata_overlap = ret < 0; ++ } ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Overlap check failed\n"); ++ goto fail; ++ } ++ ++ ret = bdrv_co_pwrite_sync(bs->file, l2e_offset, l2_entry_size(s), ++ &l2_table[idx], 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Failed to overwrite L2 " ++ "table entry: %s\n", strerror(-ret)); ++ goto fail; ++ } ++ ++ res->corruptions--; ++ res->corruptions_fixed++; ++ return 0; ++ ++fail: ++ res->check_errors++; ++ return ret; ++} ++ ++/* ++ * Increases the refcount in the given refcount table for the all clusters ++ * referenced in the L2 table. While doing so, performs some checks on L2 ++ * entries. ++ * ++ * Returns the number of errors found by the checks or -errno if an internal ++ * error occurred. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, ++ int64_t *refcount_table_size, int64_t l2_offset, ++ int flags, BdrvCheckMode fix, bool active) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t l2_entry, l2_bitmap; ++ uint64_t next_contiguous_offset = 0; ++ int i, ret; ++ size_t l2_size_bytes = s->l2_size * l2_entry_size(s); ++ g_autofree uint64_t *l2_table = g_malloc(l2_size_bytes); ++ bool metadata_overlap; ++ ++ /* Read L2 table from disk */ ++ ret = bdrv_co_pread(bs->file, l2_offset, l2_size_bytes, l2_table, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); ++ res->check_errors++; ++ return ret; ++ } ++ ++ /* Do the actual checks */ ++ for (i = 0; i < s->l2_size; i++) { ++ uint64_t coffset; ++ int csize; ++ QCow2ClusterType type; ++ ++ l2_entry = get_l2_entry(s, l2_table, i); ++ l2_bitmap = get_l2_bitmap(s, l2_table, i); ++ type = qcow2_get_cluster_type(bs, l2_entry); ++ ++ if (type != QCOW2_CLUSTER_COMPRESSED) { ++ /* Check reserved bits of Standard Cluster Descriptor */ ++ if (l2_entry & L2E_STD_RESERVED_MASK) { ++ fprintf(stderr, "ERROR found l2 entry with reserved bits set: " ++ "%" PRIx64 "\n", l2_entry); ++ res->corruptions++; ++ } ++ } ++ ++ switch (type) { ++ case QCOW2_CLUSTER_COMPRESSED: ++ /* Compressed clusters don't have QCOW_OFLAG_COPIED */ ++ if (l2_entry & QCOW_OFLAG_COPIED) { ++ fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": " ++ "copied flag must never be set for compressed " ++ "clusters\n", l2_entry & s->cluster_offset_mask); ++ l2_entry &= ~QCOW_OFLAG_COPIED; ++ res->corruptions++; ++ } ++ ++ if (has_data_file(bs)) { ++ fprintf(stderr, "ERROR compressed cluster %d with data file, " ++ "entry=0x%" PRIx64 "\n", i, l2_entry); ++ res->corruptions++; ++ break; ++ } ++ ++ if (l2_bitmap) { ++ fprintf(stderr, "ERROR compressed cluster %d with non-zero " ++ "subcluster allocation bitmap, entry=0x%" PRIx64 "\n", ++ i, l2_entry); ++ res->corruptions++; ++ break; ++ } ++ ++ /* Mark cluster as used */ ++ qcow2_parse_compressed_l2_entry(bs, l2_entry, &coffset, &csize); ++ ret = qcow2_inc_refcounts_imrt( ++ bs, res, refcount_table, refcount_table_size, coffset, csize); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (flags & CHECK_FRAG_INFO) { ++ res->bfi.allocated_clusters++; ++ res->bfi.compressed_clusters++; ++ ++ /* ++ * Compressed clusters are fragmented by nature. Since they ++ * take up sub-sector space but we only have sector granularity ++ * I/O we need to re-read the same sectors even for adjacent ++ * compressed clusters. ++ */ ++ res->bfi.fragmented_clusters++; ++ } ++ break; ++ ++ case QCOW2_CLUSTER_ZERO_ALLOC: ++ case QCOW2_CLUSTER_NORMAL: ++ { ++ uint64_t offset = l2_entry & L2E_OFFSET_MASK; ++ ++ if ((l2_bitmap >> 32) & l2_bitmap) { ++ res->corruptions++; ++ fprintf(stderr, "ERROR offset=%" PRIx64 ": Allocated " ++ "cluster has corrupted subcluster allocation bitmap\n", ++ offset); ++ } ++ ++ /* Correct offsets are cluster aligned */ ++ if (offset_into_cluster(s, offset)) { ++ bool contains_data; ++ res->corruptions++; ++ ++ if (has_subclusters(s)) { ++ contains_data = (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC); ++ } else { ++ contains_data = !(l2_entry & QCOW_OFLAG_ZERO); ++ } ++ ++ if (!contains_data) { ++ fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated " ++ "cluster is not properly aligned; L2 entry " ++ "corrupted.\n", ++ fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", ++ offset); ++ if (fix & BDRV_FIX_ERRORS) { ++ ret = fix_l2_entry_by_zero(bs, res, l2_offset, ++ l2_table, i, active, ++ &metadata_overlap); ++ if (metadata_overlap) { ++ /* ++ * Something is seriously wrong, so abort checking ++ * this L2 table. ++ */ ++ return ret; ++ } ++ ++ if (ret == 0) { ++ /* ++ * Skip marking the cluster as used ++ * (it is unused now). ++ */ ++ continue; ++ } ++ ++ /* ++ * Failed to fix. ++ * Do not abort, continue checking the rest of this ++ * L2 table's entries. ++ */ ++ } ++ } else { ++ fprintf(stderr, "ERROR offset=%" PRIx64 ": Data cluster is " ++ "not properly aligned; L2 entry corrupted.\n", offset); ++ } ++ } ++ ++ if (flags & CHECK_FRAG_INFO) { ++ res->bfi.allocated_clusters++; ++ if (next_contiguous_offset && ++ offset != next_contiguous_offset) { ++ res->bfi.fragmented_clusters++; ++ } ++ next_contiguous_offset = offset + s->cluster_size; ++ } ++ ++ /* Mark cluster as used */ ++ if (!has_data_file(bs)) { ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, ++ refcount_table_size, ++ offset, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ break; ++ } ++ ++ case QCOW2_CLUSTER_ZERO_PLAIN: ++ /* Impossible when image has subclusters */ ++ assert(!l2_bitmap); ++ break; ++ ++ case QCOW2_CLUSTER_UNALLOCATED: ++ if (l2_bitmap & QCOW_L2_BITMAP_ALL_ALLOC) { ++ res->corruptions++; ++ fprintf(stderr, "ERROR: Unallocated " ++ "cluster has non-zero subcluster allocation map\n"); ++ } ++ break; ++ ++ default: ++ abort(); ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Increases the refcount for the L1 table, its L2 tables and all referenced ++ * clusters in the given refcount table. While doing so, performs some checks ++ * on L1 and L2 entries. ++ * ++ * Returns the number of errors found by the checks or -errno if an internal ++ * error occurred. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++check_refcounts_l1(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, int64_t *refcount_table_size, ++ int64_t l1_table_offset, int l1_size, ++ int flags, BdrvCheckMode fix, bool active) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ size_t l1_size_bytes = l1_size * L1E_SIZE; ++ g_autofree uint64_t *l1_table = NULL; ++ uint64_t l2_offset; ++ int i, ret; ++ ++ if (!l1_size) { ++ return 0; ++ } ++ ++ /* Mark L1 table as used */ ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, refcount_table_size, ++ l1_table_offset, l1_size_bytes); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ l1_table = g_try_malloc(l1_size_bytes); ++ if (l1_table == NULL) { ++ res->check_errors++; ++ return -ENOMEM; ++ } ++ ++ /* Read L1 table entries from disk */ ++ ret = bdrv_co_pread(bs->file, l1_table_offset, l1_size_bytes, l1_table, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); ++ res->check_errors++; ++ return ret; ++ } ++ ++ for (i = 0; i < l1_size; i++) { ++ be64_to_cpus(&l1_table[i]); ++ } ++ ++ /* Do the actual checks */ ++ for (i = 0; i < l1_size; i++) { ++ if (!l1_table[i]) { ++ continue; ++ } ++ ++ if (l1_table[i] & L1E_RESERVED_MASK) { ++ fprintf(stderr, "ERROR found L1 entry with reserved bits set: " ++ "%" PRIx64 "\n", l1_table[i]); ++ res->corruptions++; ++ } ++ ++ l2_offset = l1_table[i] & L1E_OFFSET_MASK; ++ ++ /* Mark L2 table as used */ ++ ret = qcow2_inc_refcounts_imrt(bs, res, ++ refcount_table, refcount_table_size, ++ l2_offset, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* L2 tables are cluster aligned */ ++ if (offset_into_cluster(s, l2_offset)) { ++ fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not " ++ "cluster aligned; L1 entry corrupted\n", l2_offset); ++ res->corruptions++; ++ } ++ ++ /* Process and check L2 entries */ ++ ret = check_refcounts_l2(bs, res, refcount_table, ++ refcount_table_size, l2_offset, flags, ++ fix, active); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Checks the OFLAG_COPIED flag for all L1 and L2 entries. ++ * ++ * This function does not print an error message nor does it increment ++ * check_errors if qcow2_get_refcount fails (this is because such an error will ++ * have been already detected and sufficiently signaled by the calling function ++ * (qcow2_check_refcounts) by the time this function is called). ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size); ++ int ret; ++ uint64_t refcount; ++ int i, j; ++ bool repair; ++ ++ if (fix & BDRV_FIX_ERRORS) { ++ /* Always repair */ ++ repair = true; ++ } else if (fix & BDRV_FIX_LEAKS) { ++ /* Repair only if that seems safe: This function is always ++ * called after the refcounts have been fixed, so the refcount ++ * is accurate if that repair was successful */ ++ repair = !res->check_errors && !res->corruptions && !res->leaks; ++ } else { ++ repair = false; ++ } ++ ++ for (i = 0; i < s->l1_size; i++) { ++ uint64_t l1_entry = s->l1_table[i]; ++ uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK; ++ int l2_dirty = 0; ++ ++ if (!l2_offset) { ++ continue; ++ } ++ ++ ret = qcow2_get_refcount(bs, l2_offset >> s->cluster_bits, ++ &refcount); ++ if (ret < 0) { ++ /* don't print message nor increment check_errors */ ++ continue; ++ } ++ if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { ++ res->corruptions++; ++ fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " ++ "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", ++ repair ? "Repairing" : "ERROR", i, l1_entry, refcount); ++ if (repair) { ++ s->l1_table[i] = refcount == 1 ++ ? l1_entry | QCOW_OFLAG_COPIED ++ : l1_entry & ~QCOW_OFLAG_COPIED; ++ ret = qcow2_write_l1_entry(bs, i); ++ if (ret < 0) { ++ res->check_errors++; ++ goto fail; ++ } ++ res->corruptions--; ++ res->corruptions_fixed++; ++ } ++ } ++ ++ ret = bdrv_co_pread(bs->file, l2_offset, s->l2_size * l2_entry_size(s), ++ l2_table, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Could not read L2 table: %s\n", ++ strerror(-ret)); ++ res->check_errors++; ++ goto fail; ++ } ++ ++ for (j = 0; j < s->l2_size; j++) { ++ uint64_t l2_entry = get_l2_entry(s, l2_table, j); ++ uint64_t data_offset = l2_entry & L2E_OFFSET_MASK; ++ QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry); ++ ++ if (cluster_type == QCOW2_CLUSTER_NORMAL || ++ cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) { ++ if (has_data_file(bs)) { ++ refcount = 1; ++ } else { ++ ret = qcow2_get_refcount(bs, ++ data_offset >> s->cluster_bits, ++ &refcount); ++ if (ret < 0) { ++ /* don't print message nor increment check_errors */ ++ continue; ++ } ++ } ++ if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { ++ res->corruptions++; ++ fprintf(stderr, "%s OFLAG_COPIED data cluster: " ++ "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", ++ repair ? "Repairing" : "ERROR", l2_entry, refcount); ++ if (repair) { ++ set_l2_entry(s, l2_table, j, ++ refcount == 1 ? ++ l2_entry | QCOW_OFLAG_COPIED : ++ l2_entry & ~QCOW_OFLAG_COPIED); ++ l2_dirty++; ++ } ++ } ++ } ++ } ++ ++ if (l2_dirty > 0) { ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, ++ l2_offset, s->cluster_size, ++ false); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Could not write L2 table; metadata " ++ "overlap check failed: %s\n", strerror(-ret)); ++ res->check_errors++; ++ goto fail; ++ } ++ ++ ret = bdrv_co_pwrite(bs->file, l2_offset, s->cluster_size, l2_table, 0); ++ if (ret < 0) { ++ fprintf(stderr, "ERROR: Could not write L2 table: %s\n", ++ strerror(-ret)); ++ res->check_errors++; ++ goto fail; ++ } ++ res->corruptions -= l2_dirty; ++ res->corruptions_fixed += l2_dirty; ++ } ++ } ++ ++ ret = 0; ++ ++fail: ++ qemu_vfree(l2_table); ++ return ret; ++} ++ ++/* ++ * Checks consistency of refblocks and accounts for each refblock in ++ * *refcount_table. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++check_refblocks(BlockDriverState *bs, BdrvCheckResult *res, ++ BdrvCheckMode fix, bool *rebuild, ++ void **refcount_table, int64_t *nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i, size; ++ int ret; ++ ++ for(i = 0; i < s->refcount_table_size; i++) { ++ uint64_t offset, cluster; ++ offset = s->refcount_table[i] & REFT_OFFSET_MASK; ++ cluster = offset >> s->cluster_bits; ++ ++ if (s->refcount_table[i] & REFT_RESERVED_MASK) { ++ fprintf(stderr, "ERROR refcount table entry %" PRId64 " has " ++ "reserved bits set\n", i); ++ res->corruptions++; ++ *rebuild = true; ++ continue; ++ } ++ ++ /* Refcount blocks are cluster aligned */ ++ if (offset_into_cluster(s, offset)) { ++ fprintf(stderr, "ERROR refcount block %" PRId64 " is not " ++ "cluster aligned; refcount table entry corrupted\n", i); ++ res->corruptions++; ++ *rebuild = true; ++ continue; ++ } ++ ++ if (cluster >= *nb_clusters) { ++ res->corruptions++; ++ fprintf(stderr, "%s refcount block %" PRId64 " is outside image\n", ++ fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); ++ ++ if (fix & BDRV_FIX_ERRORS) { ++ int64_t new_nb_clusters; ++ Error *local_err = NULL; ++ ++ if (offset > INT64_MAX - s->cluster_size) { ++ ret = -EINVAL; ++ goto resize_fail; ++ } ++ ++ ret = bdrv_co_truncate(bs->file, offset + s->cluster_size, false, ++ PREALLOC_MODE_OFF, 0, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto resize_fail; ++ } ++ size = bdrv_co_getlength(bs->file->bs); ++ if (size < 0) { ++ ret = size; ++ goto resize_fail; ++ } ++ ++ new_nb_clusters = size_to_clusters(s, size); ++ assert(new_nb_clusters >= *nb_clusters); ++ ++ ret = realloc_refcount_array(s, refcount_table, ++ nb_clusters, new_nb_clusters); ++ if (ret < 0) { ++ res->check_errors++; ++ return ret; ++ } ++ ++ if (cluster >= *nb_clusters) { ++ ret = -EINVAL; ++ goto resize_fail; ++ } ++ ++ res->corruptions--; ++ res->corruptions_fixed++; ++ ret = qcow2_inc_refcounts_imrt(bs, res, ++ refcount_table, nb_clusters, ++ offset, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ /* No need to check whether the refcount is now greater than 1: ++ * This area was just allocated and zeroed, so it can only be ++ * exactly 1 after qcow2_inc_refcounts_imrt() */ ++ continue; ++ ++resize_fail: ++ *rebuild = true; ++ fprintf(stderr, "ERROR could not resize image: %s\n", ++ strerror(-ret)); ++ } ++ continue; ++ } ++ ++ if (offset != 0) { ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ offset, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ if (s->get_refcount(*refcount_table, cluster) != 1) { ++ fprintf(stderr, "ERROR refcount block %" PRId64 ++ " refcount=%" PRIu64 "\n", i, ++ s->get_refcount(*refcount_table, cluster)); ++ res->corruptions++; ++ *rebuild = true; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Calculates an in-memory refcount table. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ BdrvCheckMode fix, bool *rebuild, ++ void **refcount_table, int64_t *nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i; ++ QCowSnapshot *sn; ++ int ret; ++ ++ if (!*refcount_table) { ++ int64_t old_size = 0; ++ ret = realloc_refcount_array(s, refcount_table, ++ &old_size, *nb_clusters); ++ if (ret < 0) { ++ res->check_errors++; ++ return ret; ++ } ++ } ++ ++ /* header */ ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ 0, s->cluster_size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* current L1 table */ ++ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, ++ s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO, ++ fix, true); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* snapshots */ ++ if (has_data_file(bs) && s->nb_snapshots) { ++ fprintf(stderr, "ERROR %d snapshots in image with data file\n", ++ s->nb_snapshots); ++ res->corruptions++; ++ } ++ ++ for (i = 0; i < s->nb_snapshots; i++) { ++ sn = s->snapshots + i; ++ if (offset_into_cluster(s, sn->l1_table_offset)) { ++ fprintf(stderr, "ERROR snapshot %s (%s) l1_offset=%#" PRIx64 ": " ++ "L1 table is not cluster aligned; snapshot table entry " ++ "corrupted\n", sn->id_str, sn->name, sn->l1_table_offset); ++ res->corruptions++; ++ continue; ++ } ++ if (sn->l1_size > QCOW_MAX_L1_SIZE / L1E_SIZE) { ++ fprintf(stderr, "ERROR snapshot %s (%s) l1_size=%#" PRIx32 ": " ++ "L1 table is too large; snapshot table entry corrupted\n", ++ sn->id_str, sn->name, sn->l1_size); ++ res->corruptions++; ++ continue; ++ } ++ ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, ++ sn->l1_table_offset, sn->l1_size, 0, fix, ++ false); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ s->snapshots_offset, s->snapshots_size); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* refcount data */ ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ s->refcount_table_offset, ++ s->refcount_table_size * ++ REFTABLE_ENTRY_SIZE); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* encryption */ ++ if (s->crypto_header.length) { ++ ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, ++ s->crypto_header.offset, ++ s->crypto_header.length); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* bitmaps */ ++ ret = qcow2_check_bitmaps_refcounts(bs, res, refcount_table, nb_clusters); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return check_refblocks(bs, res, fix, rebuild, refcount_table, nb_clusters); ++} ++ ++/* ++ * Compares the actual reference count for each cluster in the image against the ++ * refcount as reported by the refcount structures on-disk. ++ */ ++static void coroutine_fn GRAPH_RDLOCK ++compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res, ++ BdrvCheckMode fix, bool *rebuild, ++ int64_t *highest_cluster, ++ void *refcount_table, int64_t nb_clusters) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i; ++ uint64_t refcount1, refcount2; ++ int ret; ++ ++ for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) { ++ ret = qcow2_get_refcount(bs, i, &refcount1); ++ if (ret < 0) { ++ fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", ++ i, strerror(-ret)); ++ res->check_errors++; ++ continue; ++ } ++ ++ refcount2 = s->get_refcount(refcount_table, i); ++ ++ if (refcount1 > 0 || refcount2 > 0) { ++ *highest_cluster = i; ++ } ++ ++ if (refcount1 != refcount2) { ++ /* Check if we're allowed to fix the mismatch */ ++ int *num_fixed = NULL; ++ if (refcount1 == 0) { ++ *rebuild = true; ++ } else if (refcount1 > refcount2 && (fix & BDRV_FIX_LEAKS)) { ++ num_fixed = &res->leaks_fixed; ++ } else if (refcount1 < refcount2 && (fix & BDRV_FIX_ERRORS)) { ++ num_fixed = &res->corruptions_fixed; ++ } ++ ++ fprintf(stderr, "%s cluster %" PRId64 " refcount=%" PRIu64 ++ " reference=%" PRIu64 "\n", ++ num_fixed != NULL ? "Repairing" : ++ refcount1 < refcount2 ? "ERROR" : ++ "Leaked", ++ i, refcount1, refcount2); ++ ++ if (num_fixed) { ++ ret = update_refcount(bs, i << s->cluster_bits, 1, ++ refcount_diff(refcount1, refcount2), ++ refcount1 > refcount2, ++ QCOW2_DISCARD_ALWAYS); ++ if (ret >= 0) { ++ (*num_fixed)++; ++ continue; ++ } ++ } ++ ++ /* And if we couldn't, print an error */ ++ if (refcount1 < refcount2) { ++ res->corruptions++; ++ } else { ++ res->leaks++; ++ } ++ } ++ } ++} ++ ++/* ++ * Allocates clusters using an in-memory refcount table (IMRT) in contrast to ++ * the on-disk refcount structures. ++ * ++ * On input, *first_free_cluster tells where to start looking, and need not ++ * actually be a free cluster; the returned offset will not be before that ++ * cluster. On output, *first_free_cluster points to the first gap found, even ++ * if that gap was too small to be used as the returned offset. ++ * ++ * Note that *first_free_cluster is a cluster index whereas the return value is ++ * an offset. ++ */ ++static int64_t alloc_clusters_imrt(BlockDriverState *bs, ++ int cluster_count, ++ void **refcount_table, ++ int64_t *imrt_nb_clusters, ++ int64_t *first_free_cluster) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t cluster = *first_free_cluster, i; ++ bool first_gap = true; ++ int contiguous_free_clusters; ++ int ret; ++ ++ /* Starting at *first_free_cluster, find a range of at least cluster_count ++ * continuously free clusters */ ++ for (contiguous_free_clusters = 0; ++ cluster < *imrt_nb_clusters && ++ contiguous_free_clusters < cluster_count; ++ cluster++) ++ { ++ if (!s->get_refcount(*refcount_table, cluster)) { ++ contiguous_free_clusters++; ++ if (first_gap) { ++ /* If this is the first free cluster found, update ++ * *first_free_cluster accordingly */ ++ *first_free_cluster = cluster; ++ first_gap = false; ++ } ++ } else if (contiguous_free_clusters) { ++ contiguous_free_clusters = 0; ++ } ++ } ++ ++ /* If contiguous_free_clusters is greater than zero, it contains the number ++ * of continuously free clusters until the current cluster; the first free ++ * cluster in the current "gap" is therefore ++ * cluster - contiguous_free_clusters */ ++ ++ /* If no such range could be found, grow the in-memory refcount table ++ * accordingly to append free clusters at the end of the image */ ++ if (contiguous_free_clusters < cluster_count) { ++ /* contiguous_free_clusters clusters are already empty at the image end; ++ * we need cluster_count clusters; therefore, we have to allocate ++ * cluster_count - contiguous_free_clusters new clusters at the end of ++ * the image (which is the current value of cluster; note that cluster ++ * may exceed old_imrt_nb_clusters if *first_free_cluster pointed beyond ++ * the image end) */ ++ ret = realloc_refcount_array(s, refcount_table, imrt_nb_clusters, ++ cluster + cluster_count ++ - contiguous_free_clusters); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* Go back to the first free cluster */ ++ cluster -= contiguous_free_clusters; ++ for (i = 0; i < cluster_count; i++) { ++ s->set_refcount(*refcount_table, cluster + i, 1); ++ } ++ ++ return cluster << s->cluster_bits; ++} ++ ++/* ++ * Helper function for rebuild_refcount_structure(). ++ * ++ * Scan the range of clusters [first_cluster, end_cluster) for allocated ++ * clusters and write all corresponding refblocks to disk. The refblock ++ * and allocation data is taken from the in-memory refcount table ++ * *refcount_table[] (of size *nb_clusters), which is basically one big ++ * (unlimited size) refblock for the whole image. ++ * ++ * For these refblocks, clusters are allocated using said in-memory ++ * refcount table. Care is taken that these allocations are reflected ++ * in the refblocks written to disk. ++ * ++ * The refblocks' offsets are written into a reftable, which is ++ * *on_disk_reftable_ptr[] (of size *on_disk_reftable_entries_ptr). If ++ * that reftable is of insufficient size, it will be resized to fit. ++ * This reftable is not written to disk. ++ * ++ * (If *on_disk_reftable_ptr is not NULL, the entries within are assumed ++ * to point to existing valid refblocks that do not need to be allocated ++ * again.) ++ * ++ * Return whether the on-disk reftable array was resized (true/false), ++ * or -errno on error. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++rebuild_refcounts_write_refblocks( ++ BlockDriverState *bs, void **refcount_table, int64_t *nb_clusters, ++ int64_t first_cluster, int64_t end_cluster, ++ uint64_t **on_disk_reftable_ptr, uint32_t *on_disk_reftable_entries_ptr, ++ Error **errp ++ ) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t cluster; ++ int64_t refblock_offset, refblock_start, refblock_index; ++ int64_t first_free_cluster = 0; ++ uint64_t *on_disk_reftable = *on_disk_reftable_ptr; ++ uint32_t on_disk_reftable_entries = *on_disk_reftable_entries_ptr; ++ void *on_disk_refblock; ++ bool reftable_grown = false; ++ int ret; ++ ++ for (cluster = first_cluster; cluster < end_cluster; cluster++) { ++ /* Check all clusters to find refblocks that contain non-zero entries */ ++ if (!s->get_refcount(*refcount_table, cluster)) { ++ continue; ++ } ++ ++ /* ++ * This cluster is allocated, so we need to create a refblock ++ * for it. The data we will write to disk is just the ++ * respective slice from *refcount_table, so it will contain ++ * accurate refcounts for all clusters belonging to this ++ * refblock. After we have written it, we will therefore skip ++ * all remaining clusters in this refblock. ++ */ ++ ++ refblock_index = cluster >> s->refcount_block_bits; ++ refblock_start = refblock_index << s->refcount_block_bits; ++ ++ if (on_disk_reftable_entries > refblock_index && ++ on_disk_reftable[refblock_index]) ++ { ++ /* ++ * We can get here after a `goto write_refblocks`: We have a ++ * reftable from a previous run, and the refblock is already ++ * allocated. No need to allocate it again. ++ */ ++ refblock_offset = on_disk_reftable[refblock_index]; ++ } else { ++ int64_t refblock_cluster_index; ++ ++ /* Don't allocate a cluster in a refblock already written to disk */ ++ if (first_free_cluster < refblock_start) { ++ first_free_cluster = refblock_start; ++ } ++ refblock_offset = alloc_clusters_imrt(bs, 1, refcount_table, ++ nb_clusters, ++ &first_free_cluster); ++ if (refblock_offset < 0) { ++ error_setg_errno(errp, -refblock_offset, ++ "ERROR allocating refblock"); ++ return refblock_offset; ++ } ++ ++ refblock_cluster_index = refblock_offset / s->cluster_size; ++ if (refblock_cluster_index >= end_cluster) { ++ /* ++ * We must write the refblock that holds this refblock's ++ * refcount ++ */ ++ end_cluster = refblock_cluster_index + 1; ++ } ++ ++ if (on_disk_reftable_entries <= refblock_index) { ++ on_disk_reftable_entries = ++ ROUND_UP((refblock_index + 1) * REFTABLE_ENTRY_SIZE, ++ s->cluster_size) / REFTABLE_ENTRY_SIZE; ++ on_disk_reftable = ++ g_try_realloc(on_disk_reftable, ++ on_disk_reftable_entries * ++ REFTABLE_ENTRY_SIZE); ++ if (!on_disk_reftable) { ++ error_setg(errp, "ERROR allocating reftable memory"); ++ return -ENOMEM; ++ } ++ ++ memset(on_disk_reftable + *on_disk_reftable_entries_ptr, 0, ++ (on_disk_reftable_entries - ++ *on_disk_reftable_entries_ptr) * ++ REFTABLE_ENTRY_SIZE); ++ ++ *on_disk_reftable_ptr = on_disk_reftable; ++ *on_disk_reftable_entries_ptr = on_disk_reftable_entries; ++ ++ reftable_grown = true; ++ } else { ++ assert(on_disk_reftable); ++ } ++ on_disk_reftable[refblock_index] = refblock_offset; ++ } ++ ++ /* Refblock is allocated, write it to disk */ ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset, ++ s->cluster_size, false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR writing refblock"); ++ return ret; ++ } ++ ++ /* ++ * The refblock is simply a slice of *refcount_table. ++ * Note that the size of *refcount_table is always aligned to ++ * whole clusters, so the write operation will not result in ++ * out-of-bounds accesses. ++ */ ++ on_disk_refblock = (void *)((char *) *refcount_table + ++ refblock_index * s->cluster_size); ++ ++ ret = bdrv_co_pwrite(bs->file, refblock_offset, s->cluster_size, ++ on_disk_refblock, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR writing refblock"); ++ return ret; ++ } ++ ++ /* This refblock is done, skip to its end */ ++ cluster = refblock_start + s->refcount_block_size - 1; ++ } ++ ++ return reftable_grown; ++} ++ ++/* ++ * Creates a new refcount structure based solely on the in-memory information ++ * given through *refcount_table (this in-memory information is basically just ++ * the concatenation of all refblocks). All necessary allocations will be ++ * reflected in that array. ++ * ++ * On success, the old refcount structure is leaked (it will be covered by the ++ * new refcount structure). ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++rebuild_refcount_structure(BlockDriverState *bs, BdrvCheckResult *res, ++ void **refcount_table, int64_t *nb_clusters, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t reftable_offset = -1; ++ int64_t reftable_length = 0; ++ int64_t reftable_clusters; ++ int64_t refblock_index; ++ uint32_t on_disk_reftable_entries = 0; ++ uint64_t *on_disk_reftable = NULL; ++ int ret = 0; ++ int reftable_size_changed = 0; ++ struct { ++ uint64_t reftable_offset; ++ uint32_t reftable_clusters; ++ } QEMU_PACKED reftable_offset_and_clusters; ++ ++ qcow2_cache_empty(bs, s->refcount_block_cache); ++ ++ /* ++ * For each refblock containing entries, we try to allocate a ++ * cluster (in the in-memory refcount table) and write its offset ++ * into on_disk_reftable[]. We then write the whole refblock to ++ * disk (as a slice of the in-memory refcount table). ++ * This is done by rebuild_refcounts_write_refblocks(). ++ * ++ * Once we have scanned all clusters, we try to find space for the ++ * reftable. This will dirty the in-memory refcount table (i.e. ++ * make it differ from the refblocks we have already written), so we ++ * need to run rebuild_refcounts_write_refblocks() again for the ++ * range of clusters where the reftable has been allocated. ++ * ++ * This second run might make the reftable grow again, in which case ++ * we will need to allocate another space for it, which is why we ++ * repeat all this until the reftable stops growing. ++ * ++ * (This loop will terminate, because with every cluster the ++ * reftable grows, it can accommodate a multitude of more refcounts, ++ * so that at some point this must be able to cover the reftable ++ * and all refblocks describing it.) ++ * ++ * We then convert the reftable to big-endian and write it to disk. ++ * ++ * Note that we never free any reftable allocations. Doing so would ++ * needlessly complicate the algorithm: The eventual second check ++ * run we do will clean up all leaks we have caused. ++ */ ++ ++ reftable_size_changed = ++ rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters, ++ 0, *nb_clusters, ++ &on_disk_reftable, ++ &on_disk_reftable_entries, errp); ++ if (reftable_size_changed < 0) { ++ res->check_errors++; ++ ret = reftable_size_changed; ++ goto fail; ++ } ++ ++ /* ++ * There was no reftable before, so rebuild_refcounts_write_refblocks() ++ * must have increased its size (from 0 to something). ++ */ ++ assert(reftable_size_changed); ++ ++ do { ++ int64_t reftable_start_cluster, reftable_end_cluster; ++ int64_t first_free_cluster = 0; ++ ++ reftable_length = on_disk_reftable_entries * REFTABLE_ENTRY_SIZE; ++ reftable_clusters = size_to_clusters(s, reftable_length); ++ ++ reftable_offset = alloc_clusters_imrt(bs, reftable_clusters, ++ refcount_table, nb_clusters, ++ &first_free_cluster); ++ if (reftable_offset < 0) { ++ error_setg_errno(errp, -reftable_offset, ++ "ERROR allocating reftable"); ++ res->check_errors++; ++ ret = reftable_offset; ++ goto fail; ++ } ++ ++ /* ++ * We need to update the affected refblocks, so re-run the ++ * write_refblocks loop for the reftable's range of clusters. ++ */ ++ assert(offset_into_cluster(s, reftable_offset) == 0); ++ reftable_start_cluster = reftable_offset / s->cluster_size; ++ reftable_end_cluster = reftable_start_cluster + reftable_clusters; ++ reftable_size_changed = ++ rebuild_refcounts_write_refblocks(bs, refcount_table, nb_clusters, ++ reftable_start_cluster, ++ reftable_end_cluster, ++ &on_disk_reftable, ++ &on_disk_reftable_entries, errp); ++ if (reftable_size_changed < 0) { ++ res->check_errors++; ++ ret = reftable_size_changed; ++ goto fail; ++ } ++ ++ /* ++ * If the reftable size has changed, we will need to find a new ++ * allocation, repeating the loop. ++ */ ++ } while (reftable_size_changed); ++ ++ /* The above loop must have run at least once */ ++ assert(reftable_offset >= 0); ++ ++ /* ++ * All allocations are done, all refblocks are written, convert the ++ * reftable to big-endian and write it to disk. ++ */ ++ ++ for (refblock_index = 0; refblock_index < on_disk_reftable_entries; ++ refblock_index++) ++ { ++ cpu_to_be64s(&on_disk_reftable[refblock_index]); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, reftable_length, ++ false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR writing reftable"); ++ goto fail; ++ } ++ ++ assert(reftable_length < INT_MAX); ++ ret = bdrv_co_pwrite(bs->file, reftable_offset, reftable_length, ++ on_disk_reftable, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR writing reftable"); ++ goto fail; ++ } ++ ++ /* Enter new reftable into the image header */ ++ reftable_offset_and_clusters.reftable_offset = cpu_to_be64(reftable_offset); ++ reftable_offset_and_clusters.reftable_clusters = ++ cpu_to_be32(reftable_clusters); ++ ret = bdrv_co_pwrite_sync(bs->file, ++ offsetof(QCowHeader, refcount_table_offset), ++ sizeof(reftable_offset_and_clusters), ++ &reftable_offset_and_clusters, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR setting reftable"); ++ goto fail; ++ } ++ ++ for (refblock_index = 0; refblock_index < on_disk_reftable_entries; ++ refblock_index++) ++ { ++ be64_to_cpus(&on_disk_reftable[refblock_index]); ++ } ++ s->refcount_table = on_disk_reftable; ++ s->refcount_table_offset = reftable_offset; ++ s->refcount_table_size = on_disk_reftable_entries; ++ update_max_refcount_table_index(s); ++ ++ return 0; ++ ++fail: ++ g_free(on_disk_reftable); ++ return ret; ++} ++ ++/* ++ * Checks an image for refcount consistency. ++ * ++ * Returns 0 if no errors are found, the number of errors in case the image is ++ * detected as corrupted, and -errno when an internal error occurred. ++ */ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ BdrvCheckResult pre_compare_res; ++ int64_t size, highest_cluster, nb_clusters; ++ void *refcount_table = NULL; ++ bool rebuild = false; ++ int ret; ++ ++ size = bdrv_co_getlength(bs->file->bs); ++ if (size < 0) { ++ res->check_errors++; ++ return size; ++ } ++ ++ nb_clusters = size_to_clusters(s, size); ++ if (nb_clusters > INT_MAX) { ++ res->check_errors++; ++ return -EFBIG; ++ } ++ ++ res->bfi.total_clusters = ++ size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE); ++ ++ ret = calculate_refcounts(bs, res, fix, &rebuild, &refcount_table, ++ &nb_clusters); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* In case we don't need to rebuild the refcount structure (but want to fix ++ * something), this function is immediately called again, in which case the ++ * result should be ignored */ ++ pre_compare_res = *res; ++ compare_refcounts(bs, res, 0, &rebuild, &highest_cluster, refcount_table, ++ nb_clusters); ++ ++ if (rebuild && (fix & BDRV_FIX_ERRORS)) { ++ BdrvCheckResult old_res = *res; ++ int fresh_leaks = 0; ++ Error *local_err = NULL; ++ ++ fprintf(stderr, "Rebuilding refcount structure\n"); ++ ret = rebuild_refcount_structure(bs, res, &refcount_table, ++ &nb_clusters, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ ++ res->corruptions = 0; ++ res->leaks = 0; ++ ++ /* Because the old reftable has been exchanged for a new one the ++ * references have to be recalculated */ ++ rebuild = false; ++ memset(refcount_table, 0, refcount_array_byte_size(s, nb_clusters)); ++ ret = calculate_refcounts(bs, res, 0, &rebuild, &refcount_table, ++ &nb_clusters); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (fix & BDRV_FIX_LEAKS) { ++ /* The old refcount structures are now leaked, fix it; the result ++ * can be ignored, aside from leaks which were introduced by ++ * rebuild_refcount_structure() that could not be fixed */ ++ BdrvCheckResult saved_res = *res; ++ *res = (BdrvCheckResult){ 0 }; ++ ++ compare_refcounts(bs, res, BDRV_FIX_LEAKS, &rebuild, ++ &highest_cluster, refcount_table, nb_clusters); ++ if (rebuild) { ++ fprintf(stderr, "ERROR rebuilt refcount structure is still " ++ "broken\n"); ++ } ++ ++ /* Any leaks accounted for here were introduced by ++ * rebuild_refcount_structure() because that function has created a ++ * new refcount structure from scratch */ ++ fresh_leaks = res->leaks; ++ *res = saved_res; ++ } ++ ++ if (res->corruptions < old_res.corruptions) { ++ res->corruptions_fixed += old_res.corruptions - res->corruptions; ++ } ++ if (res->leaks < old_res.leaks) { ++ res->leaks_fixed += old_res.leaks - res->leaks; ++ } ++ res->leaks += fresh_leaks; ++ } else if (fix) { ++ if (rebuild) { ++ fprintf(stderr, "ERROR need to rebuild refcount structures\n"); ++ res->check_errors++; ++ ret = -EIO; ++ goto fail; ++ } ++ ++ if (res->leaks || res->corruptions) { ++ *res = pre_compare_res; ++ compare_refcounts(bs, res, fix, &rebuild, &highest_cluster, ++ refcount_table, nb_clusters); ++ } ++ } ++ ++ /* check OFLAG_COPIED */ ++ ret = check_oflag_copied(bs, res, fix); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ res->image_end_offset = (highest_cluster + 1) * s->cluster_size; ++ ret = 0; ++ ++fail: ++ g_free(refcount_table); ++ ++ return ret; ++} ++ ++#define overlaps_with(ofs, sz) \ ++ ranges_overlap(offset, size, ofs, sz) ++ ++/* ++ * Checks if the given offset into the image file is actually free to use by ++ * looking for overlaps with important metadata sections (L1/L2 tables etc.), ++ * i.e. a sanity check without relying on the refcount tables. ++ * ++ * The ign parameter specifies what checks not to perform (being a bitmask of ++ * QCow2MetadataOverlap values), i.e., what sections to ignore. ++ * ++ * Returns: ++ * - 0 if writing to this offset will not affect the mentioned metadata ++ * - a positive QCow2MetadataOverlap value indicating one overlapping section ++ * - a negative value (-errno) indicating an error while performing a check, ++ * e.g. when bdrv_pread failed on QCOW2_OL_INACTIVE_L2 ++ */ ++int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, ++ int64_t size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int chk = s->overlap_check & ~ign; ++ int i, j; ++ ++ if (!size) { ++ return 0; ++ } ++ ++ if (chk & QCOW2_OL_MAIN_HEADER) { ++ if (offset < s->cluster_size) { ++ return QCOW2_OL_MAIN_HEADER; ++ } ++ } ++ ++ /* align range to test to cluster boundaries */ ++ size = ROUND_UP(offset_into_cluster(s, offset) + size, s->cluster_size); ++ offset = start_of_cluster(s, offset); ++ ++ if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) { ++ if (overlaps_with(s->l1_table_offset, s->l1_size * L1E_SIZE)) { ++ return QCOW2_OL_ACTIVE_L1; ++ } ++ } ++ ++ if ((chk & QCOW2_OL_REFCOUNT_TABLE) && s->refcount_table_size) { ++ if (overlaps_with(s->refcount_table_offset, ++ s->refcount_table_size * REFTABLE_ENTRY_SIZE)) { ++ return QCOW2_OL_REFCOUNT_TABLE; ++ } ++ } ++ ++ if ((chk & QCOW2_OL_SNAPSHOT_TABLE) && s->snapshots_size) { ++ if (overlaps_with(s->snapshots_offset, s->snapshots_size)) { ++ return QCOW2_OL_SNAPSHOT_TABLE; ++ } ++ } ++ ++ if ((chk & QCOW2_OL_INACTIVE_L1) && s->snapshots) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (s->snapshots[i].l1_size && ++ overlaps_with(s->snapshots[i].l1_table_offset, ++ s->snapshots[i].l1_size * L1E_SIZE)) { ++ return QCOW2_OL_INACTIVE_L1; ++ } ++ } ++ } ++ ++ if ((chk & QCOW2_OL_ACTIVE_L2) && s->l1_table) { ++ for (i = 0; i < s->l1_size; i++) { ++ if ((s->l1_table[i] & L1E_OFFSET_MASK) && ++ overlaps_with(s->l1_table[i] & L1E_OFFSET_MASK, ++ s->cluster_size)) { ++ return QCOW2_OL_ACTIVE_L2; ++ } ++ } ++ } ++ ++ if ((chk & QCOW2_OL_REFCOUNT_BLOCK) && s->refcount_table) { ++ unsigned last_entry = s->max_refcount_table_index; ++ assert(last_entry < s->refcount_table_size); ++ assert(last_entry + 1 == s->refcount_table_size || ++ (s->refcount_table[last_entry + 1] & REFT_OFFSET_MASK) == 0); ++ for (i = 0; i <= last_entry; i++) { ++ if ((s->refcount_table[i] & REFT_OFFSET_MASK) && ++ overlaps_with(s->refcount_table[i] & REFT_OFFSET_MASK, ++ s->cluster_size)) { ++ return QCOW2_OL_REFCOUNT_BLOCK; ++ } ++ } ++ } ++ ++ if ((chk & QCOW2_OL_INACTIVE_L2) && s->snapshots) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ uint64_t l1_ofs = s->snapshots[i].l1_table_offset; ++ uint32_t l1_sz = s->snapshots[i].l1_size; ++ uint64_t l1_sz2 = l1_sz * L1E_SIZE; ++ uint64_t *l1; ++ int ret; ++ ++ ret = qcow2_validate_table(bs, l1_ofs, l1_sz, L1E_SIZE, ++ QCOW_MAX_L1_SIZE, "", NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ l1 = g_try_malloc(l1_sz2); ++ ++ if (l1_sz2 && l1 == NULL) { ++ return -ENOMEM; ++ } ++ ++ ret = bdrv_pread(bs->file, l1_ofs, l1_sz2, l1, 0); ++ if (ret < 0) { ++ g_free(l1); ++ return ret; ++ } ++ ++ for (j = 0; j < l1_sz; j++) { ++ uint64_t l2_ofs = be64_to_cpu(l1[j]) & L1E_OFFSET_MASK; ++ if (l2_ofs && overlaps_with(l2_ofs, s->cluster_size)) { ++ g_free(l1); ++ return QCOW2_OL_INACTIVE_L2; ++ } ++ } ++ ++ g_free(l1); ++ } ++ } ++ ++ if ((chk & QCOW2_OL_BITMAP_DIRECTORY) && ++ (s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) ++ { ++ if (overlaps_with(s->bitmap_directory_offset, ++ s->bitmap_directory_size)) ++ { ++ return QCOW2_OL_BITMAP_DIRECTORY; ++ } ++ } ++ ++ return 0; ++} ++ ++static const char *metadata_ol_names[] = { ++ [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", ++ [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", ++ [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", ++ [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", ++ [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", ++ [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", ++ [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", ++ [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", ++ [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory", ++}; ++QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names)); ++ ++/* ++ * First performs a check for metadata overlaps (through ++ * qcow2_check_metadata_overlap); if that fails with a negative value (error ++ * while performing a check), that value is returned. If an impending overlap ++ * is detected, the BDS will be made unusable, the qcow2 file marked corrupt ++ * and -EIO returned. ++ * ++ * Returns 0 if there were neither overlaps nor errors while checking for ++ * overlaps; or a negative value (-errno) on error. ++ */ ++int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, ++ int64_t size, bool data_file) ++{ ++ int ret; ++ ++ if (data_file && has_data_file(bs)) { ++ return 0; ++ } ++ ++ ret = qcow2_check_metadata_overlap(bs, ign, offset, size); ++ if (ret < 0) { ++ return ret; ++ } else if (ret > 0) { ++ int metadata_ol_bitnr = ctz32(ret); ++ assert(metadata_ol_bitnr < QCOW2_OL_MAX_BITNR); ++ ++ qcow2_signal_corruption(bs, true, offset, size, "Preventing invalid " ++ "write on metadata (overlaps with %s)", ++ metadata_ol_names[metadata_ol_bitnr]); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++/* A pointer to a function of this type is given to walk_over_reftable(). That ++ * function will create refblocks and pass them to a RefblockFinishOp once they ++ * are completed (@refblock). @refblock_empty is set if the refblock is ++ * completely empty. ++ * ++ * Along with the refblock, a corresponding reftable entry is passed, in the ++ * reftable @reftable (which may be reallocated) at @reftable_index. ++ * ++ * @allocated should be set to true if a new cluster has been allocated. ++ */ ++typedef int /* GRAPH_RDLOCK_PTR */ ++ (RefblockFinishOp)(BlockDriverState *bs, uint64_t **reftable, ++ uint64_t reftable_index, uint64_t *reftable_size, ++ void *refblock, bool refblock_empty, ++ bool *allocated, Error **errp); ++ ++/** ++ * This "operation" for walk_over_reftable() allocates the refblock on disk (if ++ * it is not empty) and inserts its offset into the new reftable. The size of ++ * this new reftable is increased as required. ++ */ ++static int GRAPH_RDLOCK ++alloc_refblock(BlockDriverState *bs, uint64_t **reftable, ++ uint64_t reftable_index, uint64_t *reftable_size, ++ void *refblock, bool refblock_empty, bool *allocated, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t offset; ++ ++ if (!refblock_empty && reftable_index >= *reftable_size) { ++ uint64_t *new_reftable; ++ uint64_t new_reftable_size; ++ ++ new_reftable_size = ROUND_UP(reftable_index + 1, ++ s->cluster_size / REFTABLE_ENTRY_SIZE); ++ if (new_reftable_size > QCOW_MAX_REFTABLE_SIZE / REFTABLE_ENTRY_SIZE) { ++ error_setg(errp, ++ "This operation would make the refcount table grow " ++ "beyond the maximum size supported by QEMU, aborting"); ++ return -ENOTSUP; ++ } ++ ++ new_reftable = g_try_realloc(*reftable, new_reftable_size * ++ REFTABLE_ENTRY_SIZE); ++ if (!new_reftable) { ++ error_setg(errp, "Failed to increase reftable buffer size"); ++ return -ENOMEM; ++ } ++ ++ memset(new_reftable + *reftable_size, 0, ++ (new_reftable_size - *reftable_size) * REFTABLE_ENTRY_SIZE); ++ ++ *reftable = new_reftable; ++ *reftable_size = new_reftable_size; ++ } ++ ++ if (!refblock_empty && !(*reftable)[reftable_index]) { ++ offset = qcow2_alloc_clusters(bs, s->cluster_size); ++ if (offset < 0) { ++ error_setg_errno(errp, -offset, "Failed to allocate refblock"); ++ return offset; ++ } ++ (*reftable)[reftable_index] = offset; ++ *allocated = true; ++ } ++ ++ return 0; ++} ++ ++/** ++ * This "operation" for walk_over_reftable() writes the refblock to disk at the ++ * offset specified by the new reftable's entry. It does not modify the new ++ * reftable or change any refcounts. ++ */ ++static int GRAPH_RDLOCK ++flush_refblock(BlockDriverState *bs, uint64_t **reftable, ++ uint64_t reftable_index, uint64_t *reftable_size, ++ void *refblock, bool refblock_empty, bool *allocated, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t offset; ++ int ret; ++ ++ if (reftable_index < *reftable_size && (*reftable)[reftable_index]) { ++ offset = (*reftable)[reftable_index]; ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size, ++ false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Overlap check failed"); ++ return ret; ++ } ++ ++ ret = bdrv_pwrite(bs->file, offset, s->cluster_size, refblock, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to write refblock"); ++ return ret; ++ } ++ } else { ++ assert(refblock_empty); ++ } ++ ++ return 0; ++} ++ ++/** ++ * This function walks over the existing reftable and every referenced refblock; ++ * if @new_set_refcount is non-NULL, it is called for every refcount entry to ++ * create an equal new entry in the passed @new_refblock. Once that ++ * @new_refblock is completely filled, @operation will be called. ++ * ++ * @status_cb and @cb_opaque are used for the amend operation's status callback. ++ * @index is the index of the walk_over_reftable() calls and @total is the total ++ * number of walk_over_reftable() calls per amend operation. Both are used for ++ * calculating the parameters for the status callback. ++ * ++ * @allocated is set to true if a new cluster has been allocated. ++ */ ++static int GRAPH_RDLOCK ++walk_over_reftable(BlockDriverState *bs, uint64_t **new_reftable, ++ uint64_t *new_reftable_index, ++ uint64_t *new_reftable_size, ++ void *new_refblock, int new_refblock_size, ++ int new_refcount_bits, ++ RefblockFinishOp *operation, bool *allocated, ++ Qcow2SetRefcountFunc *new_set_refcount, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque, int index, int total, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t reftable_index; ++ bool new_refblock_empty = true; ++ int refblock_index; ++ int new_refblock_index = 0; ++ int ret; ++ ++ for (reftable_index = 0; reftable_index < s->refcount_table_size; ++ reftable_index++) ++ { ++ uint64_t refblock_offset = s->refcount_table[reftable_index] ++ & REFT_OFFSET_MASK; ++ ++ status_cb(bs, (uint64_t)index * s->refcount_table_size + reftable_index, ++ (uint64_t)total * s->refcount_table_size, cb_opaque); ++ ++ if (refblock_offset) { ++ void *refblock; ++ ++ if (offset_into_cluster(s, refblock_offset)) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Refblock offset %#" ++ PRIx64 " unaligned (reftable index: %#" ++ PRIx64 ")", refblock_offset, ++ reftable_index); ++ error_setg(errp, ++ "Image is corrupt (unaligned refblock offset)"); ++ return -EIO; ++ } ++ ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offset, ++ &refblock); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to retrieve refblock"); ++ return ret; ++ } ++ ++ for (refblock_index = 0; refblock_index < s->refcount_block_size; ++ refblock_index++) ++ { ++ uint64_t refcount; ++ ++ if (new_refblock_index >= new_refblock_size) { ++ /* new_refblock is now complete */ ++ ret = operation(bs, new_reftable, *new_reftable_index, ++ new_reftable_size, new_refblock, ++ new_refblock_empty, allocated, errp); ++ if (ret < 0) { ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ return ret; ++ } ++ ++ (*new_reftable_index)++; ++ new_refblock_index = 0; ++ new_refblock_empty = true; ++ } ++ ++ refcount = s->get_refcount(refblock, refblock_index); ++ if (new_refcount_bits < 64 && refcount >> new_refcount_bits) { ++ uint64_t offset; ++ ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ ++ offset = ((reftable_index << s->refcount_block_bits) ++ + refblock_index) << s->cluster_bits; ++ ++ error_setg(errp, "Cannot decrease refcount entry width to " ++ "%i bits: Cluster at offset %#" PRIx64 " has a " ++ "refcount of %" PRIu64, new_refcount_bits, ++ offset, refcount); ++ return -EINVAL; ++ } ++ ++ if (new_set_refcount) { ++ new_set_refcount(new_refblock, new_refblock_index++, ++ refcount); ++ } else { ++ new_refblock_index++; ++ } ++ new_refblock_empty = new_refblock_empty && refcount == 0; ++ } ++ ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ } else { ++ /* No refblock means every refcount is 0 */ ++ for (refblock_index = 0; refblock_index < s->refcount_block_size; ++ refblock_index++) ++ { ++ if (new_refblock_index >= new_refblock_size) { ++ /* new_refblock is now complete */ ++ ret = operation(bs, new_reftable, *new_reftable_index, ++ new_reftable_size, new_refblock, ++ new_refblock_empty, allocated, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ (*new_reftable_index)++; ++ new_refblock_index = 0; ++ new_refblock_empty = true; ++ } ++ ++ if (new_set_refcount) { ++ new_set_refcount(new_refblock, new_refblock_index++, 0); ++ } else { ++ new_refblock_index++; ++ } ++ } ++ } ++ } ++ ++ if (new_refblock_index > 0) { ++ /* Complete the potentially existing partially filled final refblock */ ++ if (new_set_refcount) { ++ for (; new_refblock_index < new_refblock_size; ++ new_refblock_index++) ++ { ++ new_set_refcount(new_refblock, new_refblock_index, 0); ++ } ++ } ++ ++ ret = operation(bs, new_reftable, *new_reftable_index, ++ new_reftable_size, new_refblock, new_refblock_empty, ++ allocated, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ (*new_reftable_index)++; ++ } ++ ++ status_cb(bs, (uint64_t)(index + 1) * s->refcount_table_size, ++ (uint64_t)total * s->refcount_table_size, cb_opaque); ++ ++ return 0; ++} ++ ++int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, ++ BlockDriverAmendStatusCB *status_cb, ++ void *cb_opaque, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2GetRefcountFunc *new_get_refcount; ++ Qcow2SetRefcountFunc *new_set_refcount; ++ void *new_refblock = qemu_blockalign(bs->file->bs, s->cluster_size); ++ uint64_t *new_reftable = NULL, new_reftable_size = 0; ++ uint64_t *old_reftable, old_reftable_size, old_reftable_offset; ++ uint64_t new_reftable_index = 0; ++ uint64_t i; ++ int64_t new_reftable_offset = 0, allocated_reftable_size = 0; ++ int new_refblock_size, new_refcount_bits = 1 << refcount_order; ++ int old_refcount_order; ++ int walk_index = 0; ++ int ret; ++ bool new_allocation; ++ ++ assert(s->qcow_version >= 3); ++ assert(refcount_order >= 0 && refcount_order <= 6); ++ ++ /* see qcow2_open() */ ++ new_refblock_size = 1 << (s->cluster_bits - (refcount_order - 3)); ++ ++ new_get_refcount = get_refcount_funcs[refcount_order]; ++ new_set_refcount = set_refcount_funcs[refcount_order]; ++ ++ ++ do { ++ int total_walks; ++ ++ new_allocation = false; ++ ++ /* At least we have to do this walk and the one which writes the ++ * refblocks; also, at least we have to do this loop here at least ++ * twice (normally), first to do the allocations, and second to ++ * determine that everything is correctly allocated, this then makes ++ * three walks in total */ ++ total_walks = MAX(walk_index + 2, 3); ++ ++ /* First, allocate the structures so they are present in the refcount ++ * structures */ ++ ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index, ++ &new_reftable_size, NULL, new_refblock_size, ++ new_refcount_bits, &alloc_refblock, ++ &new_allocation, NULL, status_cb, cb_opaque, ++ walk_index++, total_walks, errp); ++ if (ret < 0) { ++ goto done; ++ } ++ ++ new_reftable_index = 0; ++ ++ if (new_allocation) { ++ if (new_reftable_offset) { ++ qcow2_free_clusters( ++ bs, new_reftable_offset, ++ allocated_reftable_size * REFTABLE_ENTRY_SIZE, ++ QCOW2_DISCARD_NEVER); ++ } ++ ++ new_reftable_offset = qcow2_alloc_clusters(bs, new_reftable_size * ++ REFTABLE_ENTRY_SIZE); ++ if (new_reftable_offset < 0) { ++ error_setg_errno(errp, -new_reftable_offset, ++ "Failed to allocate the new reftable"); ++ ret = new_reftable_offset; ++ goto done; ++ } ++ allocated_reftable_size = new_reftable_size; ++ } ++ } while (new_allocation); ++ ++ /* Second, write the new refblocks */ ++ ret = walk_over_reftable(bs, &new_reftable, &new_reftable_index, ++ &new_reftable_size, new_refblock, ++ new_refblock_size, new_refcount_bits, ++ &flush_refblock, &new_allocation, new_set_refcount, ++ status_cb, cb_opaque, walk_index, walk_index + 1, ++ errp); ++ if (ret < 0) { ++ goto done; ++ } ++ assert(!new_allocation); ++ ++ ++ /* Write the new reftable */ ++ ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset, ++ new_reftable_size * REFTABLE_ENTRY_SIZE, ++ false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Overlap check failed"); ++ goto done; ++ } ++ ++ for (i = 0; i < new_reftable_size; i++) { ++ cpu_to_be64s(&new_reftable[i]); ++ } ++ ++ ret = bdrv_pwrite(bs->file, new_reftable_offset, ++ new_reftable_size * REFTABLE_ENTRY_SIZE, new_reftable, ++ 0); ++ ++ for (i = 0; i < new_reftable_size; i++) { ++ be64_to_cpus(&new_reftable[i]); ++ } ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to write the new reftable"); ++ goto done; ++ } ++ ++ ++ /* Empty the refcount cache */ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to flush the refblock cache"); ++ goto done; ++ } ++ ++ /* Update the image header to point to the new reftable; this only updates ++ * the fields which are relevant to qcow2_update_header(); other fields ++ * such as s->refcount_table or s->refcount_bits stay stale for now ++ * (because we have to restore everything if qcow2_update_header() fails) */ ++ old_refcount_order = s->refcount_order; ++ old_reftable_size = s->refcount_table_size; ++ old_reftable_offset = s->refcount_table_offset; ++ ++ s->refcount_order = refcount_order; ++ s->refcount_table_size = new_reftable_size; ++ s->refcount_table_offset = new_reftable_offset; ++ ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->refcount_order = old_refcount_order; ++ s->refcount_table_size = old_reftable_size; ++ s->refcount_table_offset = old_reftable_offset; ++ error_setg_errno(errp, -ret, "Failed to update the qcow2 header"); ++ goto done; ++ } ++ ++ /* Now update the rest of the in-memory information */ ++ old_reftable = s->refcount_table; ++ s->refcount_table = new_reftable; ++ update_max_refcount_table_index(s); ++ ++ s->refcount_bits = 1 << refcount_order; ++ s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); ++ s->refcount_max += s->refcount_max - 1; ++ ++ s->refcount_block_bits = s->cluster_bits - (refcount_order - 3); ++ s->refcount_block_size = 1 << s->refcount_block_bits; ++ ++ s->get_refcount = new_get_refcount; ++ s->set_refcount = new_set_refcount; ++ ++ /* For cleaning up all old refblocks and the old reftable below the "done" ++ * label */ ++ new_reftable = old_reftable; ++ new_reftable_size = old_reftable_size; ++ new_reftable_offset = old_reftable_offset; ++ ++done: ++ if (new_reftable) { ++ /* On success, new_reftable actually points to the old reftable (and ++ * new_reftable_size is the old reftable's size); but that is just ++ * fine */ ++ for (i = 0; i < new_reftable_size; i++) { ++ uint64_t offset = new_reftable[i] & REFT_OFFSET_MASK; ++ if (offset) { ++ qcow2_free_clusters(bs, offset, s->cluster_size, ++ QCOW2_DISCARD_OTHER); ++ } ++ } ++ g_free(new_reftable); ++ ++ if (new_reftable_offset > 0) { ++ qcow2_free_clusters(bs, new_reftable_offset, ++ new_reftable_size * REFTABLE_ENTRY_SIZE, ++ QCOW2_DISCARD_OTHER); ++ } ++ } ++ ++ qemu_vfree(new_refblock); ++ return ret; ++} ++ ++static int64_t coroutine_fn GRAPH_RDLOCK ++get_refblock_offset(BlockDriverState *bs, uint64_t offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint32_t index = offset_to_reftable_index(s, offset); ++ int64_t covering_refblock_offset = 0; ++ ++ if (index < s->refcount_table_size) { ++ covering_refblock_offset = s->refcount_table[index] & REFT_OFFSET_MASK; ++ } ++ if (!covering_refblock_offset) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Refblock at %#" PRIx64 " is " ++ "not covered by the refcount structures", ++ offset); ++ return -EIO; ++ } ++ ++ return covering_refblock_offset; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_discard_refcount_block(BlockDriverState *bs, uint64_t discard_block_offs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t refblock_offs; ++ uint64_t cluster_index = discard_block_offs >> s->cluster_bits; ++ uint32_t block_index = cluster_index & (s->refcount_block_size - 1); ++ void *refblock; ++ int ret; ++ ++ refblock_offs = get_refblock_offset(bs, discard_block_offs); ++ if (refblock_offs < 0) { ++ return refblock_offs; ++ } ++ ++ assert(discard_block_offs != 0); ++ ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs, ++ &refblock); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (s->get_refcount(refblock, block_index) != 1) { ++ qcow2_signal_corruption(bs, true, -1, -1, "Invalid refcount:" ++ " refblock offset %#" PRIx64 ++ ", reftable index %u" ++ ", block offset %#" PRIx64 ++ ", refcount %#" PRIx64, ++ refblock_offs, ++ offset_to_reftable_index(s, discard_block_offs), ++ discard_block_offs, ++ s->get_refcount(refblock, block_index)); ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ return -EINVAL; ++ } ++ s->set_refcount(refblock, block_index, 0); ++ ++ qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refblock); ++ ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ ++ if (cluster_index < s->free_cluster_index) { ++ s->free_cluster_index = cluster_index; ++ } ++ ++ refblock = qcow2_cache_is_table_offset(s->refcount_block_cache, ++ discard_block_offs); ++ if (refblock) { ++ /* discard refblock from the cache if refblock is cached */ ++ qcow2_cache_discard(s->refcount_block_cache, refblock); ++ } ++ update_refcount_discard(bs, discard_block_offs, s->cluster_size); ++ ++ return 0; ++} ++ ++int coroutine_fn qcow2_shrink_reftable(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t *reftable_tmp = ++ g_malloc(s->refcount_table_size * REFTABLE_ENTRY_SIZE); ++ int i, ret; ++ ++ for (i = 0; i < s->refcount_table_size; i++) { ++ int64_t refblock_offs = s->refcount_table[i] & REFT_OFFSET_MASK; ++ void *refblock; ++ bool unused_block; ++ ++ if (refblock_offs == 0) { ++ reftable_tmp[i] = 0; ++ continue; ++ } ++ ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs, ++ &refblock); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ /* the refblock has own reference */ ++ if (i == offset_to_reftable_index(s, refblock_offs)) { ++ uint64_t block_index = (refblock_offs >> s->cluster_bits) & ++ (s->refcount_block_size - 1); ++ uint64_t refcount = s->get_refcount(refblock, block_index); ++ ++ s->set_refcount(refblock, block_index, 0); ++ ++ unused_block = buffer_is_zero(refblock, s->cluster_size); ++ ++ s->set_refcount(refblock, block_index, refcount); ++ } else { ++ unused_block = buffer_is_zero(refblock, s->cluster_size); ++ } ++ qcow2_cache_put(s->refcount_block_cache, &refblock); ++ ++ reftable_tmp[i] = unused_block ? 0 : cpu_to_be64(s->refcount_table[i]); ++ } ++ ++ 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 ++ * reftable in memory to avoid possible image corruption. ++ */ ++ for (i = 0; i < s->refcount_table_size; i++) { ++ if (s->refcount_table[i] && !reftable_tmp[i]) { ++ if (ret == 0) { ++ ret = qcow2_discard_refcount_block(bs, s->refcount_table[i] & ++ REFT_OFFSET_MASK); ++ } ++ s->refcount_table[i] = 0; ++ } ++ } ++ ++ if (!s->cache_discards) { ++ qcow2_process_discards(bs, ret); ++ } ++ ++out: ++ g_free(reftable_tmp); ++ return ret; ++} ++ ++int64_t coroutine_fn qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i; ++ ++ for (i = size_to_clusters(s, size) - 1; i >= 0; i--) { ++ uint64_t refcount; ++ int ret = qcow2_get_refcount(bs, i, &refcount); ++ if (ret < 0) { ++ fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n", ++ i, strerror(-ret)); ++ return ret; ++ } ++ if (refcount > 0) { ++ return i; ++ } ++ } ++ qcow2_signal_corruption(bs, true, -1, -1, ++ "There are no references in the refcount table."); ++ return -EIO; ++} ++ ++int coroutine_fn GRAPH_RDLOCK ++qcow2_detect_metadata_preallocation(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t i, end_cluster, cluster_count = 0, threshold; ++ int64_t file_length, real_allocation, real_clusters; ++ ++ qemu_co_mutex_assert_locked(&s->lock); ++ ++ file_length = bdrv_co_getlength(bs->file->bs); ++ if (file_length < 0) { ++ return file_length; ++ } ++ ++ real_allocation = bdrv_co_get_allocated_file_size(bs->file->bs); ++ if (real_allocation < 0) { ++ return real_allocation; ++ } ++ ++ real_clusters = real_allocation / s->cluster_size; ++ threshold = MAX(real_clusters * 10 / 9, real_clusters + 2); ++ ++ end_cluster = size_to_clusters(s, file_length); ++ for (i = 0; i < end_cluster && cluster_count < threshold; i++) { ++ uint64_t refcount; ++ int ret = qcow2_get_refcount(bs, i, &refcount); ++ if (ret < 0) { ++ return ret; ++ } ++ cluster_count += !!refcount; ++ } ++ ++ return cluster_count >= threshold; ++} +diff --git a/qcow2/lib/qcow2-snapshot.c b/qcow2/lib/qcow2-snapshot.c +new file mode 100644 +index 00000000..92e47978 +--- /dev/null ++++ b/qcow2/lib/qcow2-snapshot.c +@@ -0,0 +1,1076 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "sysemu/block-backend.h" ++#include "qapi/error.h" ++#include "qcow2.h" ++#include "qemu/bswap.h" ++#include "qemu/error-report.h" ++#include "qemu/cutils.h" ++#include "qemu/memalign.h" ++ ++static void qcow2_free_single_snapshot(BlockDriverState *bs, int i) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ assert(i >= 0 && i < s->nb_snapshots); ++ g_free(s->snapshots[i].name); ++ g_free(s->snapshots[i].id_str); ++ g_free(s->snapshots[i].unknown_extra_data); ++ memset(&s->snapshots[i], 0, sizeof(s->snapshots[i])); ++} ++ ++void qcow2_free_snapshots(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ ++ for(i = 0; i < s->nb_snapshots; i++) { ++ qcow2_free_single_snapshot(bs, i); ++ } ++ g_free(s->snapshots); ++ s->snapshots = NULL; ++ s->nb_snapshots = 0; ++} ++ ++/* ++ * If @repair is true, try to repair a broken snapshot table instead ++ * of just returning an error: ++ * ++ * - If the snapshot table was too long, set *nb_clusters_reduced to ++ * the number of snapshots removed off the end. ++ * The caller will update the on-disk nb_snapshots accordingly; ++ * this leaks clusters, but is safe. ++ * (The on-disk information must be updated before ++ * qcow2_check_refcounts(), because that function relies on ++ * s->nb_snapshots to reflect the on-disk value.) ++ * ++ * - If there were snapshots with too much extra metadata, increment ++ * *extra_data_dropped for each. ++ * This requires the caller to eventually rewrite the whole snapshot ++ * table, which requires cluster allocation. Therefore, this should ++ * be done only after qcow2_check_refcounts() made sure the refcount ++ * structures are valid. ++ * (In the meantime, the image is still valid because ++ * qcow2_check_refcounts() does not do anything with snapshots' ++ * extra data.) ++ */ ++static coroutine_fn GRAPH_RDLOCK ++int qcow2_do_read_snapshots(BlockDriverState *bs, bool repair, ++ int *nb_clusters_reduced, ++ int *extra_data_dropped, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshotHeader h; ++ QCowSnapshotExtraData extra; ++ QCowSnapshot *sn; ++ int i, id_str_size, name_size; ++ int64_t offset, pre_sn_offset; ++ uint64_t table_length = 0; ++ int ret; ++ ++ if (!s->nb_snapshots) { ++ s->snapshots = NULL; ++ s->snapshots_size = 0; ++ return 0; ++ } ++ ++ offset = s->snapshots_offset; ++ s->snapshots = g_new0(QCowSnapshot, s->nb_snapshots); ++ ++ for(i = 0; i < s->nb_snapshots; i++) { ++ bool truncate_unknown_extra_data = false; ++ ++ pre_sn_offset = offset; ++ table_length = ROUND_UP(table_length, 8); ++ ++ /* Read statically sized part of the snapshot header */ ++ offset = ROUND_UP(offset, 8); ++ ret = bdrv_co_pread(bs->file, offset, sizeof(h), &h, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read snapshot table"); ++ goto fail; ++ } ++ ++ offset += sizeof(h); ++ sn = s->snapshots + i; ++ sn->l1_table_offset = be64_to_cpu(h.l1_table_offset); ++ sn->l1_size = be32_to_cpu(h.l1_size); ++ sn->vm_state_size = be32_to_cpu(h.vm_state_size); ++ sn->date_sec = be32_to_cpu(h.date_sec); ++ sn->date_nsec = be32_to_cpu(h.date_nsec); ++ sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec); ++ sn->extra_data_size = be32_to_cpu(h.extra_data_size); ++ ++ id_str_size = be16_to_cpu(h.id_str_size); ++ name_size = be16_to_cpu(h.name_size); ++ ++ if (sn->extra_data_size > QCOW_MAX_SNAPSHOT_EXTRA_DATA) { ++ if (!repair) { ++ ret = -EFBIG; ++ error_setg(errp, "Too much extra metadata in snapshot table " ++ "entry %i", i); ++ error_append_hint(errp, "You can force-remove this extra " ++ "metadata with qemu-img check -r all\n"); ++ goto fail; ++ } ++ ++ fprintf(stderr, "Discarding too much extra metadata in snapshot " ++ "table entry %i (%" PRIu32 " > %u)\n", ++ i, sn->extra_data_size, QCOW_MAX_SNAPSHOT_EXTRA_DATA); ++ ++ (*extra_data_dropped)++; ++ truncate_unknown_extra_data = true; ++ } ++ ++ /* Read known extra data */ ++ ret = bdrv_co_pread(bs->file, offset, ++ MIN(sizeof(extra), sn->extra_data_size), &extra, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read snapshot table"); ++ goto fail; ++ } ++ offset += MIN(sizeof(extra), sn->extra_data_size); ++ ++ if (sn->extra_data_size >= endof(QCowSnapshotExtraData, ++ vm_state_size_large)) { ++ sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large); ++ } ++ ++ if (sn->extra_data_size >= endof(QCowSnapshotExtraData, disk_size)) { ++ sn->disk_size = be64_to_cpu(extra.disk_size); ++ } else { ++ sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ } ++ ++ if (sn->extra_data_size >= endof(QCowSnapshotExtraData, icount)) { ++ sn->icount = be64_to_cpu(extra.icount); ++ } else { ++ sn->icount = -1ULL; ++ } ++ ++ if (sn->extra_data_size > sizeof(extra)) { ++ uint64_t extra_data_end; ++ size_t unknown_extra_data_size; ++ ++ extra_data_end = offset + sn->extra_data_size - sizeof(extra); ++ ++ if (truncate_unknown_extra_data) { ++ sn->extra_data_size = QCOW_MAX_SNAPSHOT_EXTRA_DATA; ++ } ++ ++ /* Store unknown extra data */ ++ unknown_extra_data_size = sn->extra_data_size - sizeof(extra); ++ sn->unknown_extra_data = g_malloc(unknown_extra_data_size); ++ ret = bdrv_co_pread(bs->file, offset, unknown_extra_data_size, ++ sn->unknown_extra_data, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to read snapshot table"); ++ goto fail; ++ } ++ offset = extra_data_end; ++ } ++ ++ /* Read snapshot ID */ ++ sn->id_str = g_malloc(id_str_size + 1); ++ ret = bdrv_co_pread(bs->file, offset, id_str_size, sn->id_str, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read snapshot table"); ++ goto fail; ++ } ++ offset += id_str_size; ++ sn->id_str[id_str_size] = '\0'; ++ ++ /* Read snapshot name */ ++ sn->name = g_malloc(name_size + 1); ++ ret = bdrv_co_pread(bs->file, offset, name_size, sn->name, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to read snapshot table"); ++ goto fail; ++ } ++ offset += name_size; ++ sn->name[name_size] = '\0'; ++ ++ /* Note that the extra data may have been truncated */ ++ table_length += sizeof(h) + sn->extra_data_size + id_str_size + ++ name_size; ++ if (!repair) { ++ assert(table_length == offset - s->snapshots_offset); ++ } ++ ++ if (table_length > QCOW_MAX_SNAPSHOTS_SIZE || ++ offset - s->snapshots_offset > INT_MAX) ++ { ++ if (!repair) { ++ ret = -EFBIG; ++ error_setg(errp, "Snapshot table is too big"); ++ error_append_hint(errp, "You can force-remove all %u " ++ "overhanging snapshots with qemu-img check " ++ "-r all\n", s->nb_snapshots - i); ++ goto fail; ++ } ++ ++ fprintf(stderr, "Discarding %u overhanging snapshots (snapshot " ++ "table is too big)\n", s->nb_snapshots - i); ++ ++ *nb_clusters_reduced += (s->nb_snapshots - i); ++ ++ /* Discard current snapshot also */ ++ qcow2_free_single_snapshot(bs, i); ++ ++ /* ++ * This leaks all the rest of the snapshot table and the ++ * snapshots' clusters, but we run in check -r all mode, ++ * so qcow2_check_refcounts() will take care of it. ++ */ ++ s->nb_snapshots = i; ++ offset = pre_sn_offset; ++ break; ++ } ++ } ++ ++ assert(offset - s->snapshots_offset <= INT_MAX); ++ s->snapshots_size = offset - s->snapshots_offset; ++ return 0; ++ ++fail: ++ qcow2_free_snapshots(bs); ++ return ret; ++} ++ ++int coroutine_fn qcow2_read_snapshots(BlockDriverState *bs, Error **errp) ++{ ++ return qcow2_do_read_snapshots(bs, false, NULL, NULL, errp); ++} ++ ++/* add at the end of the file a new list of snapshots */ ++int qcow2_write_snapshots(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *sn; ++ QCowSnapshotHeader h; ++ QCowSnapshotExtraData extra; ++ int i, name_size, id_str_size, snapshots_size; ++ struct { ++ uint32_t nb_snapshots; ++ uint64_t snapshots_offset; ++ } QEMU_PACKED header_data; ++ int64_t offset, snapshots_offset = 0; ++ int ret; ++ ++ /* compute the size of the snapshots */ ++ offset = 0; ++ for(i = 0; i < s->nb_snapshots; i++) { ++ sn = s->snapshots + i; ++ offset = ROUND_UP(offset, 8); ++ offset += sizeof(h); ++ offset += MAX(sizeof(extra), sn->extra_data_size); ++ offset += strlen(sn->id_str); ++ offset += strlen(sn->name); ++ ++ if (offset > QCOW_MAX_SNAPSHOTS_SIZE) { ++ ret = -EFBIG; ++ goto fail; ++ } ++ } ++ ++ assert(offset <= INT_MAX); ++ snapshots_size = offset; ++ ++ /* Allocate space for the new snapshot list */ ++ snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); ++ offset = snapshots_offset; ++ if (offset < 0) { ++ ret = offset; ++ goto fail; ++ } ++ ret = bdrv_flush(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* The snapshot list position has not yet been updated, so these clusters ++ * must indeed be completely free */ ++ ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size, false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ++ /* Write all snapshots to the new list */ ++ for(i = 0; i < s->nb_snapshots; i++) { ++ sn = s->snapshots + i; ++ memset(&h, 0, sizeof(h)); ++ h.l1_table_offset = cpu_to_be64(sn->l1_table_offset); ++ h.l1_size = cpu_to_be32(sn->l1_size); ++ /* If it doesn't fit in 32 bit, older implementations should treat it ++ * as a disk-only snapshot rather than truncate the VM state */ ++ if (sn->vm_state_size <= 0xffffffff) { ++ h.vm_state_size = cpu_to_be32(sn->vm_state_size); ++ } ++ h.date_sec = cpu_to_be32(sn->date_sec); ++ h.date_nsec = cpu_to_be32(sn->date_nsec); ++ h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec); ++ h.extra_data_size = cpu_to_be32(MAX(sizeof(extra), ++ sn->extra_data_size)); ++ ++ memset(&extra, 0, sizeof(extra)); ++ extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size); ++ extra.disk_size = cpu_to_be64(sn->disk_size); ++ extra.icount = cpu_to_be64(sn->icount); ++ ++ id_str_size = strlen(sn->id_str); ++ name_size = strlen(sn->name); ++ assert(id_str_size <= UINT16_MAX && name_size <= UINT16_MAX); ++ h.id_str_size = cpu_to_be16(id_str_size); ++ h.name_size = cpu_to_be16(name_size); ++ offset = ROUND_UP(offset, 8); ++ ++ ret = bdrv_pwrite(bs->file, offset, sizeof(h), &h, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += sizeof(h); ++ ++ ret = bdrv_pwrite(bs->file, offset, sizeof(extra), &extra, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += sizeof(extra); ++ ++ if (sn->extra_data_size > sizeof(extra)) { ++ size_t unknown_extra_data_size = ++ sn->extra_data_size - sizeof(extra); ++ ++ /* qcow2_read_snapshots() ensures no unbounded allocation */ ++ assert(unknown_extra_data_size <= BDRV_REQUEST_MAX_BYTES); ++ assert(sn->unknown_extra_data); ++ ++ ret = bdrv_pwrite(bs->file, offset, unknown_extra_data_size, ++ sn->unknown_extra_data, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += unknown_extra_data_size; ++ } ++ ++ ret = bdrv_pwrite(bs->file, offset, id_str_size, sn->id_str, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += id_str_size; ++ ++ ret = bdrv_pwrite(bs->file, offset, name_size, sn->name, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ offset += name_size; ++ } ++ ++ /* ++ * Update the header to point to the new snapshot table. This requires the ++ * new table and its refcounts to be stable on disk. ++ */ ++ ret = bdrv_flush(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ QEMU_BUILD_BUG_ON(offsetof(QCowHeader, snapshots_offset) != ++ endof(QCowHeader, nb_snapshots)); ++ ++ header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots); ++ header_data.snapshots_offset = cpu_to_be64(snapshots_offset); ++ ++ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), ++ sizeof(header_data), &header_data, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* free the old snapshot table */ ++ qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size, ++ QCOW2_DISCARD_SNAPSHOT); ++ s->snapshots_offset = snapshots_offset; ++ s->snapshots_size = snapshots_size; ++ return 0; ++ ++fail: ++ if (snapshots_offset > 0) { ++ qcow2_free_clusters(bs, snapshots_offset, snapshots_size, ++ QCOW2_DISCARD_ALWAYS); ++ } ++ return ret; ++} ++ ++int coroutine_fn qcow2_check_read_snapshot_table(BlockDriverState *bs, ++ BdrvCheckResult *result, ++ BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Error *local_err = NULL; ++ int nb_clusters_reduced = 0; ++ int extra_data_dropped = 0; ++ int ret; ++ struct { ++ uint32_t nb_snapshots; ++ uint64_t snapshots_offset; ++ } QEMU_PACKED snapshot_table_pointer; ++ ++ /* qcow2_do_open() discards this information in check mode */ ++ 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 " ++ "the image header: %s\n", strerror(-ret)); ++ return ret; ++ } ++ ++ s->snapshots_offset = be64_to_cpu(snapshot_table_pointer.snapshots_offset); ++ s->nb_snapshots = be32_to_cpu(snapshot_table_pointer.nb_snapshots); ++ ++ if (s->nb_snapshots > QCOW_MAX_SNAPSHOTS && (fix & BDRV_FIX_ERRORS)) { ++ fprintf(stderr, "Discarding %u overhanging snapshots\n", ++ s->nb_snapshots - QCOW_MAX_SNAPSHOTS); ++ ++ nb_clusters_reduced += s->nb_snapshots - QCOW_MAX_SNAPSHOTS; ++ s->nb_snapshots = QCOW_MAX_SNAPSHOTS; ++ } ++ ++ ret = qcow2_validate_table(bs, s->snapshots_offset, s->nb_snapshots, ++ sizeof(QCowSnapshotHeader), ++ sizeof(QCowSnapshotHeader) * QCOW_MAX_SNAPSHOTS, ++ "snapshot table", &local_err); ++ if (ret < 0) { ++ result->check_errors++; ++ error_reportf_err(local_err, "ERROR "); ++ ++ if (s->nb_snapshots > QCOW_MAX_SNAPSHOTS) { ++ fprintf(stderr, "You can force-remove all %u overhanging snapshots " ++ "with qemu-img check -r all\n", ++ s->nb_snapshots - QCOW_MAX_SNAPSHOTS); ++ } ++ ++ /* We did not read the snapshot table, so invalidate this information */ ++ s->snapshots_offset = 0; ++ s->nb_snapshots = 0; ++ ++ return ret; ++ } ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ret = qcow2_do_read_snapshots(bs, fix & BDRV_FIX_ERRORS, ++ &nb_clusters_reduced, &extra_data_dropped, ++ &local_err); ++ qemu_co_mutex_lock(&s->lock); ++ if (ret < 0) { ++ result->check_errors++; ++ error_reportf_err(local_err, ++ "ERROR failed to read the snapshot table: "); ++ ++ /* We did not read the snapshot table, so invalidate this information */ ++ s->snapshots_offset = 0; ++ s->nb_snapshots = 0; ++ ++ return ret; ++ } ++ result->corruptions += nb_clusters_reduced + extra_data_dropped; ++ ++ if (nb_clusters_reduced) { ++ /* ++ * Update image header now, because: ++ * (1) qcow2_check_refcounts() relies on s->nb_snapshots to be ++ * the same as what the image header says, ++ * (2) this leaks clusters, but qcow2_check_refcounts() will ++ * fix that. ++ */ ++ assert(fix & BDRV_FIX_ERRORS); ++ ++ snapshot_table_pointer.nb_snapshots = cpu_to_be32(s->nb_snapshots); ++ ret = bdrv_co_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), ++ sizeof(snapshot_table_pointer.nb_snapshots), ++ &snapshot_table_pointer.nb_snapshots, 0); ++ if (ret < 0) { ++ result->check_errors++; ++ fprintf(stderr, "ERROR failed to update the snapshot count in the " ++ "image header: %s\n", strerror(-ret)); ++ return ret; ++ } ++ ++ result->corruptions_fixed += nb_clusters_reduced; ++ result->corruptions -= nb_clusters_reduced; ++ } ++ ++ /* ++ * All of v3 images' snapshot table entries need to have at least ++ * 16 bytes of extra data. ++ */ ++ if (s->qcow_version >= 3) { ++ int i; ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (s->snapshots[i].extra_data_size < ++ sizeof_field(QCowSnapshotExtraData, vm_state_size_large) + ++ sizeof_field(QCowSnapshotExtraData, disk_size)) ++ { ++ result->corruptions++; ++ fprintf(stderr, "%s snapshot table entry %i is incomplete\n", ++ fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++int coroutine_fn qcow2_check_fix_snapshot_table(BlockDriverState *bs, ++ BdrvCheckResult *result, ++ BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ if (result->corruptions && (fix & BDRV_FIX_ERRORS)) { ++ qemu_co_mutex_unlock(&s->lock); ++ ret = qcow2_write_snapshots(bs); ++ qemu_co_mutex_lock(&s->lock); ++ if (ret < 0) { ++ result->check_errors++; ++ fprintf(stderr, "ERROR failed to update snapshot table: %s\n", ++ strerror(-ret)); ++ return ret; ++ } ++ ++ result->corruptions_fixed += result->corruptions; ++ result->corruptions = 0; ++ } ++ ++ return 0; ++} ++ ++static void find_new_snapshot_id(BlockDriverState *bs, ++ char *id_str, int id_str_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *sn; ++ int i; ++ unsigned long id, id_max = 0; ++ ++ for(i = 0; i < s->nb_snapshots; i++) { ++ sn = s->snapshots + i; ++ id = strtoul(sn->id_str, NULL, 10); ++ if (id > id_max) ++ id_max = id; ++ } ++ snprintf(id_str, id_str_size, "%lu", id_max + 1); ++} ++ ++static int find_snapshot_by_id_and_name(BlockDriverState *bs, ++ const char *id, ++ const char *name) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ ++ if (id && name) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (!strcmp(s->snapshots[i].id_str, id) && ++ !strcmp(s->snapshots[i].name, name)) { ++ return i; ++ } ++ } ++ } else if (id) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (!strcmp(s->snapshots[i].id_str, id)) { ++ return i; ++ } ++ } ++ } else if (name) { ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (!strcmp(s->snapshots[i].name, name)) { ++ return i; ++ } ++ } ++ } ++ ++ return -1; ++} ++ ++static int find_snapshot_by_id_or_name(BlockDriverState *bs, ++ const char *id_or_name) ++{ ++ int ret; ++ ++ ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL); ++ if (ret >= 0) { ++ return ret; ++ } ++ return find_snapshot_by_id_and_name(bs, NULL, id_or_name); ++} ++ ++/* if no id is provided, a new one is constructed */ ++int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *new_snapshot_list = NULL; ++ QCowSnapshot *old_snapshot_list = NULL; ++ QCowSnapshot sn1, *sn = &sn1; ++ int i, ret; ++ uint64_t *l1_table = NULL; ++ int64_t l1_table_offset; ++ ++ if (s->nb_snapshots >= QCOW_MAX_SNAPSHOTS) { ++ return -EFBIG; ++ } ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ ++ memset(sn, 0, sizeof(*sn)); ++ ++ /* Generate an ID */ ++ find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str)); ++ ++ /* Populate sn with passed data */ ++ sn->id_str = g_strdup(sn_info->id_str); ++ sn->name = g_strdup(sn_info->name); ++ ++ sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ sn->vm_state_size = sn_info->vm_state_size; ++ sn->date_sec = sn_info->date_sec; ++ sn->date_nsec = sn_info->date_nsec; ++ sn->vm_clock_nsec = sn_info->vm_clock_nsec; ++ sn->icount = sn_info->icount; ++ sn->extra_data_size = sizeof(QCowSnapshotExtraData); ++ ++ /* Allocate the L1 table of the snapshot and copy the current one there. */ ++ l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * L1E_SIZE); ++ if (l1_table_offset < 0) { ++ ret = l1_table_offset; ++ goto fail; ++ } ++ ++ sn->l1_table_offset = l1_table_offset; ++ sn->l1_size = s->l1_size; ++ ++ l1_table = g_try_new(uint64_t, s->l1_size); ++ if (s->l1_size && l1_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ for(i = 0; i < s->l1_size; i++) { ++ l1_table[i] = cpu_to_be64(s->l1_table[i]); ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset, ++ s->l1_size * L1E_SIZE, false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite(bs->file, sn->l1_table_offset, s->l1_size * L1E_SIZE, ++ l1_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ g_free(l1_table); ++ l1_table = NULL; ++ ++ /* ++ * Increase the refcounts of all clusters and make sure everything is ++ * stable on disk before updating the snapshot table to contain a pointer ++ * to the new L1 table. ++ */ ++ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Append the new snapshot to the snapshot list */ ++ new_snapshot_list = g_new(QCowSnapshot, s->nb_snapshots + 1); ++ if (s->snapshots) { ++ memcpy(new_snapshot_list, s->snapshots, ++ s->nb_snapshots * sizeof(QCowSnapshot)); ++ old_snapshot_list = s->snapshots; ++ } ++ s->snapshots = new_snapshot_list; ++ s->snapshots[s->nb_snapshots++] = *sn; ++ ++ ret = qcow2_write_snapshots(bs); ++ if (ret < 0) { ++ g_free(s->snapshots); ++ s->snapshots = old_snapshot_list; ++ s->nb_snapshots--; ++ goto fail; ++ } ++ ++ g_free(old_snapshot_list); ++ ++ /* The VM state isn't needed any more in the active L1 table; in fact, it ++ * hurts by causing expensive COW for the next snapshot. */ ++ qcow2_cluster_discard(bs, qcow2_vm_state_offset(s), ++ ROUND_UP(sn->vm_state_size, s->cluster_size), ++ QCOW2_DISCARD_NEVER, false); ++ ++#ifdef DEBUG_ALLOC ++ { ++ BdrvCheckResult result = {0}; ++ qcow2_check_refcounts(bs, &result, 0); ++ } ++#endif ++ return 0; ++ ++fail: ++ g_free(sn->id_str); ++ g_free(sn->name); ++ g_free(l1_table); ++ ++ return ret; ++} ++ ++/* copy the snapshot 'snapshot_name' into the current disk image */ ++int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *sn; ++ Error *local_err = NULL; ++ int i, snapshot_index; ++ int cur_l1_bytes, sn_l1_bytes; ++ int ret; ++ uint64_t *sn_l1_table = NULL; ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ ++ /* Search the snapshot */ ++ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); ++ if (snapshot_index < 0) { ++ return -ENOENT; ++ } ++ sn = &s->snapshots[snapshot_index]; ++ ++ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, ++ L1E_SIZE, QCOW_MAX_L1_SIZE, ++ "Snapshot L1 table", &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ ++ if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { ++ BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, ++ &local_err); ++ if (!blk) { ++ error_report_err(local_err); ++ ret = -ENOTSUP; ++ goto fail; ++ } ++ ++ ret = blk_truncate(blk, sn->disk_size, true, PREALLOC_MODE_OFF, 0, ++ &local_err); ++ blk_unref(blk); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ } ++ ++ /* ++ * Make sure that the current L1 table is big enough to contain the whole ++ * L1 table of the snapshot. If the snapshot L1 table is smaller, the ++ * current one must be padded with zeros. ++ */ ++ ret = qcow2_grow_l1_table(bs, sn->l1_size, true); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ cur_l1_bytes = s->l1_size * L1E_SIZE; ++ sn_l1_bytes = sn->l1_size * L1E_SIZE; ++ ++ /* ++ * Copy the snapshot L1 table to the current L1 table. ++ * ++ * Before overwriting the old current L1 table on disk, make sure to ++ * increase all refcounts for the clusters referenced by the new one. ++ * Decrease the refcount referenced by the old one only when the L1 ++ * table is overwritten. ++ */ ++ sn_l1_table = g_try_malloc0(cur_l1_bytes); ++ if (cur_l1_bytes && sn_l1_table == NULL) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_bytes, sn_l1_table, ++ 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, ++ sn->l1_size, 1); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, ++ s->l1_table_offset, cur_l1_bytes, ++ false); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, cur_l1_bytes, ++ sn_l1_table, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* ++ * Decrease refcount of clusters of current L1 table. ++ * ++ * At this point, the in-memory s->l1_table points to the old L1 table, ++ * whereas on disk we already have the new one. ++ * ++ * qcow2_update_snapshot_refcount special cases the current L1 table to use ++ * the in-memory data instead of really using the offset to load a new one, ++ * which is why this works. ++ */ ++ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, ++ s->l1_size, -1); ++ ++ /* ++ * Now update the in-memory L1 table to be in sync with the on-disk one. We ++ * need to do this even if updating refcounts failed. ++ */ ++ for(i = 0;i < s->l1_size; i++) { ++ s->l1_table[i] = be64_to_cpu(sn_l1_table[i]); ++ } ++ ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ g_free(sn_l1_table); ++ sn_l1_table = NULL; ++ ++ /* ++ * Update QCOW_OFLAG_COPIED in the active L1 table (it may have changed ++ * when we decreased the refcount of the old snapshot. ++ */ ++ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++#ifdef DEBUG_ALLOC ++ { ++ BdrvCheckResult result = {0}; ++ qcow2_check_refcounts(bs, &result, 0); ++ } ++#endif ++ return 0; ++ ++fail: ++ g_free(sn_l1_table); ++ return ret; ++} ++ ++int qcow2_snapshot_delete(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot sn; ++ int snapshot_index, ret; ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ ++ /* Search the snapshot */ ++ snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); ++ if (snapshot_index < 0) { ++ error_setg(errp, "Can't find the snapshot"); ++ return -ENOENT; ++ } ++ sn = s->snapshots[snapshot_index]; ++ ++ ret = qcow2_validate_table(bs, sn.l1_table_offset, sn.l1_size, ++ L1E_SIZE, QCOW_MAX_L1_SIZE, ++ "Snapshot L1 table", errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Remove it from the snapshot list */ ++ memmove(s->snapshots + snapshot_index, ++ s->snapshots + snapshot_index + 1, ++ (s->nb_snapshots - snapshot_index - 1) * sizeof(sn)); ++ s->nb_snapshots--; ++ ret = qcow2_write_snapshots(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to remove snapshot from snapshot list"); ++ return ret; ++ } ++ ++ /* ++ * The snapshot is now unused, clean up. If we fail after this point, we ++ * won't recover but just leak clusters. ++ */ ++ g_free(sn.unknown_extra_data); ++ g_free(sn.id_str); ++ g_free(sn.name); ++ ++ /* ++ * Now decrease the refcounts of clusters referenced by the snapshot and ++ * free the L1 table. ++ */ ++ ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset, ++ sn.l1_size, -1); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to free the cluster and L1 table"); ++ return ret; ++ } ++ qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * L1E_SIZE, ++ QCOW2_DISCARD_SNAPSHOT); ++ ++ /* must update the copied flag on the current cluster offsets */ ++ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to update snapshot status in disk"); ++ return ret; ++ } ++ ++#ifdef DEBUG_ALLOC ++ { ++ BdrvCheckResult result = {0}; ++ qcow2_check_refcounts(bs, &result, 0); ++ } ++#endif ++ return 0; ++} ++ ++int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QEMUSnapshotInfo *sn_tab, *sn_info; ++ QCowSnapshot *sn; ++ int i; ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ if (!s->nb_snapshots) { ++ *psn_tab = NULL; ++ return s->nb_snapshots; ++ } ++ ++ sn_tab = g_new0(QEMUSnapshotInfo, s->nb_snapshots); ++ for(i = 0; i < s->nb_snapshots; i++) { ++ sn_info = sn_tab + i; ++ sn = s->snapshots + i; ++ pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), ++ sn->id_str); ++ pstrcpy(sn_info->name, sizeof(sn_info->name), ++ sn->name); ++ sn_info->vm_state_size = sn->vm_state_size; ++ sn_info->date_sec = sn->date_sec; ++ sn_info->date_nsec = sn->date_nsec; ++ sn_info->vm_clock_nsec = sn->vm_clock_nsec; ++ sn_info->icount = sn->icount; ++ } ++ *psn_tab = sn_tab; ++ return s->nb_snapshots; ++} ++ ++int qcow2_snapshot_load_tmp(BlockDriverState *bs, ++ const char *snapshot_id, ++ const char *name, ++ Error **errp) ++{ ++ int i, snapshot_index; ++ BDRVQcow2State *s = bs->opaque; ++ QCowSnapshot *sn; ++ uint64_t *new_l1_table; ++ int new_l1_bytes; ++ int ret; ++ ++ assert(bdrv_is_read_only(bs)); ++ ++ /* Search the snapshot */ ++ snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); ++ if (snapshot_index < 0) { ++ error_setg(errp, ++ "Can't find snapshot"); ++ return -ENOENT; ++ } ++ sn = &s->snapshots[snapshot_index]; ++ ++ /* Allocate and read in the snapshot's L1 table */ ++ ret = qcow2_validate_table(bs, sn->l1_table_offset, sn->l1_size, ++ L1E_SIZE, QCOW_MAX_L1_SIZE, ++ "Snapshot L1 table", errp); ++ if (ret < 0) { ++ return ret; ++ } ++ new_l1_bytes = sn->l1_size * L1E_SIZE; ++ new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_bytes); ++ if (new_l1_table == NULL) { ++ return -ENOMEM; ++ } ++ ++ ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_bytes, ++ new_l1_table, 0); ++ if (ret < 0) { ++ error_setg(errp, "Failed to read l1 table for snapshot"); ++ qemu_vfree(new_l1_table); ++ return ret; ++ } ++ ++ /* Switch the L1 table */ ++ qemu_vfree(s->l1_table); ++ ++ s->l1_size = sn->l1_size; ++ s->l1_table_offset = sn->l1_table_offset; ++ s->l1_table = new_l1_table; ++ ++ for(i = 0;i < s->l1_size; i++) { ++ be64_to_cpus(&s->l1_table[i]); ++ } ++ ++ return 0; ++} +diff --git a/qcow2/lib/qcow2-threads.c b/qcow2/lib/qcow2-threads.c +new file mode 100644 +index 00000000..d6071a1e +--- /dev/null ++++ b/qcow2/lib/qcow2-threads.c +@@ -0,0 +1,527 @@ ++/* ++ * Threaded data processing for Qcow2: compression, encryption ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#define ZLIB_CONST ++#include ++ ++#ifdef CONFIG_ZSTD ++#include ++#include ++#endif ++ ++#include "qcow2.h" ++#include "block/block-io.h" ++#include "block/thread-pool.h" ++#include "crypto.h" ++ ++static int coroutine_fn ++qcow2_co_process(BlockDriverState *bs, ThreadPoolFunc *func, void *arg) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ ++ qemu_co_mutex_lock(&s->lock); ++ while (s->nb_threads >= QCOW2_MAX_THREADS) { ++ qemu_co_queue_wait(&s->thread_task_queue, &s->lock); ++ } ++ s->nb_threads++; ++ qemu_co_mutex_unlock(&s->lock); ++ ++ ret = thread_pool_submit_co(func, arg); ++ ++ qemu_co_mutex_lock(&s->lock); ++ s->nb_threads--; ++ qemu_co_queue_next(&s->thread_task_queue); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ return ret; ++} ++ ++ ++/* ++ * Compression ++ */ ++ ++typedef ssize_t (*Qcow2CompressFunc)(void *dest, size_t dest_size, ++ const void *src, size_t src_size); ++typedef struct Qcow2CompressData { ++ void *dest; ++ size_t dest_size; ++ const void *src; ++ size_t src_size; ++ ssize_t ret; ++ ++ Qcow2CompressFunc func; ++} Qcow2CompressData; ++ ++/* ++ * qcow2_zlib_compress() ++ * ++ * Compress @src_size bytes of data using zlib compression method ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: compressed size on success ++ * -ENOMEM destination buffer is not enough to store compressed data ++ * -EIO on any other error ++ */ ++static ssize_t qcow2_zlib_compress(void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ ssize_t ret; ++ z_stream strm; ++ ++ /* best compression, small window, no zlib header */ ++ memset(&strm, 0, sizeof(strm)); ++ ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, ++ -12, 9, Z_DEFAULT_STRATEGY); ++ if (ret != Z_OK) { ++ return -EIO; ++ } ++ ++ /* ++ * strm.next_in is not const in old zlib versions, such as those used on ++ * OpenBSD/NetBSD, so cast the const away ++ */ ++ strm.avail_in = src_size; ++ strm.next_in = (void *) src; ++ strm.avail_out = dest_size; ++ strm.next_out = dest; ++ ++ ret = deflate(&strm, Z_FINISH); ++ if (ret == Z_STREAM_END) { ++ ret = dest_size - strm.avail_out; ++ } else { ++ ret = (ret == Z_OK ? -ENOMEM : -EIO); ++ } ++ ++ deflateEnd(&strm); ++ ++ return ret; ++} ++ ++/* ++ * qcow2_zlib_decompress() ++ * ++ * Decompress some data (not more than @src_size bytes) to produce exactly ++ * @dest_size bytes using zlib compression method ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: 0 on success ++ * -EIO on fail ++ */ ++static ssize_t qcow2_zlib_decompress(void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ int ret; ++ z_stream strm; ++ ++ memset(&strm, 0, sizeof(strm)); ++ strm.avail_in = src_size; ++ strm.next_in = (void *) src; ++ strm.avail_out = dest_size; ++ strm.next_out = dest; ++ ++ ret = inflateInit2(&strm, -12); ++ if (ret != Z_OK) { ++ return -EIO; ++ } ++ ++ ret = inflate(&strm, Z_FINISH); ++ if ((ret == Z_STREAM_END || ret == Z_BUF_ERROR) && strm.avail_out == 0) { ++ /* ++ * We approve Z_BUF_ERROR because we need @dest buffer to be filled, but ++ * @src buffer may be processed partly (because in qcow2 we know size of ++ * compressed data with precision of one sector) ++ */ ++ ret = 0; ++ } else { ++ ret = -EIO; ++ } ++ ++ inflateEnd(&strm); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_ZSTD ++ ++/* ++ * qcow2_zstd_compress() ++ * ++ * Compress @src_size bytes of data using zstd compression method ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: compressed size on success ++ * -ENOMEM destination buffer is not enough to store compressed data ++ * -EIO on any other error ++ */ ++static ssize_t qcow2_zstd_compress(void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ ssize_t ret; ++ size_t zstd_ret; ++ ZSTD_outBuffer output = { ++ .dst = dest, ++ .size = dest_size, ++ .pos = 0 ++ }; ++ ZSTD_inBuffer input = { ++ .src = src, ++ .size = src_size, ++ .pos = 0 ++ }; ++ ZSTD_CCtx *cctx = ZSTD_createCCtx(); ++ ++ if (!cctx) { ++ return -EIO; ++ } ++ /* ++ * Use the zstd streamed interface for symmetry with decompression, ++ * where streaming is essential since we don't record the exact ++ * compressed size. ++ * ++ * ZSTD_compressStream2() tries to compress everything it could ++ * with a single call. Although, ZSTD docs says that: ++ * "You must continue calling ZSTD_compressStream2() with ZSTD_e_end ++ * until it returns 0, at which point you are free to start a new frame", ++ * in out tests we saw the only case when it returned with >0 - ++ * when the output buffer was too small. In that case, ++ * ZSTD_compressStream2() expects a bigger buffer on the next call. ++ * We can't provide a bigger buffer because we are limited with dest_size ++ * which we pass to the ZSTD_compressStream2() at once. ++ * So, we don't need any loops and just abort the compression when we ++ * don't get 0 result on the first call. ++ */ ++ zstd_ret = ZSTD_compressStream2(cctx, &output, &input, ZSTD_e_end); ++ ++ if (zstd_ret) { ++ if (zstd_ret > output.size - output.pos) { ++ ret = -ENOMEM; ++ } else { ++ ret = -EIO; ++ } ++ goto out; ++ } ++ ++ /* make sure that zstd didn't overflow the dest buffer */ ++ assert(output.pos <= dest_size); ++ ret = output.pos; ++out: ++ ZSTD_freeCCtx(cctx); ++ return ret; ++} ++ ++/* ++ * qcow2_zstd_decompress() ++ * ++ * Decompress some data (not more than @src_size bytes) to produce exactly ++ * @dest_size bytes using zstd compression method ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: 0 on success ++ * -EIO on any error ++ */ ++static ssize_t qcow2_zstd_decompress(void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ size_t zstd_ret = 0; ++ ssize_t ret = 0; ++ ZSTD_outBuffer output = { ++ .dst = dest, ++ .size = dest_size, ++ .pos = 0 ++ }; ++ ZSTD_inBuffer input = { ++ .src = src, ++ .size = src_size, ++ .pos = 0 ++ }; ++ ZSTD_DCtx *dctx = ZSTD_createDCtx(); ++ ++ if (!dctx) { ++ return -EIO; ++ } ++ ++ /* ++ * The compressed stream from the input buffer may consist of more ++ * than one zstd frame. So we iterate until we get a fully ++ * uncompressed cluster. ++ * From zstd docs related to ZSTD_decompressStream: ++ * "return : 0 when a frame is completely decoded and fully flushed" ++ * We suppose that this means: each time ZSTD_decompressStream reads ++ * only ONE full frame and returns 0 if and only if that frame ++ * is completely decoded and flushed. Only after returning 0, ++ * ZSTD_decompressStream reads another ONE full frame. ++ */ ++ while (output.pos < output.size) { ++ size_t last_in_pos = input.pos; ++ size_t last_out_pos = output.pos; ++ zstd_ret = ZSTD_decompressStream(dctx, &output, &input); ++ ++ if (ZSTD_isError(zstd_ret)) { ++ ret = -EIO; ++ break; ++ } ++ ++ /* ++ * The ZSTD manual is vague about what to do if it reads ++ * the buffer partially, and we don't want to get stuck ++ * in an infinite loop where ZSTD_decompressStream ++ * returns > 0 waiting for another input chunk. So, we add ++ * a check which ensures that the loop makes some progress ++ * on each step. ++ */ ++ if (last_in_pos >= input.pos && ++ last_out_pos >= output.pos) { ++ ret = -EIO; ++ break; ++ } ++ } ++ /* ++ * Make sure that we have the frame fully flushed here ++ * if not, we somehow managed to get uncompressed cluster ++ * greater then the cluster size, possibly because of its ++ * damage. ++ */ ++ if (zstd_ret > 0) { ++ ret = -EIO; ++ } ++ ++ ZSTD_freeDCtx(dctx); ++ assert(ret == 0 || ret == -EIO); ++ return ret; ++} ++#endif ++ ++static int qcow2_compress_pool_func(void *opaque) ++{ ++ Qcow2CompressData *data = opaque; ++ ++ data->ret = data->func(data->dest, data->dest_size, ++ data->src, data->src_size); ++ ++ return 0; ++} ++ ++static ssize_t coroutine_fn ++qcow2_co_do_compress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size, Qcow2CompressFunc func) ++{ ++ Qcow2CompressData arg = { ++ .dest = dest, ++ .dest_size = dest_size, ++ .src = src, ++ .src_size = src_size, ++ .func = func, ++ }; ++ ++ qcow2_co_process(bs, qcow2_compress_pool_func, &arg); ++ ++ return arg.ret; ++} ++ ++/* ++ * qcow2_co_compress() ++ * ++ * Compress @src_size bytes of data using the compression ++ * method defined by the image compression type ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: compressed size on success ++ * a negative error code on failure ++ */ ++ssize_t coroutine_fn ++qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2CompressFunc fn; ++ ++ switch (s->compression_type) { ++ case QCOW2_COMPRESSION_TYPE_ZLIB: ++ fn = qcow2_zlib_compress; ++ break; ++ ++#ifdef CONFIG_ZSTD ++ case QCOW2_COMPRESSION_TYPE_ZSTD: ++ fn = qcow2_zstd_compress; ++ break; ++#endif ++ default: ++ abort(); ++ } ++ ++ return qcow2_co_do_compress(bs, dest, dest_size, src, src_size, fn); ++} ++ ++/* ++ * qcow2_co_decompress() ++ * ++ * Decompress some data (not more than @src_size bytes) to produce exactly ++ * @dest_size bytes using the compression method defined by the image ++ * compression type ++ * ++ * @dest - destination buffer, @dest_size bytes ++ * @src - source buffer, @src_size bytes ++ * ++ * Returns: 0 on success ++ * a negative error code on failure ++ */ ++ssize_t coroutine_fn ++qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, ++ const void *src, size_t src_size) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2CompressFunc fn; ++ ++ switch (s->compression_type) { ++ case QCOW2_COMPRESSION_TYPE_ZLIB: ++ fn = qcow2_zlib_decompress; ++ break; ++ ++#ifdef CONFIG_ZSTD ++ case QCOW2_COMPRESSION_TYPE_ZSTD: ++ fn = qcow2_zstd_decompress; ++ break; ++#endif ++ default: ++ abort(); ++ } ++ ++ return qcow2_co_do_compress(bs, dest, dest_size, src, src_size, fn); ++} ++ ++ ++/* ++ * Cryptography ++ */ ++ ++/* ++ * Qcow2EncDecFunc: common prototype of qcrypto_block_encrypt() and ++ * qcrypto_block_decrypt() functions. ++ */ ++typedef int (*Qcow2EncDecFunc)(QCryptoBlock *block, uint64_t offset, ++ uint8_t *buf, size_t len, Error **errp); ++ ++typedef struct Qcow2EncDecData { ++ QCryptoBlock *block; ++ uint64_t offset; ++ uint8_t *buf; ++ size_t len; ++ ++ Qcow2EncDecFunc func; ++} Qcow2EncDecData; ++ ++static int qcow2_encdec_pool_func(void *opaque) ++{ ++ Qcow2EncDecData *data = opaque; ++ ++ return data->func(data->block, data->offset, data->buf, data->len, NULL); ++} ++ ++static int coroutine_fn ++qcow2_co_encdec(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len, ++ Qcow2EncDecFunc func) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2EncDecData arg = { ++ .block = s->crypto, ++ .offset = s->crypt_physical_offset ? host_offset : guest_offset, ++ .buf = buf, ++ .len = len, ++ .func = func, ++ }; ++ uint64_t sector_size; ++ ++ assert(s->crypto); ++ ++ sector_size = qcrypto_block_get_sector_size(s->crypto); ++ assert(QEMU_IS_ALIGNED(guest_offset, sector_size)); ++ assert(QEMU_IS_ALIGNED(host_offset, sector_size)); ++ assert(QEMU_IS_ALIGNED(len, sector_size)); ++ ++ return len == 0 ? 0 : qcow2_co_process(bs, qcow2_encdec_pool_func, &arg); ++} ++ ++/* ++ * qcow2_co_encrypt() ++ * ++ * Encrypts one or more contiguous aligned sectors ++ * ++ * @host_offset - underlying storage offset of the first sector of the ++ * data to be encrypted ++ * ++ * @guest_offset - guest (virtual) offset of the first sector of the ++ * data to be encrypted ++ * ++ * @buf - buffer with the data to encrypt, that after encryption ++ * will be written to the underlying storage device at ++ * @host_offset ++ * ++ * @len - length of the buffer (must be a multiple of the encryption ++ * sector size) ++ * ++ * Depending on the encryption method, @host_offset and/or @guest_offset ++ * may be used for generating the initialization vector for ++ * encryption. ++ * ++ * Note that while the whole range must be aligned on sectors, it ++ * does not have to be aligned on clusters and can also cross cluster ++ * boundaries ++ */ ++int coroutine_fn ++qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len) ++{ ++ return qcow2_co_encdec(bs, host_offset, guest_offset, buf, len, ++ qcrypto_block_encrypt); ++} ++ ++/* ++ * qcow2_co_decrypt() ++ * ++ * Decrypts one or more contiguous aligned sectors ++ * Similar to qcow2_co_encrypt ++ */ ++int coroutine_fn ++qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t guest_offset, void *buf, size_t len) ++{ ++ return qcow2_co_encdec(bs, host_offset, guest_offset, buf, len, ++ qcrypto_block_decrypt); ++} +diff --git a/qcow2/lib/qcow2.c b/qcow2/lib/qcow2.c +new file mode 100644 +index 00000000..70b19730 +--- /dev/null ++++ b/qcow2/lib/qcow2.c +@@ -0,0 +1,6214 @@ ++/* ++ * Block driver for the QCOW version 2 format ++ * ++ * Copyright (c) 2004-2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "block/qdict.h" ++#include "sysemu/block-backend.h" ++#include "qemu/main-loop.h" ++#include "qemu/module.h" ++#include "qcow2.h" ++#include "qemu/error-report.h" ++#include "qapi/error.h" ++#include "qapi/qapi-events-block-core.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qstring.h" ++#include "trace.h" ++#include "qemu/option_int.h" ++#include "qemu/cutils.h" ++#include "qemu/bswap.h" ++#include "qemu/memalign.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "crypto.h" ++#include "block/aio_task.h" ++#include "block/dirty-bitmap.h" ++ ++/* ++ Differences with QCOW: ++ ++ - Support for multiple incremental snapshots. ++ - Memory management by reference counts. ++ - Clusters which have a reference count of one have the bit ++ QCOW_OFLAG_COPIED to optimize write performance. ++ - Size of compressed clusters is stored in sectors to reduce bit usage ++ in the cluster offsets. ++ - Support for storing additional data (such as the VM state) in the ++ snapshots. ++ - If a backing store is used, the cluster size is not constrained ++ (could be backported to QCOW). ++ - L2 tables have always a size of one cluster. ++*/ ++ ++ ++typedef struct { ++ uint32_t magic; ++ uint32_t len; ++} QEMU_PACKED QCowExtension; ++ ++#define QCOW2_EXT_MAGIC_END 0 ++#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xe2792aca ++#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857 ++#define QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77 ++#define QCOW2_EXT_MAGIC_BITMAPS 0x23852875 ++#define QCOW2_EXT_MAGIC_DATA_FILE 0x44415441 ++ ++static int coroutine_fn ++qcow2_co_preadv_compressed(BlockDriverState *bs, ++ uint64_t l2_entry, ++ uint64_t offset, ++ uint64_t bytes, ++ QEMUIOVector *qiov, ++ size_t qiov_offset); ++ ++static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) ++{ ++ const QCowHeader *cow_header = (const void *)buf; ++ ++ if (buf_size >= sizeof(QCowHeader) && ++ be32_to_cpu(cow_header->magic) == QCOW_MAGIC && ++ be32_to_cpu(cow_header->version) >= 2) ++ return 100; ++ else ++ return 0; ++} ++ ++ ++static int GRAPH_RDLOCK ++qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, ++ uint8_t *buf, size_t buflen, ++ void *opaque, Error **errp) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVQcow2State *s = bs->opaque; ++ ssize_t ret; ++ ++ if ((offset + buflen) > s->crypto_header.length) { ++ error_setg(errp, "Request for data outside of extension header"); ++ return -1; ++ } ++ ++ ret = bdrv_pread(bs->file, s->crypto_header.offset + offset, buflen, buf, ++ 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read encryption header"); ++ return -1; ++ } ++ return 0; ++} ++ ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, void *opaque, ++ Error **errp) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVQcow2State *s = bs->opaque; ++ int64_t ret; ++ int64_t clusterlen; ++ ++ ret = qcow2_alloc_clusters(bs, headerlen); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Cannot allocate cluster for LUKS header size %zu", ++ headerlen); ++ return -1; ++ } ++ ++ s->crypto_header.length = headerlen; ++ s->crypto_header.offset = ret; ++ ++ /* ++ * Zero fill all space in cluster so it has predictable ++ * content, as we may not initialize some regions of the ++ * header (eg only 1 out of 8 key slots will be initialized) ++ */ ++ clusterlen = size_to_clusters(s, headerlen) * s->cluster_size; ++ assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0); ++ ret = bdrv_co_pwrite_zeroes(bs->file, ret, clusterlen, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not zero fill encryption header"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++/* The graph lock must be held when called in coroutine context */ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset, ++ const uint8_t *buf, size_t buflen, ++ void *opaque, Error **errp) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVQcow2State *s = bs->opaque; ++ ssize_t ret; ++ ++ if ((offset + buflen) > s->crypto_header.length) { ++ error_setg(errp, "Request for data outside of extension header"); ++ return -1; ++ } ++ ++ ret = bdrv_pwrite(bs->file, s->crypto_header.offset + offset, buflen, buf, ++ 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not read encryption header"); ++ return -1; ++ } ++ return 0; ++} ++ ++static QDict* ++qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp) ++{ ++ QDict *cryptoopts_qdict; ++ QDict *opts_qdict; ++ ++ /* Extract "encrypt." options into a qdict */ ++ opts_qdict = qemu_opts_to_qdict(opts, NULL); ++ qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt."); ++ qobject_unref(opts_qdict); ++ qdict_put_str(cryptoopts_qdict, "format", fmt); ++ return cryptoopts_qdict; ++} ++ ++/* ++ * read qcow2 extension and fill bs ++ * start reading from start_offset ++ * finish reading upon magic of value 0 or when end_offset reached ++ * unknown magic is skipped (future extension this version knows nothing about) ++ * return 0 upon success, non-0 otherwise ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, ++ uint64_t end_offset, void **p_feature_table, ++ int flags, bool *need_update_header, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowExtension ext; ++ uint64_t offset; ++ int ret; ++ Qcow2BitmapHeaderExt bitmaps_ext; ++ ++ if (need_update_header != NULL) { ++ *need_update_header = false; ++ } ++ ++#ifdef DEBUG_EXT ++ printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset); ++#endif ++ offset = start_offset; ++ while (offset < end_offset) { ++ ++#ifdef DEBUG_EXT ++ /* Sanity check */ ++ if (offset > s->cluster_size) ++ printf("qcow2_read_extension: suspicious offset %lu\n", offset); ++ ++ printf("attempting to read extended header in offset %lu\n", offset); ++#endif ++ ++ ret = bdrv_co_pread(bs->file, offset, sizeof(ext), &ext, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: " ++ "pread fail from offset %" PRIu64, offset); ++ return 1; ++ } ++ ext.magic = be32_to_cpu(ext.magic); ++ ext.len = be32_to_cpu(ext.len); ++ offset += sizeof(ext); ++#ifdef DEBUG_EXT ++ printf("ext.magic = 0x%x\n", ext.magic); ++#endif ++ if (offset > end_offset || ext.len > end_offset - offset) { ++ error_setg(errp, "Header extension too large"); ++ return -EINVAL; ++ } ++ ++ switch (ext.magic) { ++ case QCOW2_EXT_MAGIC_END: ++ return 0; ++ ++ case QCOW2_EXT_MAGIC_BACKING_FORMAT: ++ if (ext.len >= sizeof(bs->backing_format)) { ++ error_setg(errp, "ERROR: ext_backing_format: len=%" PRIu32 ++ " too large (>=%zu)", ext.len, ++ sizeof(bs->backing_format)); ++ return 2; ++ } ++ ret = bdrv_co_pread(bs->file, offset, ext.len, bs->backing_format, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR: ext_backing_format: " ++ "Could not read format name"); ++ return 3; ++ } ++ bs->backing_format[ext.len] = '\0'; ++ s->image_backing_format = g_strdup(bs->backing_format); ++#ifdef DEBUG_EXT ++ printf("Qcow2: Got format extension %s\n", bs->backing_format); ++#endif ++ break; ++ ++ case QCOW2_EXT_MAGIC_FEATURE_TABLE: ++ if (p_feature_table != NULL) { ++ void *feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); ++ ret = bdrv_co_pread(bs->file, offset, ext.len, feature_table, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR: ext_feature_table: " ++ "Could not read table"); ++ g_free(feature_table); ++ return ret; ++ } ++ ++ *p_feature_table = feature_table; ++ } ++ break; ++ ++ case QCOW2_EXT_MAGIC_CRYPTO_HEADER: { ++ unsigned int cflags = 0; ++ if (s->crypt_method_header != QCOW_CRYPT_LUKS) { ++ error_setg(errp, "CRYPTO header extension only " ++ "expected with LUKS encryption method"); ++ return -EINVAL; ++ } ++ if (ext.len != sizeof(Qcow2CryptoHeaderExtension)) { ++ error_setg(errp, "CRYPTO header extension size %u, " ++ "but expected size %zu", ext.len, ++ sizeof(Qcow2CryptoHeaderExtension)); ++ return -EINVAL; ++ } ++ ++ ret = bdrv_co_pread(bs->file, offset, ext.len, &s->crypto_header, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Unable to read CRYPTO header extension"); ++ return ret; ++ } ++ s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset); ++ s->crypto_header.length = be64_to_cpu(s->crypto_header.length); ++ ++ if ((s->crypto_header.offset % s->cluster_size) != 0) { ++ error_setg(errp, "Encryption header offset '%" PRIu64 "' is " ++ "not a multiple of cluster size '%u'", ++ s->crypto_header.offset, s->cluster_size); ++ return -EINVAL; ++ } ++ ++ if (flags & BDRV_O_NO_IO) { ++ cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; ++ } ++ s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", ++ qcow2_crypto_hdr_read_func, ++ bs, cflags, errp); ++ if (!s->crypto) { ++ return -EINVAL; ++ } ++ } break; ++ ++ case QCOW2_EXT_MAGIC_BITMAPS: ++ if (ext.len != sizeof(bitmaps_ext)) { ++ error_setg_errno(errp, -ret, "bitmaps_ext: " ++ "Invalid extension length"); ++ return -EINVAL; ++ } ++ ++ if (!(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) { ++ if (s->qcow_version < 3) { ++ /* Let's be a bit more specific */ ++ warn_report("This qcow2 v2 image contains bitmaps, but " ++ "they may have been modified by a program " ++ "without persistent bitmap support; so now " ++ "they must all be considered inconsistent"); ++ } else { ++ warn_report("a program lacking bitmap support " ++ "modified this file, so all bitmaps are now " ++ "considered inconsistent"); ++ } ++ error_printf("Some clusters may be leaked, " ++ "run 'qemu-img check -r' on the image " ++ "file to fix."); ++ if (need_update_header != NULL) { ++ /* Updating is needed to drop invalid bitmap extension. */ ++ *need_update_header = true; ++ } ++ break; ++ } ++ ++ ret = bdrv_co_pread(bs->file, offset, ext.len, &bitmaps_ext, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "bitmaps_ext: " ++ "Could not read ext header"); ++ return ret; ++ } ++ ++ if (bitmaps_ext.reserved32 != 0) { ++ error_setg_errno(errp, -ret, "bitmaps_ext: " ++ "Reserved field is not zero"); ++ return -EINVAL; ++ } ++ ++ bitmaps_ext.nb_bitmaps = be32_to_cpu(bitmaps_ext.nb_bitmaps); ++ bitmaps_ext.bitmap_directory_size = ++ be64_to_cpu(bitmaps_ext.bitmap_directory_size); ++ bitmaps_ext.bitmap_directory_offset = ++ be64_to_cpu(bitmaps_ext.bitmap_directory_offset); ++ ++ if (bitmaps_ext.nb_bitmaps > QCOW2_MAX_BITMAPS) { ++ error_setg(errp, ++ "bitmaps_ext: Image has %" PRIu32 " bitmaps, " ++ "exceeding the QEMU supported maximum of %d", ++ bitmaps_ext.nb_bitmaps, QCOW2_MAX_BITMAPS); ++ return -EINVAL; ++ } ++ ++ if (bitmaps_ext.nb_bitmaps == 0) { ++ error_setg(errp, "found bitmaps extension with zero bitmaps"); ++ return -EINVAL; ++ } ++ ++ if (offset_into_cluster(s, bitmaps_ext.bitmap_directory_offset)) { ++ error_setg(errp, "bitmaps_ext: " ++ "invalid bitmap directory offset"); ++ return -EINVAL; ++ } ++ ++ if (bitmaps_ext.bitmap_directory_size > ++ QCOW2_MAX_BITMAP_DIRECTORY_SIZE) { ++ error_setg(errp, "bitmaps_ext: " ++ "bitmap directory size (%" PRIu64 ") exceeds " ++ "the maximum supported size (%d)", ++ bitmaps_ext.bitmap_directory_size, ++ QCOW2_MAX_BITMAP_DIRECTORY_SIZE); ++ return -EINVAL; ++ } ++ ++ s->nb_bitmaps = bitmaps_ext.nb_bitmaps; ++ s->bitmap_directory_offset = ++ bitmaps_ext.bitmap_directory_offset; ++ s->bitmap_directory_size = ++ bitmaps_ext.bitmap_directory_size; ++ ++#ifdef DEBUG_EXT ++ printf("Qcow2: Got bitmaps extension: " ++ "offset=%" PRIu64 " nb_bitmaps=%" PRIu32 "\n", ++ s->bitmap_directory_offset, s->nb_bitmaps); ++#endif ++ break; ++ ++ case QCOW2_EXT_MAGIC_DATA_FILE: ++ { ++ s->image_data_file = g_malloc0(ext.len + 1); ++ ret = bdrv_co_pread(bs->file, offset, ext.len, s->image_data_file, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "ERROR: Could not read data file name"); ++ return ret; ++ } ++#ifdef DEBUG_EXT ++ printf("Qcow2: Got external data file %s\n", s->image_data_file); ++#endif ++ break; ++ } ++ ++ default: ++ /* unknown magic - save it in case we need to rewrite the header */ ++ /* If you add a new feature, make sure to also update the fast ++ * path of qcow2_make_empty() to deal with it. */ ++ { ++ Qcow2UnknownHeaderExtension *uext; ++ ++ uext = g_malloc0(sizeof(*uext) + ext.len); ++ uext->magic = ext.magic; ++ uext->len = ext.len; ++ QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next); ++ ++ ret = bdrv_co_pread(bs->file, offset, uext->len, uext->data, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "ERROR: unknown extension: " ++ "Could not read data"); ++ return ret; ++ } ++ } ++ break; ++ } ++ ++ offset += ((ext.len + 7) & ~7); ++ } ++ ++ return 0; ++} ++ ++static void cleanup_unknown_header_ext(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Qcow2UnknownHeaderExtension *uext, *next; ++ ++ QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) { ++ QLIST_REMOVE(uext, next); ++ g_free(uext); ++ } ++} ++ ++static void report_unsupported_feature(Error **errp, Qcow2Feature *table, ++ uint64_t mask) ++{ ++ g_autoptr(GString) features = g_string_sized_new(60); ++ ++ while (table && table->name[0] != '\0') { ++ if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { ++ if (mask & (1ULL << table->bit)) { ++ if (features->len > 0) { ++ g_string_append(features, ", "); ++ } ++ g_string_append_printf(features, "%.46s", table->name); ++ mask &= ~(1ULL << table->bit); ++ } ++ } ++ table++; ++ } ++ ++ if (mask) { ++ if (features->len > 0) { ++ g_string_append(features, ", "); ++ } ++ g_string_append_printf(features, ++ "Unknown incompatible feature: %" PRIx64, mask); ++ } ++ ++ error_setg(errp, "Unsupported qcow2 feature(s): %s", features->str); ++} ++ ++/* ++ * Sets the dirty bit and flushes afterwards if necessary. ++ * ++ * The incompatible_features bit is only set if the image file header was ++ * updated successfully. Therefore it is not required to check the return ++ * value of this function. ++ */ ++int qcow2_mark_dirty(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t val; ++ int ret; ++ ++ assert(s->qcow_version >= 3); ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ++ return 0; /* already dirty */ ++ } ++ ++ val = cpu_to_be64(s->incompatible_features | QCOW2_INCOMPAT_DIRTY); ++ ret = bdrv_pwrite_sync(bs->file, ++ offsetof(QCowHeader, incompatible_features), ++ sizeof(val), &val, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Only treat image as dirty if the header was updated successfully */ ++ s->incompatible_features |= QCOW2_INCOMPAT_DIRTY; ++ return 0; ++} ++ ++/* ++ * Clears the dirty bit and flushes before if necessary. Only call this ++ * function when there are no pending requests, it does not guard against ++ * concurrent requests dirtying the image. ++ */ ++static int GRAPH_RDLOCK qcow2_mark_clean(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ++ int ret; ++ ++ s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY; ++ ++ ret = qcow2_flush_caches(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return qcow2_update_header(bs); ++ } ++ return 0; ++} ++ ++/* ++ * Marks the image as corrupt. ++ */ ++int qcow2_mark_corrupt(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT; ++ return qcow2_update_header(bs); ++} ++ ++/* ++ * Marks the image as consistent, i.e., unsets the corrupt bit, and flushes ++ * before if necessary. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_mark_consistent(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { ++ int ret = qcow2_flush_caches(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ s->incompatible_features &= ~QCOW2_INCOMPAT_CORRUPT; ++ return qcow2_update_header(bs); ++ } ++ return 0; ++} ++ ++static void qcow2_add_check_result(BdrvCheckResult *out, ++ const BdrvCheckResult *src, ++ bool set_allocation_info) ++{ ++ out->corruptions += src->corruptions; ++ out->leaks += src->leaks; ++ out->check_errors += src->check_errors; ++ out->corruptions_fixed += src->corruptions_fixed; ++ out->leaks_fixed += src->leaks_fixed; ++ ++ if (set_allocation_info) { ++ out->image_end_offset = src->image_end_offset; ++ out->bfi = src->bfi; ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_check_locked(BlockDriverState *bs, BdrvCheckResult *result, ++ BdrvCheckMode fix) ++{ ++ BdrvCheckResult snapshot_res = {}; ++ BdrvCheckResult refcount_res = {}; ++ int ret; ++ ++ memset(result, 0, sizeof(*result)); ++ ++ ret = qcow2_check_read_snapshot_table(bs, &snapshot_res, fix); ++ if (ret < 0) { ++ qcow2_add_check_result(result, &snapshot_res, false); ++ return ret; ++ } ++ ++ ret = qcow2_check_refcounts(bs, &refcount_res, fix); ++ qcow2_add_check_result(result, &refcount_res, true); ++ if (ret < 0) { ++ qcow2_add_check_result(result, &snapshot_res, false); ++ return ret; ++ } ++ ++ ret = qcow2_check_fix_snapshot_table(bs, &snapshot_res, fix); ++ qcow2_add_check_result(result, &snapshot_res, false); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (fix && result->check_errors == 0 && result->corruptions == 0) { ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ return ret; ++ } ++ return qcow2_mark_consistent(bs); ++ } ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_check(BlockDriverState *bs, BdrvCheckResult *result, ++ BdrvCheckMode fix) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_co_check_locked(bs, result, fix); ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; ++} ++ ++int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, ++ uint64_t entries, size_t entry_len, ++ int64_t max_size_bytes, const char *table_name, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (entries > max_size_bytes / entry_len) { ++ error_setg(errp, "%s too large", table_name); ++ return -EFBIG; ++ } ++ ++ /* Use signed INT64_MAX as the maximum even for uint64_t header fields, ++ * because values will be passed to qemu functions taking int64_t. */ ++ if ((INT64_MAX - entries * entry_len < offset) || ++ (offset_into_cluster(s, offset) != 0)) { ++ error_setg(errp, "%s offset invalid", table_name); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const char *const mutable_opts[] = { ++ QCOW2_OPT_LAZY_REFCOUNTS, ++ QCOW2_OPT_DISCARD_REQUEST, ++ QCOW2_OPT_DISCARD_SNAPSHOT, ++ QCOW2_OPT_DISCARD_OTHER, ++ QCOW2_OPT_DISCARD_NO_UNREF, ++ QCOW2_OPT_OVERLAP, ++ QCOW2_OPT_OVERLAP_TEMPLATE, ++ QCOW2_OPT_OVERLAP_MAIN_HEADER, ++ QCOW2_OPT_OVERLAP_ACTIVE_L1, ++ QCOW2_OPT_OVERLAP_ACTIVE_L2, ++ QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, ++ QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, ++ QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, ++ QCOW2_OPT_OVERLAP_INACTIVE_L1, ++ QCOW2_OPT_OVERLAP_INACTIVE_L2, ++ QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, ++ QCOW2_OPT_CACHE_SIZE, ++ QCOW2_OPT_L2_CACHE_SIZE, ++ QCOW2_OPT_L2_CACHE_ENTRY_SIZE, ++ QCOW2_OPT_REFCOUNT_CACHE_SIZE, ++ QCOW2_OPT_CACHE_CLEAN_INTERVAL, ++ NULL ++}; ++ ++static QemuOptsList qcow2_runtime_opts = { ++ .name = "qcow2", ++ .head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head), ++ .desc = { ++ { ++ .name = QCOW2_OPT_LAZY_REFCOUNTS, ++ .type = QEMU_OPT_BOOL, ++ .help = "Postpone refcount updates", ++ }, ++ { ++ .name = QCOW2_OPT_DISCARD_REQUEST, ++ .type = QEMU_OPT_BOOL, ++ .help = "Pass guest discard requests to the layer below", ++ }, ++ { ++ .name = QCOW2_OPT_DISCARD_SNAPSHOT, ++ .type = QEMU_OPT_BOOL, ++ .help = "Generate discard requests when snapshot related space " ++ "is freed", ++ }, ++ { ++ .name = QCOW2_OPT_DISCARD_OTHER, ++ .type = QEMU_OPT_BOOL, ++ .help = "Generate discard requests when other clusters are freed", ++ }, ++ { ++ .name = QCOW2_OPT_DISCARD_NO_UNREF, ++ .type = QEMU_OPT_BOOL, ++ .help = "Do not unreference discarded clusters", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP, ++ .type = QEMU_OPT_STRING, ++ .help = "Selects which overlap checks to perform from a range of " ++ "templates (none, constant, cached, all)", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_TEMPLATE, ++ .type = QEMU_OPT_STRING, ++ .help = "Selects which overlap checks to perform from a range of " ++ "templates (none, constant, cached, all)", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_MAIN_HEADER, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the main qcow2 header", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_ACTIVE_L1, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the active L1 table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_ACTIVE_L2, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into an active L2 table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the refcount table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into a refcount block", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the snapshot table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_INACTIVE_L1, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into an inactive L1 table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_INACTIVE_L2, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into an inactive L2 table", ++ }, ++ { ++ .name = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, ++ .type = QEMU_OPT_BOOL, ++ .help = "Check for unintended writes into the bitmap directory", ++ }, ++ { ++ .name = QCOW2_OPT_CACHE_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Maximum combined metadata (L2 tables and refcount blocks) " ++ "cache size", ++ }, ++ { ++ .name = QCOW2_OPT_L2_CACHE_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Maximum L2 table cache size", ++ }, ++ { ++ .name = QCOW2_OPT_L2_CACHE_ENTRY_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Size of each entry in the L2 cache", ++ }, ++ { ++ .name = QCOW2_OPT_REFCOUNT_CACHE_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Maximum refcount block cache size", ++ }, ++ { ++ .name = QCOW2_OPT_CACHE_CLEAN_INTERVAL, ++ .type = QEMU_OPT_NUMBER, ++ .help = "Clean unused cache entries after this time (in seconds)", ++ }, ++ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", ++ "ID of secret providing qcow2 AES key or LUKS passphrase"), ++ { /* end of list */ } ++ }, ++}; ++ ++static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = { ++ [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER, ++ [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1, ++ [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2, ++ [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, ++ [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, ++ [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, ++ [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1, ++ [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, ++ [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, ++}; ++ ++static void cache_clean_timer_cb(void *opaque) ++{ ++ BlockDriverState *bs = opaque; ++ BDRVQcow2State *s = bs->opaque; ++ qcow2_cache_clean_unused(s->l2_table_cache); ++ qcow2_cache_clean_unused(s->refcount_block_cache); ++ timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + ++ (int64_t) s->cache_clean_interval * 1000); ++} ++ ++static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ if (s->cache_clean_interval > 0) { ++ s->cache_clean_timer = ++ aio_timer_new_with_attrs(context, QEMU_CLOCK_VIRTUAL, ++ SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL, ++ cache_clean_timer_cb, bs); ++ timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + ++ (int64_t) s->cache_clean_interval * 1000); ++ } ++} ++ ++static void cache_clean_timer_del(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ if (s->cache_clean_timer) { ++ timer_free(s->cache_clean_timer); ++ s->cache_clean_timer = NULL; ++ } ++} ++ ++static void qcow2_detach_aio_context(BlockDriverState *bs) ++{ ++ cache_clean_timer_del(bs); ++} ++ ++static void qcow2_attach_aio_context(BlockDriverState *bs, ++ AioContext *new_context) ++{ ++ cache_clean_timer_init(bs, new_context); ++} ++ ++static bool read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, ++ uint64_t *l2_cache_size, ++ uint64_t *l2_cache_entry_size, ++ uint64_t *refcount_cache_size, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t combined_cache_size, l2_cache_max_setting; ++ bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; ++ bool l2_cache_entry_size_set; ++ int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; ++ uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ uint64_t max_l2_entries = DIV_ROUND_UP(virtual_disk_size, s->cluster_size); ++ /* An L2 table is always one cluster in size so the max cache size ++ * should be a multiple of the cluster size. */ ++ uint64_t max_l2_cache = ROUND_UP(max_l2_entries * l2_entry_size(s), ++ s->cluster_size); ++ ++ combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE); ++ l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE); ++ refcount_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE); ++ l2_cache_entry_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE); ++ ++ combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0); ++ l2_cache_max_setting = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE, ++ DEFAULT_L2_CACHE_MAX_SIZE); ++ *refcount_cache_size = qemu_opt_get_size(opts, ++ QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0); ++ ++ *l2_cache_entry_size = qemu_opt_get_size( ++ opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size); ++ ++ *l2_cache_size = MIN(max_l2_cache, l2_cache_max_setting); ++ ++ if (combined_cache_size_set) { ++ if (l2_cache_size_set && refcount_cache_size_set) { ++ error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE ++ " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " ++ "at the same time"); ++ return false; ++ } else if (l2_cache_size_set && ++ (l2_cache_max_setting > combined_cache_size)) { ++ error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " ++ QCOW2_OPT_CACHE_SIZE); ++ return false; ++ } else if (*refcount_cache_size > combined_cache_size) { ++ error_setg(errp, QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not exceed " ++ QCOW2_OPT_CACHE_SIZE); ++ return false; ++ } ++ ++ if (l2_cache_size_set) { ++ *refcount_cache_size = combined_cache_size - *l2_cache_size; ++ } else if (refcount_cache_size_set) { ++ *l2_cache_size = combined_cache_size - *refcount_cache_size; ++ } else { ++ /* Assign as much memory as possible to the L2 cache, and ++ * use the remainder for the refcount cache */ ++ if (combined_cache_size >= max_l2_cache + min_refcount_cache) { ++ *l2_cache_size = max_l2_cache; ++ *refcount_cache_size = combined_cache_size - *l2_cache_size; ++ } else { ++ *refcount_cache_size = ++ MIN(combined_cache_size, min_refcount_cache); ++ *l2_cache_size = combined_cache_size - *refcount_cache_size; ++ } ++ } ++ } ++ ++ /* ++ * If the L2 cache is not enough to cover the whole disk then ++ * default to 4KB entries. Smaller entries reduce the cost of ++ * loads and evictions and increase I/O performance. ++ */ ++ if (*l2_cache_size < max_l2_cache && !l2_cache_entry_size_set) { ++ *l2_cache_entry_size = MIN(s->cluster_size, 4096); ++ } ++ ++ /* l2_cache_size and refcount_cache_size are ensured to have at least ++ * their minimum values in qcow2_update_options_prepare() */ ++ ++ if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) || ++ *l2_cache_entry_size > s->cluster_size || ++ !is_power_of_2(*l2_cache_entry_size)) { ++ error_setg(errp, "L2 cache entry size must be a power of two " ++ "between %d and the cluster size (%d)", ++ 1 << MIN_CLUSTER_BITS, s->cluster_size); ++ return false; ++ } ++ ++ return true; ++} ++ ++typedef struct Qcow2ReopenState { ++ Qcow2Cache *l2_table_cache; ++ Qcow2Cache *refcount_block_cache; ++ int l2_slice_size; /* Number of entries in a slice of the L2 table */ ++ bool use_lazy_refcounts; ++ int overlap_check; ++ bool discard_passthrough[QCOW2_DISCARD_MAX]; ++ bool discard_no_unref; ++ uint64_t cache_clean_interval; ++ QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ ++} Qcow2ReopenState; ++ ++static int GRAPH_RDLOCK ++qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, ++ QDict *options, int flags, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QemuOpts *opts = NULL; ++ const char *opt_overlap_check, *opt_overlap_check_template; ++ int overlap_check_template = 0; ++ uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size; ++ int i; ++ const char *encryptfmt; ++ QDict *encryptopts = NULL; ++ int ret; ++ ++ qdict_extract_subqdict(options, &encryptopts, "encrypt."); ++ encryptfmt = qdict_get_try_str(encryptopts, "format"); ++ ++ opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_absorb_qdict(opts, options, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* get L2 table/refcount block cache size from command line options */ ++ if (!read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size, ++ &refcount_cache_size, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ l2_cache_size /= l2_cache_entry_size; ++ if (l2_cache_size < MIN_L2_CACHE_SIZE) { ++ l2_cache_size = MIN_L2_CACHE_SIZE; ++ } ++ if (l2_cache_size > INT_MAX) { ++ error_setg(errp, "L2 cache size too big"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ refcount_cache_size /= s->cluster_size; ++ if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) { ++ refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE; ++ } ++ if (refcount_cache_size > INT_MAX) { ++ error_setg(errp, "Refcount cache size too big"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* alloc new L2 table/refcount block cache, flush old one */ ++ if (s->l2_table_cache) { ++ ret = qcow2_cache_flush(bs, s->l2_table_cache); ++ if (ret) { ++ error_setg_errno(errp, -ret, "Failed to flush the L2 table cache"); ++ goto fail; ++ } ++ } ++ ++ if (s->refcount_block_cache) { ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret) { ++ error_setg_errno(errp, -ret, ++ "Failed to flush the refcount block cache"); ++ goto fail; ++ } ++ } ++ ++ r->l2_slice_size = l2_cache_entry_size / l2_entry_size(s); ++ r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size, ++ l2_cache_entry_size); ++ r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size, ++ s->cluster_size); ++ if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) { ++ error_setg(errp, "Could not allocate metadata caches"); ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ /* New interval for cache cleanup timer */ ++ r->cache_clean_interval = ++ qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, ++ DEFAULT_CACHE_CLEAN_INTERVAL); ++#ifndef CONFIG_LINUX ++ if (r->cache_clean_interval != 0) { ++ error_setg(errp, QCOW2_OPT_CACHE_CLEAN_INTERVAL ++ " not supported on this host"); ++ ret = -EINVAL; ++ goto fail; ++ } ++#endif ++ if (r->cache_clean_interval > UINT_MAX) { ++ error_setg(errp, "Cache clean interval too big"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* lazy-refcounts; flush if going from enabled to disabled */ ++ r->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS, ++ (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)); ++ if (r->use_lazy_refcounts && s->qcow_version < 3) { ++ error_setg(errp, "Lazy refcounts require a qcow2 image with at least " ++ "qemu 1.1 compatibility level"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (s->use_lazy_refcounts && !r->use_lazy_refcounts) { ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to disable lazy refcounts"); ++ goto fail; ++ } ++ } ++ ++ /* Overlap check options */ ++ opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP); ++ opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE); ++ if (opt_overlap_check_template && opt_overlap_check && ++ strcmp(opt_overlap_check_template, opt_overlap_check)) ++ { ++ error_setg(errp, "Conflicting values for qcow2 options '" ++ QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE ++ "' ('%s')", opt_overlap_check, opt_overlap_check_template); ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (!opt_overlap_check) { ++ opt_overlap_check = opt_overlap_check_template ?: "cached"; ++ } ++ ++ if (!strcmp(opt_overlap_check, "none")) { ++ overlap_check_template = 0; ++ } else if (!strcmp(opt_overlap_check, "constant")) { ++ overlap_check_template = QCOW2_OL_CONSTANT; ++ } else if (!strcmp(opt_overlap_check, "cached")) { ++ overlap_check_template = QCOW2_OL_CACHED; ++ } else if (!strcmp(opt_overlap_check, "all")) { ++ overlap_check_template = QCOW2_OL_ALL; ++ } else { ++ error_setg(errp, "Unsupported value '%s' for qcow2 option " ++ "'overlap-check'. Allowed are any of the following: " ++ "none, constant, cached, all", opt_overlap_check); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ r->overlap_check = 0; ++ for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) { ++ /* overlap-check defines a template bitmask, but every flag may be ++ * overwritten through the associated boolean option */ ++ r->overlap_check |= ++ qemu_opt_get_bool(opts, overlap_bool_option_names[i], ++ overlap_check_template & (1 << i)) << i; ++ } ++ ++ r->discard_passthrough[QCOW2_DISCARD_NEVER] = false; ++ r->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true; ++ r->discard_passthrough[QCOW2_DISCARD_REQUEST] = ++ qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST, ++ flags & BDRV_O_UNMAP); ++ r->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] = ++ qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true); ++ r->discard_passthrough[QCOW2_DISCARD_OTHER] = ++ qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false); ++ ++ r->discard_no_unref = qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_NO_UNREF, ++ false); ++ if (r->discard_no_unref && s->qcow_version < 3) { ++ error_setg(errp, ++ "discard-no-unref is only supported since qcow2 version 3"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ switch (s->crypt_method_header) { ++ case QCOW_CRYPT_NONE: ++ if (encryptfmt) { ++ error_setg(errp, "No encryption in image header, but options " ++ "specified format '%s'", encryptfmt); ++ ret = -EINVAL; ++ goto fail; ++ } ++ break; ++ ++ case QCOW_CRYPT_AES: ++ if (encryptfmt && !g_str_equal(encryptfmt, "aes")) { ++ error_setg(errp, ++ "Header reported 'aes' encryption format but " ++ "options specify '%s'", encryptfmt); ++ ret = -EINVAL; ++ goto fail; ++ } ++ qdict_put_str(encryptopts, "format", "qcow"); ++ r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); ++ if (!r->crypto_opts) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ break; ++ ++ case QCOW_CRYPT_LUKS: ++ if (encryptfmt && !g_str_equal(encryptfmt, "luks")) { ++ error_setg(errp, ++ "Header reported 'luks' encryption format but " ++ "options specify '%s'", encryptfmt); ++ ret = -EINVAL; ++ goto fail; ++ } ++ qdict_put_str(encryptopts, "format", "luks"); ++ r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); ++ if (!r->crypto_opts) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ break; ++ ++ default: ++ error_setg(errp, "Unsupported encryption method %d", ++ s->crypt_method_header); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ ret = 0; ++fail: ++ qobject_unref(encryptopts); ++ qemu_opts_del(opts); ++ opts = NULL; ++ return ret; ++} ++ ++static void qcow2_update_options_commit(BlockDriverState *bs, ++ Qcow2ReopenState *r) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int i; ++ ++ if (s->l2_table_cache) { ++ qcow2_cache_destroy(s->l2_table_cache); ++ } ++ if (s->refcount_block_cache) { ++ qcow2_cache_destroy(s->refcount_block_cache); ++ } ++ s->l2_table_cache = r->l2_table_cache; ++ s->refcount_block_cache = r->refcount_block_cache; ++ s->l2_slice_size = r->l2_slice_size; ++ ++ s->overlap_check = r->overlap_check; ++ s->use_lazy_refcounts = r->use_lazy_refcounts; ++ ++ for (i = 0; i < QCOW2_DISCARD_MAX; i++) { ++ s->discard_passthrough[i] = r->discard_passthrough[i]; ++ } ++ ++ s->discard_no_unref = r->discard_no_unref; ++ ++ if (s->cache_clean_interval != r->cache_clean_interval) { ++ cache_clean_timer_del(bs); ++ s->cache_clean_interval = r->cache_clean_interval; ++ cache_clean_timer_init(bs, bdrv_get_aio_context(bs)); ++ } ++ ++ qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ s->crypto_opts = r->crypto_opts; ++} ++ ++static void qcow2_update_options_abort(BlockDriverState *bs, ++ Qcow2ReopenState *r) ++{ ++ if (r->l2_table_cache) { ++ qcow2_cache_destroy(r->l2_table_cache); ++ } ++ if (r->refcount_block_cache) { ++ qcow2_cache_destroy(r->refcount_block_cache); ++ } ++ qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_update_options(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ Qcow2ReopenState r = {}; ++ int ret; ++ ++ ret = qcow2_update_options_prepare(bs, &r, options, flags, errp); ++ if (ret >= 0) { ++ qcow2_update_options_commit(bs, &r); ++ } else { ++ qcow2_update_options_abort(bs, &r); ++ } ++ ++ return ret; ++} ++ ++static int validate_compression_type(BDRVQcow2State *s, Error **errp) ++{ ++ switch (s->compression_type) { ++ case QCOW2_COMPRESSION_TYPE_ZLIB: ++#ifdef CONFIG_ZSTD ++ case QCOW2_COMPRESSION_TYPE_ZSTD: ++#endif ++ break; ++ ++ default: ++ error_setg(errp, "qcow2: unknown compression type: %u", ++ s->compression_type); ++ return -ENOTSUP; ++ } ++ ++ /* ++ * if the compression type differs from QCOW2_COMPRESSION_TYPE_ZLIB ++ * the incompatible feature flag must be set ++ */ ++ if (s->compression_type == QCOW2_COMPRESSION_TYPE_ZLIB) { ++ if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION) { ++ error_setg(errp, "qcow2: Compression type incompatible feature " ++ "bit must not be set"); ++ return -EINVAL; ++ } ++ } else { ++ if (!(s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION)) { ++ error_setg(errp, "qcow2: Compression type incompatible feature " ++ "bit must be set"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Called with s->lock held. */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, ++ bool open_data_file, Error **errp) ++{ ++ ERRP_GUARD(); ++ BDRVQcow2State *s = bs->opaque; ++ unsigned int len, i; ++ int ret = 0; ++ QCowHeader header; ++ uint64_t ext_end; ++ uint64_t l1_vm_state_index; ++ bool update_header = false; ++ ++ 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; ++ } ++ header.magic = be32_to_cpu(header.magic); ++ header.version = be32_to_cpu(header.version); ++ header.backing_file_offset = be64_to_cpu(header.backing_file_offset); ++ header.backing_file_size = be32_to_cpu(header.backing_file_size); ++ header.size = be64_to_cpu(header.size); ++ header.cluster_bits = be32_to_cpu(header.cluster_bits); ++ header.crypt_method = be32_to_cpu(header.crypt_method); ++ header.l1_table_offset = be64_to_cpu(header.l1_table_offset); ++ header.l1_size = be32_to_cpu(header.l1_size); ++ header.refcount_table_offset = be64_to_cpu(header.refcount_table_offset); ++ header.refcount_table_clusters = ++ be32_to_cpu(header.refcount_table_clusters); ++ header.snapshots_offset = be64_to_cpu(header.snapshots_offset); ++ header.nb_snapshots = be32_to_cpu(header.nb_snapshots); ++ ++ if (header.magic != QCOW_MAGIC) { ++ error_setg(errp, "Image is not in qcow2 format"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ if (header.version < 2 || header.version > 3) { ++ error_setg(errp, "Unsupported qcow2 version %" PRIu32, header.version); ++ ret = -ENOTSUP; ++ goto fail; ++ } ++ ++ s->qcow_version = header.version; ++ ++ /* Initialise cluster size */ ++ if (header.cluster_bits < MIN_CLUSTER_BITS || ++ header.cluster_bits > MAX_CLUSTER_BITS) { ++ error_setg(errp, "Unsupported cluster size: 2^%" PRIu32, ++ header.cluster_bits); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ s->cluster_bits = header.cluster_bits; ++ s->cluster_size = 1 << s->cluster_bits; ++ ++ /* Initialise version 3 header fields */ ++ if (header.version == 2) { ++ header.incompatible_features = 0; ++ header.compatible_features = 0; ++ header.autoclear_features = 0; ++ header.refcount_order = 4; ++ header.header_length = 72; ++ } else { ++ header.incompatible_features = ++ be64_to_cpu(header.incompatible_features); ++ header.compatible_features = be64_to_cpu(header.compatible_features); ++ header.autoclear_features = be64_to_cpu(header.autoclear_features); ++ header.refcount_order = be32_to_cpu(header.refcount_order); ++ header.header_length = be32_to_cpu(header.header_length); ++ ++ if (header.header_length < 104) { ++ error_setg(errp, "qcow2 header too short"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ ++ if (header.header_length > s->cluster_size) { ++ error_setg(errp, "qcow2 header exceeds cluster size"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ 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_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"); ++ goto fail; ++ } ++ } ++ ++ if (header.backing_file_offset > s->cluster_size) { ++ error_setg(errp, "Invalid backing file offset"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (header.backing_file_offset) { ++ ext_end = header.backing_file_offset; ++ } else { ++ ext_end = 1 << header.cluster_bits; ++ } ++ ++ /* Handle feature bits */ ++ s->incompatible_features = header.incompatible_features; ++ s->compatible_features = header.compatible_features; ++ s->autoclear_features = header.autoclear_features; ++ ++ /* ++ * Handle compression type ++ * Older qcow2 images don't contain the compression type header. ++ * Distinguish them by the header length and use ++ * the only valid (default) compression type in that case ++ */ ++ if (header.header_length > offsetof(QCowHeader, compression_type)) { ++ s->compression_type = header.compression_type; ++ } else { ++ s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; ++ } ++ ++ ret = validate_compression_type(s, errp); ++ if (ret) { ++ goto fail; ++ } ++ ++ if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) { ++ void *feature_table = NULL; ++ qcow2_read_extensions(bs, header.header_length, ext_end, ++ &feature_table, flags, NULL, NULL); ++ report_unsupported_feature(errp, feature_table, ++ s->incompatible_features & ++ ~QCOW2_INCOMPAT_MASK); ++ ret = -ENOTSUP; ++ g_free(feature_table); ++ goto fail; ++ } ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) { ++ /* Corrupt images may not be written to unless they are being repaired ++ */ ++ if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { ++ error_setg(errp, "qcow2: Image is corrupt; cannot be opened " ++ "read/write"); ++ ret = -EACCES; ++ goto fail; ++ } ++ } ++ ++ s->subclusters_per_cluster = ++ has_subclusters(s) ? QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER : 1; ++ s->subcluster_size = s->cluster_size / s->subclusters_per_cluster; ++ s->subcluster_bits = ctz32(s->subcluster_size); ++ ++ if (s->subcluster_size < (1 << MIN_CLUSTER_BITS)) { ++ error_setg(errp, "Unsupported subcluster size: %d", s->subcluster_size); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* Check support for various header values */ ++ if (header.refcount_order > 6) { ++ error_setg(errp, "Reference count entry width too large; may not " ++ "exceed 64 bits"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ s->refcount_order = header.refcount_order; ++ s->refcount_bits = 1 << s->refcount_order; ++ s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); ++ s->refcount_max += s->refcount_max - 1; ++ ++ s->crypt_method_header = header.crypt_method; ++ if (s->crypt_method_header) { ++ if (bdrv_uses_whitelist() && ++ s->crypt_method_header == QCOW_CRYPT_AES) { ++ error_setg(errp, ++ "Use of AES-CBC encrypted qcow2 images is no longer " ++ "supported in system emulators"); ++ error_append_hint(errp, ++ "You can use 'qemu-img convert' to convert your " ++ "image to an alternative supported format, such " ++ "as unencrypted qcow2, or raw with the LUKS " ++ "format instead.\n"); ++ ret = -ENOSYS; ++ goto fail; ++ } ++ ++ if (s->crypt_method_header == QCOW_CRYPT_AES) { ++ s->crypt_physical_offset = false; ++ } else { ++ /* Assuming LUKS and any future crypt methods we ++ * add will all use physical offsets, due to the ++ * fact that the alternative is insecure... */ ++ s->crypt_physical_offset = true; ++ } ++ ++ bs->encrypted = true; ++ } ++ ++ s->l2_bits = s->cluster_bits - ctz32(l2_entry_size(s)); ++ s->l2_size = 1 << s->l2_bits; ++ /* 2^(s->refcount_order - 3) is the refcount width in bytes */ ++ s->refcount_block_bits = s->cluster_bits - (s->refcount_order - 3); ++ s->refcount_block_size = 1 << s->refcount_block_bits; ++ bs->total_sectors = header.size / BDRV_SECTOR_SIZE; ++ s->csize_shift = (62 - (s->cluster_bits - 8)); ++ s->csize_mask = (1 << (s->cluster_bits - 8)) - 1; ++ s->cluster_offset_mask = (1LL << s->csize_shift) - 1; ++ ++ s->refcount_table_offset = header.refcount_table_offset; ++ s->refcount_table_size = ++ header.refcount_table_clusters << (s->cluster_bits - 3); ++ ++ if (header.refcount_table_clusters == 0 && !(flags & BDRV_O_CHECK)) { ++ error_setg(errp, "Image does not contain a reference count table"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ ret = qcow2_validate_table(bs, s->refcount_table_offset, ++ header.refcount_table_clusters, ++ s->cluster_size, QCOW_MAX_REFTABLE_SIZE, ++ "Reference count table", errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (!(flags & BDRV_O_CHECK)) { ++ /* ++ * The total size in bytes of the snapshot table is checked in ++ * qcow2_read_snapshots() because the size of each snapshot is ++ * variable and we don't know it yet. ++ * Here we only check the offset and number of snapshots. ++ */ ++ ret = qcow2_validate_table(bs, header.snapshots_offset, ++ header.nb_snapshots, ++ sizeof(QCowSnapshotHeader), ++ sizeof(QCowSnapshotHeader) * ++ QCOW_MAX_SNAPSHOTS, ++ "Snapshot table", errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* read the level 1 table */ ++ ret = qcow2_validate_table(bs, header.l1_table_offset, ++ header.l1_size, L1E_SIZE, ++ QCOW_MAX_L1_SIZE, "Active L1 table", errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ s->l1_size = header.l1_size; ++ s->l1_table_offset = header.l1_table_offset; ++ ++ l1_vm_state_index = size_to_l1(s, header.size); ++ if (l1_vm_state_index > INT_MAX) { ++ error_setg(errp, "Image is too big"); ++ ret = -EFBIG; ++ goto fail; ++ } ++ s->l1_vm_state_index = l1_vm_state_index; ++ ++ /* the L1 table must contain at least enough entries to put ++ header.size bytes */ ++ if (s->l1_size < s->l1_vm_state_index) { ++ error_setg(errp, "L1 table is too small"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (s->l1_size > 0) { ++ s->l1_table = qemu_try_blockalign(bs->file->bs, s->l1_size * L1E_SIZE); ++ if (s->l1_table == NULL) { ++ error_setg(errp, "Could not allocate L1 table"); ++ ret = -ENOMEM; ++ goto fail; ++ } ++ 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; ++ } ++ for(i = 0;i < s->l1_size; i++) { ++ s->l1_table[i] = be64_to_cpu(s->l1_table[i]); ++ } ++ } ++ ++ /* Parse driver-specific options */ ++ ret = qcow2_update_options(bs, options, flags, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ s->flags = flags; ++ ++ ret = qcow2_refcount_init(bs); ++ if (ret != 0) { ++ error_setg_errno(errp, -ret, "Could not initialize refcount handling"); ++ goto fail; ++ } ++ ++ QLIST_INIT(&s->cluster_allocs); ++ QTAILQ_INIT(&s->discards); ++ ++ /* read qcow2 extensions */ ++ if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL, ++ flags, &update_header, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (open_data_file && (flags & BDRV_O_NO_IO)) { ++ /* ++ * Don't open the data file for 'qemu-img info' so that it can be used ++ * to verify that an untrusted qcow2 image doesn't refer to external ++ * files. ++ * ++ * Note: This still makes has_data_file() return true. ++ */ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { ++ s->data_file = NULL; ++ } else { ++ s->data_file = bs->file; ++ } ++ qdict_extract_subqdict(options, NULL, "data-file."); ++ qdict_del(options, "data-file"); ++ } else if (open_data_file) { ++ /* Open external data file */ ++ bdrv_graph_co_rdunlock(); ++ s->data_file = bdrv_co_open_child(NULL, options, "data-file", bs, ++ &child_of_bds, BDRV_CHILD_DATA, ++ true, errp); ++ bdrv_graph_co_rdlock(); ++ if (*errp) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { ++ if (!s->data_file && s->image_data_file) { ++ bdrv_graph_co_rdunlock(); ++ s->data_file = bdrv_co_open_child(s->image_data_file, options, ++ "data-file", bs, ++ &child_of_bds, ++ BDRV_CHILD_DATA, false, errp); ++ bdrv_graph_co_rdlock(); ++ if (!s->data_file) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ if (!s->data_file) { ++ error_setg(errp, "'data-file' is required for this image"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ /* No data here */ ++ bs->file->role &= ~BDRV_CHILD_DATA; ++ ++ /* Must succeed because we have given up permissions if anything */ ++ bdrv_child_refresh_perms(bs, bs->file, &error_abort); ++ } else { ++ if (s->data_file) { ++ error_setg(errp, "'data-file' can only be set for images with " ++ "an external data file"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ s->data_file = bs->file; ++ ++ if (data_file_is_raw(bs)) { ++ error_setg(errp, "data-file-raw requires a data file"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ } ++ ++ /* qcow2_read_extension may have set up the crypto context ++ * if the crypt method needs a header region, some methods ++ * don't need header extensions, so must check here ++ */ ++ if (s->crypt_method_header && !s->crypto) { ++ if (s->crypt_method_header == QCOW_CRYPT_AES) { ++ unsigned int cflags = 0; ++ if (flags & BDRV_O_NO_IO) { ++ cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; ++ } ++ s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", ++ NULL, NULL, cflags, errp); ++ if (!s->crypto) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ } else if (!(flags & BDRV_O_NO_IO)) { ++ error_setg(errp, "Missing CRYPTO header for crypt method %d", ++ s->crypt_method_header); ++ ret = -EINVAL; ++ goto fail; ++ } ++ } ++ ++ /* read the backing file name */ ++ if (header.backing_file_offset != 0) { ++ len = header.backing_file_size; ++ if (len > MIN(1023, s->cluster_size - header.backing_file_offset) || ++ len >= sizeof(bs->backing_file)) { ++ error_setg(errp, "Backing file name too long"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ s->image_backing_file = g_malloc(len + 1); ++ 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; ++ } ++ s->image_backing_file[len] = '\0'; ++ ++ /* ++ * Update only when something has changed. This function is called by ++ * qcow2_co_invalidate_cache(), and we do not want to reset ++ * auto_backing_file unless necessary. ++ */ ++ if (!g_str_equal(s->image_backing_file, bs->backing_file)) { ++ pstrcpy(bs->backing_file, sizeof(bs->backing_file), ++ s->image_backing_file); ++ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), ++ s->image_backing_file); ++ } ++ } ++ ++ /* ++ * Internal snapshots; skip reading them in check mode, because ++ * we do not need them then, and we do not want to abort because ++ * of a broken table. ++ */ ++ if (!(flags & BDRV_O_CHECK)) { ++ s->snapshots_offset = header.snapshots_offset; ++ s->nb_snapshots = header.nb_snapshots; ++ ++ ret = qcow2_read_snapshots(bs, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* Clear unknown autoclear feature bits */ ++ update_header |= s->autoclear_features & ~QCOW2_AUTOCLEAR_MASK; ++ update_header = update_header && bdrv_is_writable(bs); ++ if (update_header) { ++ s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; ++ } ++ ++ /* == Handle persistent dirty bitmaps == ++ * ++ * We want load dirty bitmaps in three cases: ++ * ++ * 1. Normal open of the disk in active mode, not related to invalidation ++ * after migration. ++ * ++ * 2. Invalidation of the target vm after pre-copy phase of migration, if ++ * bitmaps are _not_ migrating through migration channel, i.e. ++ * 'dirty-bitmaps' capability is disabled. ++ * ++ * 3. Invalidation of source vm after failed or canceled migration. ++ * This is a very interesting case. There are two possible types of ++ * bitmaps: ++ * ++ * A. Stored on inactivation and removed. They should be loaded from the ++ * image. ++ * ++ * B. Not stored: not-persistent bitmaps and bitmaps, migrated through ++ * the migration channel (with dirty-bitmaps capability). ++ * ++ * On the other hand, there are two possible sub-cases: ++ * ++ * 3.1 disk was changed by somebody else while were inactive. In this ++ * case all in-RAM dirty bitmaps (both persistent and not) are ++ * definitely invalid. And we don't have any method to determine ++ * this. ++ * ++ * Simple and safe thing is to just drop all the bitmaps of type B on ++ * inactivation. But in this case we lose bitmaps in valid 4.2 case. ++ * ++ * On the other hand, resuming source vm, if disk was already changed ++ * is a bad thing anyway: not only bitmaps, the whole vm state is ++ * out of sync with disk. ++ * ++ * This means, that user or management tool, who for some reason ++ * decided to resume source vm, after disk was already changed by ++ * target vm, should at least drop all dirty bitmaps by hand. ++ * ++ * So, we can ignore this case for now, but TODO: "generation" ++ * extension for qcow2, to determine, that image was changed after ++ * last inactivation. And if it is changed, we will drop (or at least ++ * mark as 'invalid' all the bitmaps of type B, both persistent ++ * and not). ++ * ++ * 3.2 disk was _not_ changed while were inactive. Bitmaps may be saved ++ * to disk ('dirty-bitmaps' capability disabled), or not saved ++ * ('dirty-bitmaps' capability enabled), but we don't need to care ++ * of: let's load bitmaps as always: stored bitmaps will be loaded, ++ * and not stored has flag IN_USE=1 in the image and will be skipped ++ * on loading. ++ * ++ * One remaining possible case when we don't want load bitmaps: ++ * ++ * 4. Open disk in inactive mode in target vm (bitmaps are migrating or ++ * will be loaded on invalidation, no needs try loading them before) ++ */ ++ ++ if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) { ++ /* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */ ++ bool header_updated; ++ if (!qcow2_load_dirty_bitmaps(bs, &header_updated, errp)) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ update_header = update_header && !header_updated; ++ } ++ ++ if (update_header) { ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not update qcow2 header"); ++ goto fail; ++ } ++ } ++ ++ bs->supported_zero_flags = header.version >= 3 ? ++ BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK : 0; ++ bs->supported_truncate_flags = BDRV_REQ_ZERO_WRITE; ++ ++ /* Repair image if dirty */ ++ if (!(flags & BDRV_O_CHECK) && bdrv_is_writable(bs) && ++ (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) { ++ BdrvCheckResult result = {0}; ++ ++ ret = qcow2_co_check_locked(bs, &result, ++ BDRV_FIX_ERRORS | BDRV_FIX_LEAKS); ++ if (ret < 0 || result.check_errors) { ++ if (ret >= 0) { ++ ret = -EIO; ++ } ++ error_setg_errno(errp, -ret, "Could not repair dirty image"); ++ goto fail; ++ } ++ } ++ ++#ifdef DEBUG_ALLOC ++ { ++ BdrvCheckResult result = {0}; ++ qcow2_check_refcounts(bs, &result, 0); ++ } ++#endif ++ ++ qemu_co_queue_init(&s->thread_task_queue); ++ ++ return ret; ++ ++ fail: ++ g_free(s->image_data_file); ++ if (open_data_file && has_data_file(bs)) { ++ bdrv_graph_co_rdunlock(); ++ bdrv_co_unref_child(bs, s->data_file); ++ bdrv_graph_co_rdlock(); ++ s->data_file = NULL; ++ } ++ g_free(s->unknown_header_fields); ++ cleanup_unknown_header_ext(bs); ++ qcow2_free_snapshots(bs); ++ qcow2_refcount_close(bs); ++ qemu_vfree(s->l1_table); ++ /* else pre-write overlap checks in cache_destroy may crash */ ++ s->l1_table = NULL; ++ cache_clean_timer_del(bs); ++ if (s->l2_table_cache) { ++ qcow2_cache_destroy(s->l2_table_cache); ++ } ++ if (s->refcount_block_cache) { ++ qcow2_cache_destroy(s->refcount_block_cache); ++ } ++ qcrypto_block_free(s->crypto); ++ qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ return ret; ++} ++ ++typedef struct QCow2OpenCo { ++ BlockDriverState *bs; ++ QDict *options; ++ int flags; ++ Error **errp; ++ int ret; ++} QCow2OpenCo; ++ ++static void coroutine_fn qcow2_open_entry(void *opaque) ++{ ++ QCow2OpenCo *qoc = opaque; ++ BDRVQcow2State *s = qoc->bs->opaque; ++ ++ GRAPH_RDLOCK_GUARD(); ++ ++ qemu_co_mutex_lock(&s->lock); ++ qoc->ret = qcow2_do_open(qoc->bs, qoc->options, qoc->flags, true, ++ qoc->errp); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ aio_wait_kick(); ++} ++ ++static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCow2OpenCo qoc = { ++ .bs = bs, ++ .options = options, ++ .flags = flags, ++ .errp = errp, ++ .ret = -EINPROGRESS ++ }; ++ int ret; ++ ++ ret = bdrv_open_file_child(NULL, options, "file", bs, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ /* Initialise locks */ ++ qemu_co_mutex_init(&s->lock); ++ ++ assert(!qemu_in_coroutine()); ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ ++ aio_co_enter(bdrv_get_aio_context(bs), ++ qemu_coroutine_create(qcow2_open_entry, &qoc)); ++ AIO_WAIT_WHILE_UNLOCKED(NULL, qoc.ret == -EINPROGRESS); ++ ++ return qoc.ret; ++} ++ ++static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ if (bs->encrypted) { ++ /* Encryption works on a sector granularity */ ++ bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); ++ } ++ bs->bl.pwrite_zeroes_alignment = s->subcluster_size; ++ bs->bl.pdiscard_alignment = s->cluster_size; ++} ++ ++static int GRAPH_UNLOCKED ++qcow2_reopen_prepare(BDRVReopenState *state,BlockReopenQueue *queue, ++ Error **errp) ++{ ++ BDRVQcow2State *s = state->bs->opaque; ++ Qcow2ReopenState *r; ++ int ret; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ r = g_new0(Qcow2ReopenState, 1); ++ state->opaque = r; ++ ++ ret = qcow2_update_options_prepare(state->bs, r, state->options, ++ state->flags, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* We need to write out any unwritten data if we reopen read-only. */ ++ if ((state->flags & BDRV_O_RDWR) == 0) { ++ ret = qcow2_reopen_bitmaps_ro(state->bs, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_flush(state->bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_mark_clean(state->bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ ++ /* ++ * Without an external data file, s->data_file points to the same BdrvChild ++ * as bs->file. It needs to be resynced after reopen because bs->file may ++ * be changed. We can't use it in the meantime. ++ */ ++ if (!has_data_file(state->bs)) { ++ assert(s->data_file == state->bs->file); ++ s->data_file = NULL; ++ } ++ ++ return 0; ++ ++fail: ++ qcow2_update_options_abort(state->bs, r); ++ g_free(r); ++ return ret; ++} ++ ++static void qcow2_reopen_commit(BDRVReopenState *state) ++{ ++ BDRVQcow2State *s = state->bs->opaque; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ qcow2_update_options_commit(state->bs, state->opaque); ++ if (!s->data_file) { ++ /* ++ * If we don't have an external data file, s->data_file was cleared by ++ * qcow2_reopen_prepare() and needs to be updated. ++ */ ++ s->data_file = state->bs->file; ++ } ++ g_free(state->opaque); ++} ++ ++static void qcow2_reopen_commit_post(BDRVReopenState *state) ++{ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (state->flags & BDRV_O_RDWR) { ++ Error *local_err = NULL; ++ ++ if (qcow2_reopen_bitmaps_rw(state->bs, &local_err) < 0) { ++ /* ++ * This is not fatal, bitmaps just left read-only, so all following ++ * writes will fail. User can remove read-only bitmaps to unblock ++ * writes or retry reopen. ++ */ ++ error_reportf_err(local_err, ++ "%s: Failed to make dirty bitmaps writable: ", ++ bdrv_get_node_name(state->bs)); ++ } ++ } ++} ++ ++static void qcow2_reopen_abort(BDRVReopenState *state) ++{ ++ BDRVQcow2State *s = state->bs->opaque; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ if (!s->data_file) { ++ /* ++ * If we don't have an external data file, s->data_file was cleared by ++ * qcow2_reopen_prepare() and needs to be restored. ++ */ ++ s->data_file = state->bs->file; ++ } ++ qcow2_update_options_abort(state->bs, state->opaque); ++ g_free(state->opaque); ++} ++ ++static void qcow2_join_options(QDict *options, QDict *old_options) ++{ ++ bool has_new_overlap_template = ++ qdict_haskey(options, QCOW2_OPT_OVERLAP) || ++ qdict_haskey(options, QCOW2_OPT_OVERLAP_TEMPLATE); ++ bool has_new_total_cache_size = ++ qdict_haskey(options, QCOW2_OPT_CACHE_SIZE); ++ bool has_all_cache_options; ++ ++ /* New overlap template overrides all old overlap options */ ++ if (has_new_overlap_template) { ++ qdict_del(old_options, QCOW2_OPT_OVERLAP); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_TEMPLATE); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_MAIN_HEADER); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L1); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_ACTIVE_L2); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_TABLE); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L1); ++ qdict_del(old_options, QCOW2_OPT_OVERLAP_INACTIVE_L2); ++ } ++ ++ /* New total cache size overrides all old options */ ++ if (qdict_haskey(options, QCOW2_OPT_CACHE_SIZE)) { ++ qdict_del(old_options, QCOW2_OPT_L2_CACHE_SIZE); ++ qdict_del(old_options, QCOW2_OPT_REFCOUNT_CACHE_SIZE); ++ } ++ ++ qdict_join(options, old_options, false); ++ ++ /* ++ * If after merging all cache size options are set, an old total size is ++ * overwritten. Do keep all options, however, if all three are new. The ++ * resulting error message is what we want to happen. ++ */ ++ has_all_cache_options = ++ qdict_haskey(options, QCOW2_OPT_CACHE_SIZE) || ++ qdict_haskey(options, QCOW2_OPT_L2_CACHE_SIZE) || ++ qdict_haskey(options, QCOW2_OPT_REFCOUNT_CACHE_SIZE); ++ ++ if (has_all_cache_options && !has_new_total_cache_size) { ++ qdict_del(options, QCOW2_OPT_CACHE_SIZE); ++ } ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, ++ int64_t count, int64_t *pnum, int64_t *map, ++ BlockDriverState **file) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t host_offset; ++ unsigned int bytes; ++ QCow2SubclusterType type; ++ int ret, status = 0; ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ if (!s->metadata_preallocation_checked) { ++ ret = qcow2_detect_metadata_preallocation(bs); ++ s->metadata_preallocation = (ret == 1); ++ s->metadata_preallocation_checked = true; ++ } ++ ++ bytes = MIN(INT_MAX, count); ++ ret = qcow2_get_host_offset(bs, offset, &bytes, &host_offset, &type); ++ qemu_co_mutex_unlock(&s->lock); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ *pnum = bytes; ++ ++ if ((type == QCOW2_SUBCLUSTER_NORMAL || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC || ++ type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC) && !s->crypto) { ++ *map = host_offset; ++ *file = s->data_file->bs; ++ status |= BDRV_BLOCK_OFFSET_VALID; ++ } ++ if (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC) { ++ status |= BDRV_BLOCK_ZERO; ++ } else if (type != QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && ++ type != QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC) { ++ status |= BDRV_BLOCK_DATA; ++ } ++ if (s->metadata_preallocation && (status & BDRV_BLOCK_DATA) && ++ (status & BDRV_BLOCK_OFFSET_VALID)) ++ { ++ status |= BDRV_BLOCK_RECURSE; ++ } ++ if (type == QCOW2_SUBCLUSTER_COMPRESSED) { ++ status |= BDRV_BLOCK_COMPRESSED; ++ } ++ return status; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_handle_l2meta(BlockDriverState *bs, QCowL2Meta **pl2meta, bool link_l2) ++{ ++ int ret = 0; ++ QCowL2Meta *l2meta = *pl2meta; ++ ++ while (l2meta != NULL) { ++ QCowL2Meta *next; ++ ++ if (link_l2) { ++ ret = qcow2_alloc_cluster_link_l2(bs, l2meta); ++ if (ret) { ++ goto out; ++ } ++ } else { ++ qcow2_alloc_cluster_abort(bs, l2meta); ++ } ++ ++ /* Take the request off the list of running requests */ ++ QLIST_REMOVE(l2meta, next_in_flight); ++ ++ qemu_co_queue_restart_all(&l2meta->dependent_requests); ++ ++ next = l2meta->next; ++ g_free(l2meta); ++ l2meta = next; ++ } ++out: ++ *pl2meta = l2meta; ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_preadv_encrypted(BlockDriverState *bs, ++ uint64_t host_offset, ++ uint64_t offset, ++ uint64_t bytes, ++ QEMUIOVector *qiov, ++ uint64_t qiov_offset) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ uint8_t *buf; ++ ++ assert(bs->encrypted && s->crypto); ++ assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); ++ ++ /* ++ * For encrypted images, read everything into a temporary ++ * contiguous buffer on which the AES functions can work. ++ * Also, decryption in a separate buffer is better as it ++ * prevents the guest from learning information about the ++ * encrypted nature of the virtual disk. ++ */ ++ ++ buf = qemu_try_blockalign(s->data_file->bs, bytes); ++ if (buf == NULL) { ++ return -ENOMEM; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); ++ ret = bdrv_co_pread(s->data_file, host_offset, bytes, buf, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (qcow2_co_decrypt(bs, host_offset, offset, buf, bytes) < 0) ++ { ++ ret = -EIO; ++ goto fail; ++ } ++ qemu_iovec_from_buf(qiov, qiov_offset, buf, bytes); ++ ++fail: ++ qemu_vfree(buf); ++ ++ return ret; ++} ++ ++typedef struct Qcow2AioTask { ++ AioTask task; ++ ++ BlockDriverState *bs; ++ QCow2SubclusterType subcluster_type; /* only for read */ ++ uint64_t host_offset; /* or l2_entry for compressed read */ ++ uint64_t offset; ++ uint64_t bytes; ++ QEMUIOVector *qiov; ++ uint64_t qiov_offset; ++ QCowL2Meta *l2meta; /* only for write */ ++} Qcow2AioTask; ++ ++static coroutine_fn int qcow2_co_preadv_task_entry(AioTask *task); ++static coroutine_fn int qcow2_add_task(BlockDriverState *bs, ++ AioTaskPool *pool, ++ AioTaskFunc func, ++ QCow2SubclusterType subcluster_type, ++ uint64_t host_offset, ++ uint64_t offset, ++ uint64_t bytes, ++ QEMUIOVector *qiov, ++ size_t qiov_offset, ++ QCowL2Meta *l2meta) ++{ ++ Qcow2AioTask local_task; ++ Qcow2AioTask *task = pool ? g_new(Qcow2AioTask, 1) : &local_task; ++ ++ *task = (Qcow2AioTask) { ++ .task.func = func, ++ .bs = bs, ++ .subcluster_type = subcluster_type, ++ .qiov = qiov, ++ .host_offset = host_offset, ++ .offset = offset, ++ .bytes = bytes, ++ .qiov_offset = qiov_offset, ++ .l2meta = l2meta, ++ }; ++ ++ trace_qcow2_add_task(qemu_coroutine_self(), bs, pool, ++ func == qcow2_co_preadv_task_entry ? "read" : "write", ++ subcluster_type, host_offset, offset, bytes, ++ qiov, qiov_offset); ++ ++ if (!pool) { ++ return func(&task->task); ++ } ++ ++ aio_task_pool_start_task(pool, &task->task); ++ ++ return 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_preadv_task(BlockDriverState *bs, QCow2SubclusterType subc_type, ++ uint64_t host_offset, uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ switch (subc_type) { ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ /* Both zero types are handled in qcow2_co_preadv_part */ ++ g_assert_not_reached(); ++ ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ assert(bs->backing); /* otherwise handled in qcow2_co_preadv_part */ ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); ++ return bdrv_co_preadv_part(bs->backing, offset, bytes, ++ qiov, qiov_offset, 0); ++ ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ return qcow2_co_preadv_compressed(bs, host_offset, ++ offset, bytes, qiov, qiov_offset); ++ ++ case QCOW2_SUBCLUSTER_NORMAL: ++ if (bs->encrypted) { ++ return qcow2_co_preadv_encrypted(bs, host_offset, ++ offset, bytes, qiov, qiov_offset); ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); ++ return bdrv_co_preadv_part(s->data_file, host_offset, ++ bytes, qiov, qiov_offset, 0); ++ ++ default: ++ g_assert_not_reached(); ++ } ++ ++ g_assert_not_reached(); ++} ++ ++/* ++ * This function can count as GRAPH_RDLOCK because qcow2_co_preadv_part() holds ++ * the graph lock and keeps it until this coroutine has terminated. ++ */ ++static int coroutine_fn GRAPH_RDLOCK qcow2_co_preadv_task_entry(AioTask *task) ++{ ++ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); ++ ++ assert(!t->l2meta); ++ ++ return qcow2_co_preadv_task(t->bs, t->subcluster_type, ++ t->host_offset, t->offset, t->bytes, ++ t->qiov, t->qiov_offset); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret = 0; ++ unsigned int cur_bytes; /* number of bytes in current iteration */ ++ uint64_t host_offset = 0; ++ QCow2SubclusterType type; ++ AioTaskPool *aio = NULL; ++ ++ while (bytes != 0 && aio_task_pool_status(aio) == 0) { ++ /* prepare next request */ ++ cur_bytes = MIN(bytes, INT_MAX); ++ if (s->crypto) { ++ cur_bytes = MIN(cur_bytes, ++ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_get_host_offset(bs, offset, &cur_bytes, ++ &host_offset, &type); ++ qemu_co_mutex_unlock(&s->lock); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ if (type == QCOW2_SUBCLUSTER_ZERO_PLAIN || ++ type == QCOW2_SUBCLUSTER_ZERO_ALLOC || ++ (type == QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && !bs->backing) || ++ (type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC && !bs->backing)) ++ { ++ qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes); ++ } else { ++ if (!aio && cur_bytes != bytes) { ++ aio = aio_task_pool_new(QCOW2_MAX_WORKERS); ++ } ++ ret = qcow2_add_task(bs, aio, qcow2_co_preadv_task_entry, type, ++ host_offset, offset, cur_bytes, ++ qiov, qiov_offset, NULL); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ bytes -= cur_bytes; ++ offset += cur_bytes; ++ qiov_offset += cur_bytes; ++ } ++ ++out: ++ if (aio) { ++ aio_task_pool_wait_all(aio); ++ if (ret == 0) { ++ ret = aio_task_pool_status(aio); ++ } ++ g_free(aio); ++ } ++ ++ return ret; ++} ++ ++/* Check if it's possible to merge a write request with the writing of ++ * the data from the COW regions */ ++static bool merge_cow(uint64_t offset, unsigned bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ QCowL2Meta *l2meta) ++{ ++ QCowL2Meta *m; ++ ++ for (m = l2meta; m != NULL; m = m->next) { ++ /* If both COW regions are empty then there's nothing to merge */ ++ if (m->cow_start.nb_bytes == 0 && m->cow_end.nb_bytes == 0) { ++ continue; ++ } ++ ++ /* If COW regions are handled already, skip this too */ ++ if (m->skip_cow) { ++ continue; ++ } ++ ++ /* ++ * The write request should start immediately after the first ++ * COW region. This does not always happen because the area ++ * touched by the request can be larger than the one defined ++ * by @m (a single request can span an area consisting of a ++ * mix of previously unallocated and allocated clusters, that ++ * is why @l2meta is a list). ++ */ ++ if (l2meta_cow_start(m) + m->cow_start.nb_bytes != offset) { ++ /* In this case the request starts before this region */ ++ assert(offset < l2meta_cow_start(m)); ++ assert(m->cow_start.nb_bytes == 0); ++ continue; ++ } ++ ++ /* The write request should end immediately before the second ++ * COW region (see above for why it does not always happen) */ ++ if (m->offset + m->cow_end.offset != offset + bytes) { ++ assert(offset + bytes > m->offset + m->cow_end.offset); ++ assert(m->cow_end.nb_bytes == 0); ++ continue; ++ } ++ ++ /* Make sure that adding both COW regions to the QEMUIOVector ++ * does not exceed IOV_MAX */ ++ if (qemu_iovec_subvec_niov(qiov, qiov_offset, bytes) > IOV_MAX - 2) { ++ continue; ++ } ++ ++ m->data_qiov = qiov; ++ m->data_qiov_offset = qiov_offset; ++ return true; ++ } ++ ++ return false; ++} ++ ++/* ++ * Return 1 if the COW regions read as zeroes, 0 if not, < 0 on error. ++ * Note that returning 0 does not guarantee non-zero data. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++is_zero_cow(BlockDriverState *bs, QCowL2Meta *m) ++{ ++ /* ++ * This check is designed for optimization shortcut so it must be ++ * efficient. ++ * Instead of is_zero(), use bdrv_co_is_zero_fast() as it is ++ * faster (but not as accurate and can result in false negatives). ++ */ ++ int ret = bdrv_co_is_zero_fast(bs, m->offset + m->cow_start.offset, ++ m->cow_start.nb_bytes); ++ if (ret <= 0) { ++ return ret; ++ } ++ ++ return bdrv_co_is_zero_fast(bs, m->offset + m->cow_end.offset, ++ m->cow_end.nb_bytes); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowL2Meta *m; ++ ++ if (!(s->data_file->bs->supported_zero_flags & BDRV_REQ_NO_FALLBACK)) { ++ return 0; ++ } ++ ++ if (bs->encrypted) { ++ return 0; ++ } ++ ++ for (m = l2meta; m != NULL; m = m->next) { ++ int ret; ++ uint64_t start_offset = m->alloc_offset + m->cow_start.offset; ++ unsigned nb_bytes = m->cow_end.offset + m->cow_end.nb_bytes - ++ m->cow_start.offset; ++ ++ if (!m->cow_start.nb_bytes && !m->cow_end.nb_bytes) { ++ continue; ++ } ++ ++ ret = is_zero_cow(bs, m); ++ if (ret < 0) { ++ return ret; ++ } else if (ret == 0) { ++ continue; ++ } ++ ++ /* ++ * instead of writing zero COW buffers, ++ * efficiently zero out the whole clusters ++ */ ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, start_offset, nb_bytes, ++ true); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_SPACE); ++ ret = bdrv_co_pwrite_zeroes(s->data_file, start_offset, nb_bytes, ++ BDRV_REQ_NO_FALLBACK); ++ if (ret < 0) { ++ if (ret != -ENOTSUP && ret != -EAGAIN) { ++ return ret; ++ } ++ continue; ++ } ++ ++ trace_qcow2_skip_cow(qemu_coroutine_self(), m->offset, m->nb_clusters); ++ m->skip_cow = true; ++ } ++ return 0; ++} ++ ++/* ++ * qcow2_co_pwritev_task ++ * Called with s->lock unlocked ++ * l2meta - if not NULL, qcow2_co_pwritev_task() will consume it. Caller must ++ * not use it somehow after qcow2_co_pwritev_task() call ++ */ ++static coroutine_fn GRAPH_RDLOCK ++int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, ++ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, ++ uint64_t qiov_offset, QCowL2Meta *l2meta) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ void *crypt_buf = NULL; ++ QEMUIOVector encrypted_qiov; ++ ++ if (bs->encrypted) { ++ assert(s->crypto); ++ assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); ++ crypt_buf = qemu_try_blockalign(bs->file->bs, bytes); ++ if (crypt_buf == NULL) { ++ ret = -ENOMEM; ++ goto out_unlocked; ++ } ++ qemu_iovec_to_buf(qiov, qiov_offset, crypt_buf, bytes); ++ ++ if (qcow2_co_encrypt(bs, host_offset, offset, crypt_buf, bytes) < 0) { ++ ret = -EIO; ++ goto out_unlocked; ++ } ++ ++ qemu_iovec_init_buf(&encrypted_qiov, crypt_buf, bytes); ++ qiov = &encrypted_qiov; ++ qiov_offset = 0; ++ } ++ ++ /* Try to efficiently initialize the physical space with zeroes */ ++ ret = handle_alloc_space(bs, l2meta); ++ if (ret < 0) { ++ goto out_unlocked; ++ } ++ ++ /* ++ * If we need to do COW, check if it's possible to merge the ++ * writing of the guest data together with that of the COW regions. ++ * If it's not possible (or not necessary) then write the ++ * guest data now. ++ */ ++ if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) { ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); ++ trace_qcow2_writev_data(qemu_coroutine_self(), host_offset); ++ ret = bdrv_co_pwritev_part(s->data_file, host_offset, ++ bytes, qiov, qiov_offset, 0); ++ if (ret < 0) { ++ goto out_unlocked; ++ } ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ ret = qcow2_handle_l2meta(bs, &l2meta, true); ++ goto out_locked; ++ ++out_unlocked: ++ qemu_co_mutex_lock(&s->lock); ++ ++out_locked: ++ qcow2_handle_l2meta(bs, &l2meta, false); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ qemu_vfree(crypt_buf); ++ ++ return ret; ++} ++ ++/* ++ * This function can count as GRAPH_RDLOCK because qcow2_co_pwritev_part() holds ++ * the graph lock and keeps it until this coroutine has terminated. ++ */ ++static coroutine_fn GRAPH_RDLOCK int qcow2_co_pwritev_task_entry(AioTask *task) ++{ ++ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); ++ ++ assert(!t->subcluster_type); ++ ++ return qcow2_co_pwritev_task(t->bs, t->host_offset, ++ t->offset, t->bytes, t->qiov, t->qiov_offset, ++ t->l2meta); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset, ++ BdrvRequestFlags flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int offset_in_cluster; ++ int ret; ++ unsigned int cur_bytes; /* number of sectors in current iteration */ ++ uint64_t host_offset; ++ QCowL2Meta *l2meta = NULL; ++ AioTaskPool *aio = NULL; ++ ++ trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes); ++ ++ while (bytes != 0 && aio_task_pool_status(aio) == 0) { ++ ++ l2meta = NULL; ++ ++ trace_qcow2_writev_start_part(qemu_coroutine_self()); ++ offset_in_cluster = offset_into_cluster(s, offset); ++ cur_bytes = MIN(bytes, INT_MAX); ++ if (bs->encrypted) { ++ cur_bytes = MIN(cur_bytes, ++ QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size ++ - offset_in_cluster); ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, ++ &host_offset, &l2meta); ++ if (ret < 0) { ++ goto out_locked; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, host_offset, ++ cur_bytes, true); ++ if (ret < 0) { ++ goto out_locked; ++ } ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ++ if (!aio && cur_bytes != bytes) { ++ aio = aio_task_pool_new(QCOW2_MAX_WORKERS); ++ } ++ ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_task_entry, 0, ++ host_offset, offset, ++ cur_bytes, qiov, qiov_offset, l2meta); ++ l2meta = NULL; /* l2meta is consumed by qcow2_co_pwritev_task() */ ++ if (ret < 0) { ++ goto fail_nometa; ++ } ++ ++ bytes -= cur_bytes; ++ offset += cur_bytes; ++ qiov_offset += cur_bytes; ++ trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes); ++ } ++ ret = 0; ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++out_locked: ++ qcow2_handle_l2meta(bs, &l2meta, false); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ++fail_nometa: ++ if (aio) { ++ aio_task_pool_wait_all(aio); ++ if (ret == 0) { ++ ret = aio_task_pool_status(aio); ++ } ++ g_free(aio); ++ } ++ ++ trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); ++ ++ return ret; ++} ++ ++static int GRAPH_RDLOCK qcow2_inactivate(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret, result = 0; ++ Error *local_err = NULL; ++ ++ qcow2_store_persistent_dirty_bitmaps(bs, true, &local_err); ++ if (local_err != NULL) { ++ result = -EINVAL; ++ error_reportf_err(local_err, "Lost persistent bitmaps during " ++ "inactivation of node '%s': ", ++ bdrv_get_device_or_node_name(bs)); ++ } ++ ++ ret = qcow2_cache_flush(bs, s->l2_table_cache); ++ if (ret) { ++ result = ret; ++ error_report("Failed to flush the L2 table cache: %s", ++ strerror(-ret)); ++ } ++ ++ ret = qcow2_cache_flush(bs, s->refcount_block_cache); ++ if (ret) { ++ result = ret; ++ error_report("Failed to flush the refcount block cache: %s", ++ strerror(-ret)); ++ } ++ ++ if (result == 0) { ++ qcow2_mark_clean(bs); ++ } ++ ++ return result; ++} ++ ++static void coroutine_mixed_fn GRAPH_RDLOCK ++qcow2_do_close(BlockDriverState *bs, bool close_data_file) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ qemu_vfree(s->l1_table); ++ /* else pre-write overlap checks in cache_destroy may crash */ ++ s->l1_table = NULL; ++ ++ if (!(s->flags & BDRV_O_INACTIVE)) { ++ qcow2_inactivate(bs); ++ } ++ ++ cache_clean_timer_del(bs); ++ qcow2_cache_destroy(s->l2_table_cache); ++ qcow2_cache_destroy(s->refcount_block_cache); ++ ++ qcrypto_block_free(s->crypto); ++ s->crypto = NULL; ++ qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ ++ g_free(s->unknown_header_fields); ++ cleanup_unknown_header_ext(bs); ++ ++ g_free(s->image_data_file); ++ g_free(s->image_backing_file); ++ g_free(s->image_backing_format); ++ ++ if (close_data_file && has_data_file(bs)) { ++ GLOBAL_STATE_CODE(); ++ bdrv_graph_rdunlock_main_loop(); ++ bdrv_graph_wrlock(); ++ bdrv_unref_child(bs, s->data_file); ++ bdrv_graph_wrunlock(); ++ s->data_file = NULL; ++ bdrv_graph_rdlock_main_loop(); ++ } ++ ++ qcow2_refcount_close(bs); ++ qcow2_free_snapshots(bs); ++} ++ ++static void GRAPH_UNLOCKED qcow2_close(BlockDriverState *bs) ++{ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ qcow2_do_close(bs, true); ++} ++ ++static void coroutine_fn GRAPH_RDLOCK ++qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) ++{ ++ ERRP_GUARD(); ++ BDRVQcow2State *s = bs->opaque; ++ BdrvChild *data_file; ++ int flags = s->flags; ++ QCryptoBlock *crypto = NULL; ++ QDict *options; ++ int ret; ++ ++ /* ++ * Backing files are read-only which makes all of their metadata immutable, ++ * that means we don't have to worry about reopening them here. ++ */ ++ ++ crypto = s->crypto; ++ s->crypto = NULL; ++ ++ /* ++ * Do not reopen s->data_file (i.e., have qcow2_do_close() not close it, ++ * and then prevent qcow2_do_open() from opening it), because this function ++ * runs in the I/O path and as such we must not invoke global-state ++ * functions like bdrv_unref_child() and bdrv_open_child(). ++ */ ++ ++ qcow2_do_close(bs, false); ++ ++ data_file = s->data_file; ++ memset(s, 0, sizeof(BDRVQcow2State)); ++ s->data_file = data_file; ++ ++ options = qdict_clone_shallow(bs->options); ++ ++ flags &= ~BDRV_O_INACTIVE; ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_do_open(bs, options, flags, false, errp); ++ qemu_co_mutex_unlock(&s->lock); ++ qobject_unref(options); ++ if (ret < 0) { ++ error_prepend(errp, "Could not reopen qcow2 layer: "); ++ bs->drv = NULL; ++ return; ++ } ++ ++ s->crypto = crypto; ++} ++ ++static size_t header_ext_add(char *buf, uint32_t magic, const void *s, ++ size_t len, size_t buflen) ++{ ++ QCowExtension *ext_backing_fmt = (QCowExtension*) buf; ++ size_t ext_len = sizeof(QCowExtension) + ((len + 7) & ~7); ++ ++ if (buflen < ext_len) { ++ return -ENOSPC; ++ } ++ ++ *ext_backing_fmt = (QCowExtension) { ++ .magic = cpu_to_be32(magic), ++ .len = cpu_to_be32(len), ++ }; ++ ++ if (len) { ++ memcpy(buf + sizeof(QCowExtension), s, len); ++ } ++ ++ return ext_len; ++} ++ ++/* ++ * Updates the qcow2 header, including the variable length parts of it, i.e. ++ * the backing file name and all extensions. qcow2 was not designed to allow ++ * such changes, so if we run out of space (we can only use the first cluster) ++ * this function may fail. ++ * ++ * Returns 0 on success, -errno in error cases. ++ */ ++int qcow2_update_header(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCowHeader *header; ++ char *buf; ++ size_t buflen = s->cluster_size; ++ int ret; ++ uint64_t total_size; ++ uint32_t refcount_table_clusters; ++ size_t header_length; ++ Qcow2UnknownHeaderExtension *uext; ++ ++ buf = qemu_blockalign(bs, buflen); ++ ++ /* Header structure */ ++ header = (QCowHeader*) buf; ++ ++ if (buflen < sizeof(*header)) { ++ ret = -ENOSPC; ++ goto fail; ++ } ++ ++ header_length = sizeof(*header) + s->unknown_header_fields_size; ++ total_size = bs->total_sectors * BDRV_SECTOR_SIZE; ++ refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); ++ ++ ret = validate_compression_type(s, NULL); ++ if (ret) { ++ goto fail; ++ } ++ ++ *header = (QCowHeader) { ++ /* Version 2 fields */ ++ .magic = cpu_to_be32(QCOW_MAGIC), ++ .version = cpu_to_be32(s->qcow_version), ++ .backing_file_offset = 0, ++ .backing_file_size = 0, ++ .cluster_bits = cpu_to_be32(s->cluster_bits), ++ .size = cpu_to_be64(total_size), ++ .crypt_method = cpu_to_be32(s->crypt_method_header), ++ .l1_size = cpu_to_be32(s->l1_size), ++ .l1_table_offset = cpu_to_be64(s->l1_table_offset), ++ .refcount_table_offset = cpu_to_be64(s->refcount_table_offset), ++ .refcount_table_clusters = cpu_to_be32(refcount_table_clusters), ++ .nb_snapshots = cpu_to_be32(s->nb_snapshots), ++ .snapshots_offset = cpu_to_be64(s->snapshots_offset), ++ ++ /* Version 3 fields */ ++ .incompatible_features = cpu_to_be64(s->incompatible_features), ++ .compatible_features = cpu_to_be64(s->compatible_features), ++ .autoclear_features = cpu_to_be64(s->autoclear_features), ++ .refcount_order = cpu_to_be32(s->refcount_order), ++ .header_length = cpu_to_be32(header_length), ++ .compression_type = s->compression_type, ++ }; ++ ++ /* For older versions, write a shorter header */ ++ switch (s->qcow_version) { ++ case 2: ++ ret = offsetof(QCowHeader, incompatible_features); ++ break; ++ case 3: ++ ret = sizeof(*header); ++ break; ++ default: ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ memset(buf, 0, buflen); ++ ++ /* Preserve any unknown field in the header */ ++ if (s->unknown_header_fields_size) { ++ if (buflen < s->unknown_header_fields_size) { ++ ret = -ENOSPC; ++ goto fail; ++ } ++ ++ memcpy(buf, s->unknown_header_fields, s->unknown_header_fields_size); ++ buf += s->unknown_header_fields_size; ++ buflen -= s->unknown_header_fields_size; ++ } ++ ++ /* Backing file format header extension */ ++ if (s->image_backing_format) { ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT, ++ s->image_backing_format, ++ strlen(s->image_backing_format), ++ buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* External data file header extension */ ++ if (has_data_file(bs) && s->image_data_file) { ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_DATA_FILE, ++ s->image_data_file, strlen(s->image_data_file), ++ buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* Full disk encryption header pointer extension */ ++ if (s->crypto_header.offset != 0) { ++ s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset); ++ s->crypto_header.length = cpu_to_be64(s->crypto_header.length); ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_CRYPTO_HEADER, ++ &s->crypto_header, sizeof(s->crypto_header), ++ buflen); ++ s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset); ++ s->crypto_header.length = be64_to_cpu(s->crypto_header.length); ++ if (ret < 0) { ++ goto fail; ++ } ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* ++ * Feature table. A mere 8 feature names occupies 392 bytes, and ++ * when coupled with the v3 minimum header of 104 bytes plus the ++ * 8-byte end-of-extension marker, that would leave only 8 bytes ++ * for a backing file name in an image with 512-byte clusters. ++ * Thus, we choose to omit this header for cluster sizes 4k and ++ * smaller. ++ */ ++ if (s->qcow_version >= 3 && s->cluster_size > 4096) { ++ static const Qcow2Feature features[] = { ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_DIRTY_BITNR, ++ .name = "dirty bit", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_CORRUPT_BITNR, ++ .name = "corrupt bit", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_DATA_FILE_BITNR, ++ .name = "external data file", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_COMPRESSION_BITNR, ++ .name = "compression type", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, ++ .bit = QCOW2_INCOMPAT_EXTL2_BITNR, ++ .name = "extended L2 entries", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_COMPATIBLE, ++ .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, ++ .name = "lazy refcounts", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_AUTOCLEAR, ++ .bit = QCOW2_AUTOCLEAR_BITMAPS_BITNR, ++ .name = "bitmaps", ++ }, ++ { ++ .type = QCOW2_FEAT_TYPE_AUTOCLEAR, ++ .bit = QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR, ++ .name = "raw external data", ++ }, ++ }; ++ ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE, ++ features, sizeof(features), buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* Bitmap extension */ ++ if (s->nb_bitmaps > 0) { ++ Qcow2BitmapHeaderExt bitmaps_header = { ++ .nb_bitmaps = cpu_to_be32(s->nb_bitmaps), ++ .bitmap_directory_size = ++ cpu_to_be64(s->bitmap_directory_size), ++ .bitmap_directory_offset = ++ cpu_to_be64(s->bitmap_directory_offset) ++ }; ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BITMAPS, ++ &bitmaps_header, sizeof(bitmaps_header), ++ buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* Keep unknown header extensions */ ++ QLIST_FOREACH(uext, &s->unknown_header_ext, next) { ++ ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ } ++ ++ /* End of header extensions */ ++ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_END, NULL, 0, buflen); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ buf += ret; ++ buflen -= ret; ++ ++ /* Backing file name */ ++ if (s->image_backing_file) { ++ size_t backing_file_len = strlen(s->image_backing_file); ++ ++ if (buflen < backing_file_len) { ++ ret = -ENOSPC; ++ goto fail; ++ } ++ ++ /* Using strncpy is ok here, since buf is not NUL-terminated. */ ++ strncpy(buf, s->image_backing_file, buflen); ++ ++ header->backing_file_offset = cpu_to_be64(buf - ((char*) header)); ++ header->backing_file_size = cpu_to_be32(backing_file_len); ++ } ++ ++ /* Write the new header */ ++ ret = bdrv_pwrite(bs->file, 0, s->cluster_size, header, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = 0; ++fail: ++ qemu_vfree(header); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_change_backing_file(BlockDriverState *bs, const char *backing_file, ++ const char *backing_fmt) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ++ /* Adding a backing file means that the external data file alone won't be ++ * enough to make sense of the content */ ++ if (backing_file && data_file_is_raw(bs)) { ++ return -EINVAL; ++ } ++ ++ if (backing_file && strlen(backing_file) > 1023) { ++ return -EINVAL; ++ } ++ ++ pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), ++ backing_file ?: ""); ++ pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); ++ pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); ++ ++ g_free(s->image_backing_file); ++ g_free(s->image_backing_format); ++ ++ s->image_backing_file = backing_file ? g_strdup(bs->backing_file) : NULL; ++ s->image_backing_format = backing_fmt ? g_strdup(bs->backing_format) : NULL; ++ ++ return qcow2_update_header(bs); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_set_up_encryption(BlockDriverState *bs, ++ QCryptoBlockCreateOptions *cryptoopts, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ QCryptoBlock *crypto = NULL; ++ int fmt, ret; ++ ++ switch (cryptoopts->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ fmt = QCOW_CRYPT_LUKS; ++ break; ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ fmt = QCOW_CRYPT_AES; ++ break; ++ default: ++ error_setg(errp, "Crypto format not supported in qcow2"); ++ return -EINVAL; ++ } ++ ++ s->crypt_method_header = fmt; ++ ++ crypto = qcrypto_block_create(cryptoopts, "encrypt.", ++ qcow2_crypto_hdr_init_func, ++ qcow2_crypto_hdr_write_func, ++ bs, 0, errp); ++ if (!crypto) { ++ return -EINVAL; ++ } ++ ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not write encryption header"); ++ goto out; ++ } ++ ++ ret = 0; ++ out: ++ qcrypto_block_free(crypto); ++ return ret; ++} ++ ++/** ++ * Preallocates metadata structures for data clusters between @offset (in the ++ * guest disk) and @new_length (which is thus generally the new guest disk ++ * size). ++ * ++ * Returns: 0 on success, -errno on failure. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++preallocate_co(BlockDriverState *bs, uint64_t offset, uint64_t new_length, ++ PreallocMode mode, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t bytes; ++ uint64_t host_offset = 0; ++ int64_t file_length; ++ unsigned int cur_bytes; ++ int ret; ++ QCowL2Meta *meta = NULL, *m; ++ ++ assert(offset <= new_length); ++ bytes = new_length - offset; ++ ++ while (bytes) { ++ cur_bytes = MIN(bytes, QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size)); ++ ret = qcow2_alloc_host_offset(bs, offset, &cur_bytes, ++ &host_offset, &meta); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Allocating clusters failed"); ++ goto out; ++ } ++ ++ for (m = meta; m != NULL; m = m->next) { ++ m->prealloc = true; ++ } ++ ++ ret = qcow2_handle_l2meta(bs, &meta, true); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Mapping clusters failed"); ++ goto out; ++ } ++ ++ /* TODO Preallocate data if requested */ ++ ++ bytes -= cur_bytes; ++ offset += cur_bytes; ++ } ++ ++ /* ++ * It is expected that the image file is large enough to actually contain ++ * all of the allocated clusters (otherwise we get failing reads after ++ * EOF). Extend the image to the last allocated sector. ++ */ ++ file_length = bdrv_co_getlength(s->data_file->bs); ++ if (file_length < 0) { ++ error_setg_errno(errp, -file_length, "Could not get file size"); ++ ret = file_length; ++ goto out; ++ } ++ ++ if (host_offset + cur_bytes > file_length) { ++ if (mode == PREALLOC_MODE_METADATA) { ++ mode = PREALLOC_MODE_OFF; ++ } ++ ret = bdrv_co_truncate(s->data_file, host_offset + cur_bytes, false, ++ mode, 0, errp); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ ret = 0; ++ ++out: ++ qcow2_handle_l2meta(bs, &meta, false); ++ return ret; ++} ++ ++/* qcow2_refcount_metadata_size: ++ * @clusters: number of clusters to refcount (including data and L1/L2 tables) ++ * @cluster_size: size of a cluster, in bytes ++ * @refcount_order: refcount bits power-of-2 exponent ++ * @generous_increase: allow for the refcount table to be 1.5x as large as it ++ * needs to be ++ * ++ * Returns: Number of bytes required for refcount blocks and table metadata. ++ */ ++int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size, ++ int refcount_order, bool generous_increase, ++ uint64_t *refblock_count) ++{ ++ /* ++ * Every host cluster is reference-counted, including metadata (even ++ * refcount metadata is recursively included). ++ * ++ * An accurate formula for the size of refcount metadata size is difficult ++ * to derive. An easier method of calculation is finding the fixed point ++ * where no further refcount blocks or table clusters are required to ++ * reference count every cluster. ++ */ ++ int64_t blocks_per_table_cluster = cluster_size / REFTABLE_ENTRY_SIZE; ++ int64_t refcounts_per_block = cluster_size * 8 / (1 << refcount_order); ++ int64_t table = 0; /* number of refcount table clusters */ ++ int64_t blocks = 0; /* number of refcount block clusters */ ++ int64_t last; ++ int64_t n = 0; ++ ++ do { ++ last = n; ++ blocks = DIV_ROUND_UP(clusters + table + blocks, refcounts_per_block); ++ table = DIV_ROUND_UP(blocks, blocks_per_table_cluster); ++ n = clusters + blocks + table; ++ ++ if (n == last && generous_increase) { ++ clusters += DIV_ROUND_UP(table, 2); ++ n = 0; /* force another loop */ ++ generous_increase = false; ++ } ++ } while (n != last); ++ ++ if (refblock_count) { ++ *refblock_count = blocks; ++ } ++ ++ return (blocks + table) * cluster_size; ++} ++ ++/** ++ * qcow2_calc_prealloc_size: ++ * @total_size: virtual disk size in bytes ++ * @cluster_size: cluster size in bytes ++ * @refcount_order: refcount bits power-of-2 exponent ++ * @extended_l2: true if the image has extended L2 entries ++ * ++ * Returns: Total number of bytes required for the fully allocated image ++ * (including metadata). ++ */ ++static int64_t qcow2_calc_prealloc_size(int64_t total_size, ++ size_t cluster_size, ++ int refcount_order, ++ bool extended_l2) ++{ ++ int64_t meta_size = 0; ++ uint64_t nl1e, nl2e; ++ int64_t aligned_total_size = ROUND_UP(total_size, cluster_size); ++ size_t l2e_size = extended_l2 ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; ++ ++ /* header: 1 cluster */ ++ meta_size += cluster_size; ++ ++ /* total size of L2 tables */ ++ nl2e = aligned_total_size / cluster_size; ++ nl2e = ROUND_UP(nl2e, cluster_size / l2e_size); ++ meta_size += nl2e * l2e_size; ++ ++ /* total size of L1 tables */ ++ nl1e = nl2e * l2e_size / cluster_size; ++ nl1e = ROUND_UP(nl1e, cluster_size / L1E_SIZE); ++ meta_size += nl1e * L1E_SIZE; ++ ++ /* total size of refcount table and blocks */ ++ meta_size += qcow2_refcount_metadata_size( ++ (meta_size + aligned_total_size) / cluster_size, ++ cluster_size, refcount_order, false, NULL); ++ ++ return meta_size + aligned_total_size; ++} ++ ++static bool validate_cluster_size(size_t cluster_size, bool extended_l2, ++ Error **errp) ++{ ++ int cluster_bits = ctz32(cluster_size); ++ if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || ++ (1 << cluster_bits) != cluster_size) ++ { ++ error_setg(errp, "Cluster size must be a power of two between %d and " ++ "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); ++ return false; ++ } ++ ++ if (extended_l2) { ++ unsigned min_cluster_size = ++ (1 << MIN_CLUSTER_BITS) * QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER; ++ if (cluster_size < min_cluster_size) { ++ error_setg(errp, "Extended L2 entries are only supported with " ++ "cluster sizes of at least %u bytes", min_cluster_size); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static size_t qcow2_opt_get_cluster_size_del(QemuOpts *opts, bool extended_l2, ++ Error **errp) ++{ ++ size_t cluster_size; ++ ++ cluster_size = qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE, ++ DEFAULT_CLUSTER_SIZE); ++ if (!validate_cluster_size(cluster_size, extended_l2, errp)) { ++ return 0; ++ } ++ return cluster_size; ++} ++ ++static int qcow2_opt_get_version_del(QemuOpts *opts, Error **errp) ++{ ++ char *buf; ++ int ret; ++ ++ buf = qemu_opt_get_del(opts, BLOCK_OPT_COMPAT_LEVEL); ++ if (!buf) { ++ ret = 3; /* default */ ++ } else if (!strcmp(buf, "0.10")) { ++ ret = 2; ++ } else if (!strcmp(buf, "1.1")) { ++ ret = 3; ++ } else { ++ error_setg(errp, "Invalid compatibility level: '%s'", buf); ++ ret = -EINVAL; ++ } ++ g_free(buf); ++ return ret; ++} ++ ++static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version, ++ Error **errp) ++{ ++ uint64_t refcount_bits; ++ ++ refcount_bits = qemu_opt_get_number_del(opts, BLOCK_OPT_REFCOUNT_BITS, 16); ++ if (refcount_bits > 64 || !is_power_of_2(refcount_bits)) { ++ error_setg(errp, "Refcount width must be a power of two and may not " ++ "exceed 64 bits"); ++ return 0; ++ } ++ ++ if (version < 3 && refcount_bits != 16) { ++ error_setg(errp, "Different refcount widths than 16 bits require " ++ "compatibility level 1.1 or above (use compat=1.1 or " ++ "greater)"); ++ return 0; ++ } ++ ++ return refcount_bits; ++} ++ ++static int coroutine_fn GRAPH_UNLOCKED ++qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) ++{ ++ ERRP_GUARD(); ++ BlockdevCreateOptionsQcow2 *qcow2_opts; ++ QDict *options; ++ ++ /* ++ * Open the image file and write a minimal qcow2 header. ++ * ++ * We keep things simple and start with a zero-sized image. We also ++ * do without refcount blocks or a L1 table for now. We'll fix the ++ * inconsistency later. ++ * ++ * We do need a refcount table because growing the refcount table means ++ * allocating two new refcount blocks - the second of which would be at ++ * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file ++ * size for any qcow2 image. ++ */ ++ BlockBackend *blk = NULL; ++ BlockDriverState *bs = NULL; ++ BlockDriverState *data_bs = NULL; ++ QCowHeader *header; ++ size_t cluster_size; ++ int version; ++ int refcount_order; ++ uint64_t *refcount_table; ++ int ret; ++ uint8_t compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; ++ ++ assert(create_options->driver == BLOCKDEV_DRIVER_QCOW2); ++ qcow2_opts = &create_options->u.qcow2; ++ ++ bs = bdrv_co_open_blockdev_ref(qcow2_opts->file, errp); ++ if (bs == NULL) { ++ return -EIO; ++ } ++ ++ /* Validate options and set default values */ ++ if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) { ++ error_setg(errp, "Image size must be a multiple of %u bytes", ++ (unsigned) BDRV_SECTOR_SIZE); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (qcow2_opts->has_version) { ++ switch (qcow2_opts->version) { ++ case BLOCKDEV_QCOW2_VERSION_V2: ++ version = 2; ++ break; ++ case BLOCKDEV_QCOW2_VERSION_V3: ++ version = 3; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ } else { ++ version = 3; ++ } ++ ++ if (qcow2_opts->has_cluster_size) { ++ cluster_size = qcow2_opts->cluster_size; ++ } else { ++ cluster_size = DEFAULT_CLUSTER_SIZE; ++ } ++ ++ if (!qcow2_opts->has_extended_l2) { ++ qcow2_opts->extended_l2 = false; ++ } ++ if (qcow2_opts->extended_l2) { ++ if (version < 3) { ++ error_setg(errp, "Extended L2 entries are only supported with " ++ "compatibility level 1.1 and above (use version=v3 or " ++ "greater)"); ++ ret = -EINVAL; ++ goto out; ++ } ++ } ++ ++ if (!validate_cluster_size(cluster_size, qcow2_opts->extended_l2, errp)) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (!qcow2_opts->has_preallocation) { ++ qcow2_opts->preallocation = PREALLOC_MODE_OFF; ++ } ++ if (qcow2_opts->backing_file && ++ qcow2_opts->preallocation != PREALLOC_MODE_OFF && ++ !qcow2_opts->extended_l2) ++ { ++ error_setg(errp, "Backing file and preallocation can only be used at " ++ "the same time if extended_l2 is on"); ++ ret = -EINVAL; ++ goto out; ++ } ++ if (qcow2_opts->has_backing_fmt && !qcow2_opts->backing_file) { ++ error_setg(errp, "Backing format cannot be used without backing file"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (!qcow2_opts->has_lazy_refcounts) { ++ qcow2_opts->lazy_refcounts = false; ++ } ++ if (version < 3 && qcow2_opts->lazy_refcounts) { ++ error_setg(errp, "Lazy refcounts only supported with compatibility " ++ "level 1.1 and above (use version=v3 or greater)"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (!qcow2_opts->has_refcount_bits) { ++ qcow2_opts->refcount_bits = 16; ++ } ++ if (qcow2_opts->refcount_bits > 64 || ++ !is_power_of_2(qcow2_opts->refcount_bits)) ++ { ++ error_setg(errp, "Refcount width must be a power of two and may not " ++ "exceed 64 bits"); ++ ret = -EINVAL; ++ goto out; ++ } ++ if (version < 3 && qcow2_opts->refcount_bits != 16) { ++ error_setg(errp, "Different refcount widths than 16 bits require " ++ "compatibility level 1.1 or above (use version=v3 or " ++ "greater)"); ++ ret = -EINVAL; ++ goto out; ++ } ++ refcount_order = ctz32(qcow2_opts->refcount_bits); ++ ++ if (qcow2_opts->data_file_raw && !qcow2_opts->data_file) { ++ error_setg(errp, "data-file-raw requires data-file"); ++ ret = -EINVAL; ++ goto out; ++ } ++ if (qcow2_opts->data_file_raw && qcow2_opts->backing_file) { ++ error_setg(errp, "Backing file and data-file-raw cannot be used at " ++ "the same time"); ++ ret = -EINVAL; ++ goto out; ++ } ++ if (qcow2_opts->data_file_raw && ++ qcow2_opts->preallocation == PREALLOC_MODE_OFF) ++ { ++ /* ++ * data-file-raw means that "the external data file can be ++ * read as a consistent standalone raw image without looking ++ * at the qcow2 metadata." It does not say that the metadata ++ * must be ignored, though (and the qcow2 driver in fact does ++ * not ignore it), so the L1/L2 tables must be present and ++ * give a 1:1 mapping, so you get the same result regardless ++ * of whether you look at the metadata or whether you ignore ++ * it. ++ */ ++ qcow2_opts->preallocation = PREALLOC_MODE_METADATA; ++ ++ /* ++ * Cannot use preallocation with backing files, but giving a ++ * backing file when specifying data_file_raw is an error ++ * anyway. ++ */ ++ assert(!qcow2_opts->backing_file); ++ } ++ ++ if (qcow2_opts->data_file) { ++ if (version < 3) { ++ error_setg(errp, "External data files are only supported with " ++ "compatibility level 1.1 and above (use version=v3 or " ++ "greater)"); ++ ret = -EINVAL; ++ goto out; ++ } ++ data_bs = bdrv_co_open_blockdev_ref(qcow2_opts->data_file, errp); ++ if (data_bs == NULL) { ++ ret = -EIO; ++ goto out; ++ } ++ } ++ ++ if (qcow2_opts->has_compression_type && ++ qcow2_opts->compression_type != QCOW2_COMPRESSION_TYPE_ZLIB) { ++ ++ ret = -EINVAL; ++ ++ if (version < 3) { ++ error_setg(errp, "Non-zlib compression type is only supported with " ++ "compatibility level 1.1 and above (use version=v3 or " ++ "greater)"); ++ goto out; ++ } ++ ++ switch (qcow2_opts->compression_type) { ++#ifdef CONFIG_ZSTD ++ case QCOW2_COMPRESSION_TYPE_ZSTD: ++ break; ++#endif ++ default: ++ error_setg(errp, "Unknown compression type"); ++ goto out; ++ } ++ ++ compression_type = qcow2_opts->compression_type; ++ } ++ ++ /* Create BlockBackend to write to the image */ ++ blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL, ++ errp); ++ if (!blk) { ++ ret = -EPERM; ++ goto out; ++ } ++ blk_set_allow_write_beyond_eof(blk, true); ++ ++ /* Write the header */ ++ QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header)); ++ header = g_malloc0(cluster_size); ++ *header = (QCowHeader) { ++ .magic = cpu_to_be32(QCOW_MAGIC), ++ .version = cpu_to_be32(version), ++ .cluster_bits = cpu_to_be32(ctz32(cluster_size)), ++ .size = cpu_to_be64(0), ++ .l1_table_offset = cpu_to_be64(0), ++ .l1_size = cpu_to_be32(0), ++ .refcount_table_offset = cpu_to_be64(cluster_size), ++ .refcount_table_clusters = cpu_to_be32(1), ++ .refcount_order = cpu_to_be32(refcount_order), ++ /* don't deal with endianness since compression_type is 1 byte long */ ++ .compression_type = compression_type, ++ .header_length = cpu_to_be32(sizeof(*header)), ++ }; ++ ++ /* We'll update this to correct value later */ ++ header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); ++ ++ if (qcow2_opts->lazy_refcounts) { ++ header->compatible_features |= ++ cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); ++ } ++ if (data_bs) { ++ header->incompatible_features |= ++ cpu_to_be64(QCOW2_INCOMPAT_DATA_FILE); ++ } ++ if (qcow2_opts->data_file_raw) { ++ header->autoclear_features |= ++ cpu_to_be64(QCOW2_AUTOCLEAR_DATA_FILE_RAW); ++ } ++ if (compression_type != QCOW2_COMPRESSION_TYPE_ZLIB) { ++ header->incompatible_features |= ++ cpu_to_be64(QCOW2_INCOMPAT_COMPRESSION); ++ } ++ ++ if (qcow2_opts->extended_l2) { ++ header->incompatible_features |= ++ cpu_to_be64(QCOW2_INCOMPAT_EXTL2); ++ } ++ ++ 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"); ++ goto out; ++ } ++ ++ /* 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_co_pwrite(blk, cluster_size, 2 * cluster_size, refcount_table, 0); ++ g_free(refcount_table); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not write refcount table"); ++ goto out; ++ } ++ ++ blk_co_unref(blk); ++ blk = NULL; ++ ++ /* ++ * And now open the image and make it consistent first (i.e. increase the ++ * refcount of the cluster that is occupied by the header and the refcount ++ * table) ++ */ ++ options = qdict_new(); ++ qdict_put_str(options, "driver", "qcow2"); ++ qdict_put_str(options, "file", bs->node_name); ++ if (data_bs) { ++ qdict_put_str(options, "data-file", data_bs->node_name); ++ } ++ blk = blk_co_new_open(NULL, NULL, options, ++ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH, ++ errp); ++ if (blk == NULL) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ bdrv_graph_co_rdlock(); ++ ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size); ++ if (ret < 0) { ++ bdrv_graph_co_rdunlock(); ++ error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 " ++ "header and refcount table"); ++ goto out; ++ ++ } else if (ret != 0) { ++ error_report("Huh, first cluster in empty image is already in use?"); ++ abort(); ++ } ++ ++ /* Set the external data file if necessary */ ++ if (data_bs) { ++ BDRVQcow2State *s = blk_bs(blk)->opaque; ++ s->image_data_file = g_strdup(data_bs->filename); ++ } ++ ++ /* Create a full header (including things like feature table) */ ++ ret = qcow2_update_header(blk_bs(blk)); ++ bdrv_graph_co_rdunlock(); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not update qcow2 header"); ++ goto out; ++ } ++ ++ /* Okay, now that we have a valid image, let's give it the right size */ ++ 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; ++ } ++ ++ /* Want a backing file? There you go. */ ++ if (qcow2_opts->backing_file) { ++ const char *backing_format = NULL; ++ ++ if (qcow2_opts->has_backing_fmt) { ++ backing_format = BlockdevDriver_str(qcow2_opts->backing_fmt); ++ } ++ ++ bdrv_graph_co_rdlock(); ++ ret = bdrv_co_change_backing_file(blk_bs(blk), qcow2_opts->backing_file, ++ backing_format, false); ++ bdrv_graph_co_rdunlock(); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Could not assign backing file '%s' " ++ "with format '%s'", qcow2_opts->backing_file, ++ backing_format); ++ goto out; ++ } ++ } ++ ++ /* Want encryption? There you go. */ ++ if (qcow2_opts->encrypt) { ++ bdrv_graph_co_rdlock(); ++ ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp); ++ bdrv_graph_co_rdunlock(); ++ ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ blk_co_unref(blk); ++ blk = NULL; ++ ++ /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning. ++ * Using BDRV_O_NO_IO, since encryption is now setup we don't want to ++ * have to setup decryption context. We're not doing any I/O on the top ++ * level BlockDriverState, only lower layers, where BDRV_O_NO_IO does ++ * not have effect. ++ */ ++ options = qdict_new(); ++ qdict_put_str(options, "driver", "qcow2"); ++ qdict_put_str(options, "file", bs->node_name); ++ if (data_bs) { ++ qdict_put_str(options, "data-file", data_bs->node_name); ++ } ++ blk = blk_co_new_open(NULL, NULL, options, ++ BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO, ++ errp); ++ if (blk == NULL) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ ret = 0; ++out: ++ blk_co_unref(blk); ++ bdrv_co_unref(bs); ++ bdrv_co_unref(data_bs); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_UNLOCKED ++qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts, ++ Error **errp) ++{ ++ BlockdevCreateOptions *create_options = NULL; ++ QDict *qdict; ++ Visitor *v; ++ BlockDriverState *bs = NULL; ++ BlockDriverState *data_bs = NULL; ++ const char *val; ++ int ret; ++ ++ /* Only the keyval visitor supports the dotted syntax needed for ++ * encryption, so go through a QDict before getting a QAPI type. Ignore ++ * options meant for the protocol layer so that the visitor doesn't ++ * complain. */ ++ qdict = qemu_opts_to_qdict_filtered(opts, NULL, bdrv_qcow2.create_opts, ++ true); ++ ++ /* Handle encryption options */ ++ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT); ++ if (val && !strcmp(val, "on")) { ++ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow"); ++ } else if (val && !strcmp(val, "off")) { ++ qdict_del(qdict, BLOCK_OPT_ENCRYPT); ++ } ++ ++ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT); ++ if (val && !strcmp(val, "aes")) { ++ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow"); ++ } ++ ++ /* Convert compat=0.10/1.1 into compat=v2/v3, to be renamed into ++ * version=v2/v3 below. */ ++ val = qdict_get_try_str(qdict, BLOCK_OPT_COMPAT_LEVEL); ++ if (val && !strcmp(val, "0.10")) { ++ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v2"); ++ } else if (val && !strcmp(val, "1.1")) { ++ qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3"); ++ } ++ ++ /* Change legacy command line options into QMP ones */ ++ static const QDictRenames opt_renames[] = { ++ { BLOCK_OPT_BACKING_FILE, "backing-file" }, ++ { BLOCK_OPT_BACKING_FMT, "backing-fmt" }, ++ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" }, ++ { BLOCK_OPT_LAZY_REFCOUNTS, "lazy-refcounts" }, ++ { BLOCK_OPT_EXTL2, "extended-l2" }, ++ { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" }, ++ { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT }, ++ { BLOCK_OPT_COMPAT_LEVEL, "version" }, ++ { BLOCK_OPT_DATA_FILE_RAW, "data-file-raw" }, ++ { BLOCK_OPT_COMPRESSION_TYPE, "compression-type" }, ++ { NULL, NULL }, ++ }; ++ ++ if (!qdict_rename_keys(qdict, opt_renames, errp)) { ++ ret = -EINVAL; ++ goto finish; ++ } ++ ++ /* Create and open the file (protocol layer) */ ++ ret = bdrv_co_create_file(filename, opts, errp); ++ if (ret < 0) { ++ goto finish; ++ } ++ ++ bs = bdrv_co_open(filename, NULL, NULL, ++ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp); ++ if (bs == NULL) { ++ ret = -EIO; ++ goto finish; ++ } ++ ++ /* Create and open an external data file (protocol layer) */ ++ val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE); ++ if (val) { ++ ret = bdrv_co_create_file(val, opts, errp); ++ if (ret < 0) { ++ goto finish; ++ } ++ ++ data_bs = bdrv_co_open(val, NULL, NULL, ++ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, ++ errp); ++ if (data_bs == NULL) { ++ ret = -EIO; ++ goto finish; ++ } ++ ++ qdict_del(qdict, BLOCK_OPT_DATA_FILE); ++ qdict_put_str(qdict, "data-file", data_bs->node_name); ++ } ++ ++ /* Set 'driver' and 'node' options */ ++ qdict_put_str(qdict, "driver", "qcow2"); ++ qdict_put_str(qdict, "file", bs->node_name); ++ ++ /* Now get the QAPI type BlockdevCreateOptions */ ++ v = qobject_input_visitor_new_flat_confused(qdict, errp); ++ if (!v) { ++ ret = -EINVAL; ++ goto finish; ++ } ++ ++ visit_type_BlockdevCreateOptions(v, NULL, &create_options, errp); ++ visit_free(v); ++ if (!create_options) { ++ ret = -EINVAL; ++ goto finish; ++ } ++ ++ /* Silently round up size */ ++ create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size, ++ BDRV_SECTOR_SIZE); ++ ++ /* Create the qcow2 image (format layer) */ ++ ret = qcow2_co_create(create_options, errp); ++finish: ++ if (ret < 0) { ++ bdrv_graph_co_rdlock(); ++ bdrv_co_delete_file_noerr(bs); ++ bdrv_co_delete_file_noerr(data_bs); ++ bdrv_graph_co_rdunlock(); ++ } else { ++ ret = 0; ++ } ++ ++ qobject_unref(qdict); ++ bdrv_co_unref(bs); ++ bdrv_co_unref(data_bs); ++ qapi_free_BlockdevCreateOptions(create_options); ++ return ret; ++} ++ ++ ++static bool coroutine_fn GRAPH_RDLOCK ++is_zero(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ int64_t nr; ++ int res; ++ ++ /* Clamp to image length, before checking status of underlying sectors */ ++ if (offset + bytes > bs->total_sectors * BDRV_SECTOR_SIZE) { ++ bytes = bs->total_sectors * BDRV_SECTOR_SIZE - offset; ++ } ++ ++ if (!bytes) { ++ return true; ++ } ++ ++ /* ++ * bdrv_block_status_above doesn't merge different types of zeros, for ++ * example, zeros which come from the region which is unallocated in ++ * the whole backing chain, and zeros which come because of a short ++ * backing file. So, we need a loop. ++ */ ++ do { ++ res = bdrv_co_block_status_above(bs, NULL, offset, bytes, &nr, NULL, NULL); ++ offset += nr; ++ bytes -= nr; ++ } while (res >= 0 && (res & BDRV_BLOCK_ZERO) && nr && bytes); ++ ++ return res >= 0 && (res & BDRV_BLOCK_ZERO) && bytes == 0; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, ++ BdrvRequestFlags flags) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ ++ uint32_t head = offset_into_subcluster(s, offset); ++ uint32_t tail = ROUND_UP(offset + bytes, s->subcluster_size) - ++ (offset + bytes); ++ ++ trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, bytes); ++ if (offset + bytes == bs->total_sectors * BDRV_SECTOR_SIZE) { ++ tail = 0; ++ } ++ ++ if (head || tail) { ++ uint64_t off; ++ unsigned int nr; ++ QCow2SubclusterType type; ++ ++ assert(head + bytes + tail <= s->subcluster_size); ++ ++ /* check whether remainder of cluster already reads as zero */ ++ if (!(is_zero(bs, offset - head, head) && ++ is_zero(bs, offset + bytes, tail))) { ++ return -ENOTSUP; ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ /* We can have new write after previous check */ ++ offset -= head; ++ bytes = s->subcluster_size; ++ nr = s->subcluster_size; ++ ret = qcow2_get_host_offset(bs, offset, &nr, &off, &type); ++ if (ret < 0 || ++ (type != QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN && ++ type != QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC && ++ type != QCOW2_SUBCLUSTER_ZERO_PLAIN && ++ type != QCOW2_SUBCLUSTER_ZERO_ALLOC)) { ++ qemu_co_mutex_unlock(&s->lock); ++ return ret < 0 ? ret : -ENOTSUP; ++ } ++ } else { ++ qemu_co_mutex_lock(&s->lock); ++ } ++ ++ trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, bytes); ++ ++ /* Whatever is left can use real zero subclusters */ ++ ret = qcow2_subcluster_zeroize(bs, offset, bytes, flags); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) ++{ ++ int ret; ++ BDRVQcow2State *s = bs->opaque; ++ ++ /* If the image does not support QCOW_OFLAG_ZERO then discarding ++ * clusters could expose stale data from the backing file. */ ++ if (s->qcow_version < 3 && bs->backing) { ++ return -ENOTSUP; ++ } ++ ++ if (!QEMU_IS_ALIGNED(offset | bytes, s->cluster_size)) { ++ assert(bytes < s->cluster_size); ++ /* Ignore partial clusters, except for the special case of the ++ * complete partial cluster at the end of an unaligned file */ ++ if (!QEMU_IS_ALIGNED(offset, s->cluster_size) || ++ offset + bytes != bs->total_sectors * BDRV_SECTOR_SIZE) { ++ return -ENOTSUP; ++ } ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_cluster_discard(bs, offset, bytes, QCOW2_DISCARD_REQUEST, ++ false); ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_copy_range_from(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ unsigned int cur_bytes; /* number of bytes in current iteration */ ++ BdrvChild *child = NULL; ++ BdrvRequestFlags cur_write_flags; ++ ++ assert(!bs->encrypted); ++ qemu_co_mutex_lock(&s->lock); ++ ++ while (bytes != 0) { ++ uint64_t copy_offset = 0; ++ QCow2SubclusterType type; ++ /* prepare next request */ ++ cur_bytes = MIN(bytes, INT_MAX); ++ cur_write_flags = write_flags; ++ ++ ret = qcow2_get_host_offset(bs, src_offset, &cur_bytes, ++ ©_offset, &type); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ switch (type) { ++ case QCOW2_SUBCLUSTER_UNALLOCATED_PLAIN: ++ case QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC: ++ if (bs->backing && bs->backing->bs) { ++ int64_t backing_length = bdrv_co_getlength(bs->backing->bs); ++ if (src_offset >= backing_length) { ++ cur_write_flags |= BDRV_REQ_ZERO_WRITE; ++ } else { ++ child = bs->backing; ++ cur_bytes = MIN(cur_bytes, backing_length - src_offset); ++ copy_offset = src_offset; ++ } ++ } else { ++ cur_write_flags |= BDRV_REQ_ZERO_WRITE; ++ } ++ break; ++ ++ case QCOW2_SUBCLUSTER_ZERO_PLAIN: ++ case QCOW2_SUBCLUSTER_ZERO_ALLOC: ++ cur_write_flags |= BDRV_REQ_ZERO_WRITE; ++ break; ++ ++ case QCOW2_SUBCLUSTER_COMPRESSED: ++ ret = -ENOTSUP; ++ goto out; ++ ++ case QCOW2_SUBCLUSTER_NORMAL: ++ child = s->data_file; ++ break; ++ ++ default: ++ abort(); ++ } ++ qemu_co_mutex_unlock(&s->lock); ++ ret = bdrv_co_copy_range_from(child, ++ copy_offset, ++ dst, dst_offset, ++ cur_bytes, read_flags, cur_write_flags); ++ qemu_co_mutex_lock(&s->lock); ++ if (ret < 0) { ++ goto out; ++ } ++ ++ bytes -= cur_bytes; ++ src_offset += cur_bytes; ++ dst_offset += cur_bytes; ++ } ++ ret = 0; ++ ++out: ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_copy_range_to(BlockDriverState *bs, ++ BdrvChild *src, int64_t src_offset, ++ BdrvChild *dst, int64_t dst_offset, ++ int64_t bytes, BdrvRequestFlags read_flags, ++ BdrvRequestFlags write_flags) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ unsigned int cur_bytes; /* number of sectors in current iteration */ ++ uint64_t host_offset; ++ QCowL2Meta *l2meta = NULL; ++ ++ assert(!bs->encrypted); ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ while (bytes != 0) { ++ ++ l2meta = NULL; ++ ++ cur_bytes = MIN(bytes, INT_MAX); ++ ++ /* TODO: ++ * If src->bs == dst->bs, we could simply copy by incrementing ++ * the refcnt, without copying user data. ++ * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */ ++ ret = qcow2_alloc_host_offset(bs, dst_offset, &cur_bytes, ++ &host_offset, &l2meta); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, host_offset, cur_bytes, ++ true); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ret = bdrv_co_copy_range_to(src, src_offset, s->data_file, host_offset, ++ cur_bytes, read_flags, write_flags); ++ qemu_co_mutex_lock(&s->lock); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_handle_l2meta(bs, &l2meta, true); ++ if (ret) { ++ goto fail; ++ } ++ ++ bytes -= cur_bytes; ++ src_offset += cur_bytes; ++ dst_offset += cur_bytes; ++ } ++ ret = 0; ++ ++fail: ++ qcow2_handle_l2meta(bs, &l2meta, false); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ++ trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, ++ PreallocMode prealloc, BdrvRequestFlags flags, Error **errp) ++{ ++ ERRP_GUARD(); ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t old_length; ++ int64_t new_l1_size; ++ int ret; ++ QDict *options; ++ ++ if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA && ++ prealloc != PREALLOC_MODE_FALLOC && prealloc != PREALLOC_MODE_FULL) ++ { ++ error_setg(errp, "Unsupported preallocation mode '%s'", ++ PreallocMode_str(prealloc)); ++ return -ENOTSUP; ++ } ++ ++ if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { ++ error_setg(errp, "The new size must be a multiple of %u", ++ (unsigned) BDRV_SECTOR_SIZE); ++ return -EINVAL; ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ++ /* ++ * Even though we store snapshot size for all images, it was not ++ * required until v3, so it is not safe to proceed for v2. ++ */ ++ if (s->nb_snapshots && s->qcow_version < 3) { ++ error_setg(errp, "Can't resize a v2 image which has snapshots"); ++ ret = -ENOTSUP; ++ goto fail; ++ } ++ ++ /* See qcow2-bitmap.c for which bitmap scenarios prevent a resize. */ ++ if (qcow2_truncate_bitmaps_check(bs, errp)) { ++ ret = -ENOTSUP; ++ goto fail; ++ } ++ ++ old_length = bs->total_sectors * BDRV_SECTOR_SIZE; ++ new_l1_size = size_to_l1(s, offset); ++ ++ if (offset < old_length) { ++ int64_t last_cluster, old_file_size; ++ if (prealloc != PREALLOC_MODE_OFF) { ++ error_setg(errp, ++ "Preallocation can't be used for shrinking an image"); ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size), ++ old_length - ROUND_UP(offset, ++ s->cluster_size), ++ QCOW2_DISCARD_ALWAYS, true); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to discard cropped clusters"); ++ goto fail; ++ } ++ ++ ret = qcow2_shrink_l1_table(bs, new_l1_size); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to reduce the number of L2 tables"); ++ goto fail; ++ } ++ ++ ret = qcow2_shrink_reftable(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to discard unused refblocks"); ++ goto fail; ++ } ++ ++ old_file_size = bdrv_co_getlength(bs->file->bs); ++ if (old_file_size < 0) { ++ error_setg_errno(errp, -old_file_size, ++ "Failed to inquire current file length"); ++ ret = old_file_size; ++ goto fail; ++ } ++ last_cluster = qcow2_get_last_cluster(bs, old_file_size); ++ if (last_cluster < 0) { ++ error_setg_errno(errp, -last_cluster, ++ "Failed to find the last cluster"); ++ ret = last_cluster; ++ goto fail; ++ } ++ if ((last_cluster + 1) * s->cluster_size < old_file_size) { ++ Error *local_err = NULL; ++ ++ /* ++ * Do not pass @exact here: It will not help the user if ++ * we get an error here just because they wanted to shrink ++ * their qcow2 image (on a block device) with qemu-img. ++ * (And on the qcow2 layer, the @exact requirement is ++ * always fulfilled, so there is no need to pass it on.) ++ */ ++ bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size, ++ false, PREALLOC_MODE_OFF, 0, &local_err); ++ if (local_err) { ++ warn_reportf_err(local_err, ++ "Failed to truncate the tail of the image: "); ++ } ++ } ++ } else { ++ ret = qcow2_grow_l1_table(bs, new_l1_size, true); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to grow the L1 table"); ++ goto fail; ++ } ++ ++ if (data_file_is_raw(bs) && prealloc == PREALLOC_MODE_OFF) { ++ /* ++ * When creating a qcow2 image with data-file-raw, we enforce ++ * at least prealloc=metadata, so that the L1/L2 tables are ++ * fully allocated and reading from the data file will return ++ * the same data as reading from the qcow2 image. When the ++ * image is grown, we must consequently preallocate the ++ * metadata structures to cover the added area. ++ */ ++ prealloc = PREALLOC_MODE_METADATA; ++ } ++ } ++ ++ switch (prealloc) { ++ case PREALLOC_MODE_OFF: ++ if (has_data_file(bs)) { ++ /* ++ * If the caller wants an exact resize, the external data ++ * file should be resized to the exact target size, too, ++ * so we pass @exact here. ++ */ ++ ret = bdrv_co_truncate(s->data_file, offset, exact, prealloc, 0, ++ errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ } ++ break; ++ ++ case PREALLOC_MODE_METADATA: ++ ret = preallocate_co(bs, old_length, offset, prealloc, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ break; ++ ++ case PREALLOC_MODE_FALLOC: ++ case PREALLOC_MODE_FULL: ++ { ++ int64_t allocation_start, host_offset, guest_offset; ++ int64_t clusters_allocated; ++ int64_t old_file_size, last_cluster, new_file_size; ++ uint64_t nb_new_data_clusters, nb_new_l2_tables; ++ bool subclusters_need_allocation = false; ++ ++ /* With a data file, preallocation means just allocating the metadata ++ * and forwarding the truncate request to the data file */ ++ if (has_data_file(bs)) { ++ ret = preallocate_co(bs, old_length, offset, prealloc, errp); ++ if (ret < 0) { ++ goto fail; ++ } ++ break; ++ } ++ ++ old_file_size = bdrv_co_getlength(bs->file->bs); ++ if (old_file_size < 0) { ++ error_setg_errno(errp, -old_file_size, ++ "Failed to inquire current file length"); ++ ret = old_file_size; ++ goto fail; ++ } ++ ++ last_cluster = qcow2_get_last_cluster(bs, old_file_size); ++ if (last_cluster >= 0) { ++ old_file_size = (last_cluster + 1) * s->cluster_size; ++ } else { ++ old_file_size = ROUND_UP(old_file_size, s->cluster_size); ++ } ++ ++ nb_new_data_clusters = (ROUND_UP(offset, s->cluster_size) - ++ start_of_cluster(s, old_length)) >> s->cluster_bits; ++ ++ /* This is an overestimation; we will not actually allocate space for ++ * these in the file but just make sure the new refcount structures are ++ * able to cover them so we will not have to allocate new refblocks ++ * while entering the data blocks in the potentially new L2 tables. ++ * (We do not actually care where the L2 tables are placed. Maybe they ++ * are already allocated or they can be placed somewhere before ++ * @old_file_size. It does not matter because they will be fully ++ * allocated automatically, so they do not need to be covered by the ++ * preallocation. All that matters is that we will not have to allocate ++ * new refcount structures for them.) */ ++ nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters, ++ s->cluster_size / l2_entry_size(s)); ++ /* The cluster range may not be aligned to L2 boundaries, so add one L2 ++ * table for a potential head/tail */ ++ nb_new_l2_tables++; ++ ++ allocation_start = qcow2_refcount_area(bs, old_file_size, ++ nb_new_data_clusters + ++ nb_new_l2_tables, ++ true, 0, 0); ++ if (allocation_start < 0) { ++ error_setg_errno(errp, -allocation_start, ++ "Failed to resize refcount structures"); ++ ret = allocation_start; ++ goto fail; ++ } ++ ++ clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start, ++ nb_new_data_clusters); ++ if (clusters_allocated < 0) { ++ error_setg_errno(errp, -clusters_allocated, ++ "Failed to allocate data clusters"); ++ ret = clusters_allocated; ++ goto fail; ++ } ++ ++ assert(clusters_allocated == nb_new_data_clusters); ++ ++ /* Allocate the data area */ ++ new_file_size = allocation_start + ++ nb_new_data_clusters * s->cluster_size; ++ /* ++ * Image file grows, so @exact does not matter. ++ * ++ * If we need to zero out the new area, try first whether the protocol ++ * driver can already take care of this. ++ */ ++ if (flags & BDRV_REQ_ZERO_WRITE) { ++ ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, ++ BDRV_REQ_ZERO_WRITE, NULL); ++ if (ret >= 0) { ++ flags &= ~BDRV_REQ_ZERO_WRITE; ++ /* Ensure that we read zeroes and not backing file data */ ++ subclusters_need_allocation = true; ++ } ++ } else { ++ ret = -1; ++ } ++ if (ret < 0) { ++ ret = bdrv_co_truncate(bs->file, new_file_size, false, prealloc, 0, ++ errp); ++ } ++ if (ret < 0) { ++ error_prepend(errp, "Failed to resize underlying file: "); ++ qcow2_free_clusters(bs, allocation_start, ++ nb_new_data_clusters * s->cluster_size, ++ QCOW2_DISCARD_OTHER); ++ goto fail; ++ } ++ ++ /* Create the necessary L2 entries */ ++ host_offset = allocation_start; ++ guest_offset = old_length; ++ while (nb_new_data_clusters) { ++ int64_t nb_clusters = MIN( ++ nb_new_data_clusters, ++ s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset)); ++ unsigned cow_start_length = offset_into_cluster(s, guest_offset); ++ QCowL2Meta allocation; ++ guest_offset = start_of_cluster(s, guest_offset); ++ allocation = (QCowL2Meta) { ++ .offset = guest_offset, ++ .alloc_offset = host_offset, ++ .nb_clusters = nb_clusters, ++ .cow_start = { ++ .offset = 0, ++ .nb_bytes = cow_start_length, ++ }, ++ .cow_end = { ++ .offset = nb_clusters << s->cluster_bits, ++ .nb_bytes = 0, ++ }, ++ .prealloc = !subclusters_need_allocation, ++ }; ++ qemu_co_queue_init(&allocation.dependent_requests); ++ ++ ret = qcow2_alloc_cluster_link_l2(bs, &allocation); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update L2 tables"); ++ qcow2_free_clusters(bs, host_offset, ++ nb_new_data_clusters * s->cluster_size, ++ QCOW2_DISCARD_OTHER); ++ goto fail; ++ } ++ ++ guest_offset += nb_clusters * s->cluster_size; ++ host_offset += nb_clusters * s->cluster_size; ++ nb_new_data_clusters -= nb_clusters; ++ } ++ break; ++ } ++ ++ default: ++ g_assert_not_reached(); ++ } ++ ++ if ((flags & BDRV_REQ_ZERO_WRITE) && offset > old_length) { ++ uint64_t zero_start = QEMU_ALIGN_UP(old_length, s->subcluster_size); ++ ++ /* ++ * Use zero clusters as much as we can. qcow2_subcluster_zeroize() ++ * requires a subcluster-aligned start. The end may be unaligned if ++ * it is at the end of the image (which it is here). ++ */ ++ if (offset > zero_start) { ++ ret = qcow2_subcluster_zeroize(bs, zero_start, offset - zero_start, ++ 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to zero out new clusters"); ++ goto fail; ++ } ++ } ++ ++ /* Write explicit zeros for the unaligned head */ ++ if (zero_start > old_length) { ++ uint64_t len = MIN(zero_start, offset) - old_length; ++ uint8_t *buf = qemu_blockalign0(bs, len); ++ QEMUIOVector qiov; ++ qemu_iovec_init_buf(&qiov, buf, len); ++ ++ qemu_co_mutex_unlock(&s->lock); ++ ret = qcow2_co_pwritev_part(bs, old_length, len, &qiov, 0, 0); ++ qemu_co_mutex_lock(&s->lock); ++ ++ qemu_vfree(buf); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to zero out the new area"); ++ goto fail; ++ } ++ } ++ } ++ ++ if (prealloc != PREALLOC_MODE_OFF) { ++ /* Flush metadata before actually changing the image size */ ++ ret = qcow2_write_caches(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, ++ "Failed to flush the preallocated area to disk"); ++ goto fail; ++ } ++ } ++ ++ bs->total_sectors = offset / BDRV_SECTOR_SIZE; ++ ++ /* write updated header.size */ ++ offset = cpu_to_be64(offset); ++ ret = bdrv_co_pwrite_sync(bs->file, offsetof(QCowHeader, size), ++ sizeof(offset), &offset, 0); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update the image size"); ++ goto fail; ++ } ++ ++ s->l1_vm_state_index = new_l1_size; ++ ++ /* Update cache sizes */ ++ options = qdict_clone_shallow(bs->options); ++ ret = qcow2_update_options(bs, options, s->flags, errp); ++ qobject_unref(options); ++ if (ret < 0) { ++ goto fail; ++ } ++ ret = 0; ++fail: ++ qemu_co_mutex_unlock(&s->lock); ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwritev_compressed_task(BlockDriverState *bs, ++ uint64_t offset, uint64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ssize_t out_len; ++ uint8_t *buf, *out_buf; ++ uint64_t cluster_offset; ++ ++ assert(bytes == s->cluster_size || (bytes < s->cluster_size && ++ (offset + bytes == bs->total_sectors << BDRV_SECTOR_BITS))); ++ ++ buf = qemu_blockalign(bs, s->cluster_size); ++ if (bytes < s->cluster_size) { ++ /* Zero-pad last write if image size is not cluster aligned */ ++ memset(buf + bytes, 0, s->cluster_size - bytes); ++ } ++ qemu_iovec_to_buf(qiov, qiov_offset, buf, bytes); ++ ++ out_buf = g_malloc(s->cluster_size); ++ ++ out_len = qcow2_co_compress(bs, out_buf, s->cluster_size - 1, ++ buf, s->cluster_size); ++ if (out_len == -ENOMEM) { ++ /* could not compress: write normal cluster */ ++ ret = qcow2_co_pwritev_part(bs, offset, bytes, qiov, qiov_offset, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ goto success; ++ } else if (out_len < 0) { ++ ret = -EINVAL; ++ goto fail; ++ } ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_alloc_compressed_cluster_offset(bs, offset, out_len, ++ &cluster_offset); ++ if (ret < 0) { ++ qemu_co_mutex_unlock(&s->lock); ++ goto fail; ++ } ++ ++ ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len, true); ++ qemu_co_mutex_unlock(&s->lock); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ BLKDBG_CO_EVENT(s->data_file, BLKDBG_WRITE_COMPRESSED); ++ ret = bdrv_co_pwrite(s->data_file, cluster_offset, out_len, out_buf, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++success: ++ ret = 0; ++fail: ++ qemu_vfree(buf); ++ g_free(out_buf); ++ return ret; ++} ++ ++/* ++ * This function can count as GRAPH_RDLOCK because ++ * qcow2_co_pwritev_compressed_part() holds the graph lock and keeps it until ++ * this coroutine has terminated. ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwritev_compressed_task_entry(AioTask *task) ++{ ++ Qcow2AioTask *t = container_of(task, Qcow2AioTask, task); ++ ++ assert(!t->subcluster_type && !t->l2meta); ++ ++ return qcow2_co_pwritev_compressed_task(t->bs, t->offset, t->bytes, t->qiov, ++ t->qiov_offset); ++} ++ ++/* ++ * XXX: put compressed sectors first, then all the cluster aligned ++ * tables to avoid losing bytes in alignment ++ */ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_pwritev_compressed_part(BlockDriverState *bs, ++ int64_t offset, int64_t bytes, ++ QEMUIOVector *qiov, size_t qiov_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ AioTaskPool *aio = NULL; ++ int ret = 0; ++ ++ if (has_data_file(bs)) { ++ return -ENOTSUP; ++ } ++ ++ if (bytes == 0) { ++ /* ++ * align end of file to a sector boundary to ease reading with ++ * sector based I/Os ++ */ ++ int64_t len = bdrv_co_getlength(bs->file->bs); ++ if (len < 0) { ++ return len; ++ } ++ return bdrv_co_truncate(bs->file, len, false, PREALLOC_MODE_OFF, 0, ++ NULL); ++ } ++ ++ if (offset_into_cluster(s, offset)) { ++ return -EINVAL; ++ } ++ ++ if (offset_into_cluster(s, bytes) && ++ (offset + bytes) != (bs->total_sectors << BDRV_SECTOR_BITS)) { ++ return -EINVAL; ++ } ++ ++ while (bytes && aio_task_pool_status(aio) == 0) { ++ uint64_t chunk_size = MIN(bytes, s->cluster_size); ++ ++ if (!aio && chunk_size != bytes) { ++ aio = aio_task_pool_new(QCOW2_MAX_WORKERS); ++ } ++ ++ ret = qcow2_add_task(bs, aio, qcow2_co_pwritev_compressed_task_entry, ++ 0, 0, offset, chunk_size, qiov, qiov_offset, NULL); ++ if (ret < 0) { ++ break; ++ } ++ qiov_offset += chunk_size; ++ offset += chunk_size; ++ bytes -= chunk_size; ++ } ++ ++ if (aio) { ++ aio_task_pool_wait_all(aio); ++ if (ret == 0) { ++ ret = aio_task_pool_status(aio); ++ } ++ g_free(aio); ++ } ++ ++ return ret; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_preadv_compressed(BlockDriverState *bs, ++ uint64_t l2_entry, ++ uint64_t offset, ++ uint64_t bytes, ++ QEMUIOVector *qiov, ++ size_t qiov_offset) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret = 0, csize; ++ uint64_t coffset; ++ uint8_t *buf, *out_buf; ++ int offset_in_cluster = offset_into_cluster(s, offset); ++ ++ qcow2_parse_compressed_l2_entry(bs, l2_entry, &coffset, &csize); ++ ++ buf = g_try_malloc(csize); ++ if (!buf) { ++ return -ENOMEM; ++ } ++ ++ out_buf = qemu_blockalign(bs, s->cluster_size); ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_COMPRESSED); ++ ret = bdrv_co_pread(bs->file, coffset, csize, buf, 0); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ if (qcow2_co_decompress(bs, out_buf, s->cluster_size, buf, csize) < 0) { ++ ret = -EIO; ++ goto fail; ++ } ++ ++ qemu_iovec_from_buf(qiov, qiov_offset, out_buf + offset_in_cluster, bytes); ++ ++fail: ++ qemu_vfree(out_buf); ++ g_free(buf); ++ ++ return ret; ++} ++ ++static int GRAPH_RDLOCK make_completely_empty(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ Error *local_err = NULL; ++ int ret, l1_clusters; ++ int64_t offset; ++ uint64_t *new_reftable = NULL; ++ uint64_t rt_entry, l1_size2; ++ struct { ++ uint64_t l1_offset; ++ uint64_t reftable_offset; ++ uint32_t reftable_clusters; ++ } QEMU_PACKED l1_ofs_rt_ofs_cls; ++ ++ ret = qcow2_cache_empty(bs, s->l2_table_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = qcow2_cache_empty(bs, s->refcount_block_cache); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ /* Refcounts will be broken utterly */ ++ ret = qcow2_mark_dirty(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ++ ++ l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / L1E_SIZE); ++ l1_size2 = (uint64_t)s->l1_size * L1E_SIZE; ++ ++ /* After this call, neither the in-memory nor the on-disk refcount ++ * information accurately describe the actual references */ ++ ++ ret = bdrv_pwrite_zeroes(bs->file, s->l1_table_offset, ++ l1_clusters * s->cluster_size, 0); ++ if (ret < 0) { ++ goto fail_broken_refcounts; ++ } ++ memset(s->l1_table, 0, l1_size2); ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_EMPTY_IMAGE_PREPARE); ++ ++ /* Overwrite enough clusters at the beginning of the sectors to place ++ * the refcount table, a refcount block and the L1 table in; this may ++ * overwrite parts of the existing refcount and L1 table, which is not ++ * an issue because the dirty flag is set, complete data loss is in fact ++ * desired and partial data loss is consequently fine as well */ ++ ret = bdrv_pwrite_zeroes(bs->file, s->cluster_size, ++ (2 + l1_clusters) * s->cluster_size, 0); ++ /* This call (even if it failed overall) may have overwritten on-disk ++ * refcount structures; in that case, the in-memory refcount information ++ * will probably differ from the on-disk information which makes the BDS ++ * unusable */ ++ if (ret < 0) { ++ goto fail_broken_refcounts; ++ } ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); ++ BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_UPDATE); ++ ++ /* "Create" an empty reftable (one cluster) directly after the image ++ * header and an empty L1 table three clusters after the image header; ++ * the cluster between those two will be used as the first refblock */ ++ l1_ofs_rt_ofs_cls.l1_offset = cpu_to_be64(3 * s->cluster_size); ++ l1_ofs_rt_ofs_cls.reftable_offset = cpu_to_be64(s->cluster_size); ++ l1_ofs_rt_ofs_cls.reftable_clusters = cpu_to_be32(1); ++ ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_table_offset), ++ sizeof(l1_ofs_rt_ofs_cls), &l1_ofs_rt_ofs_cls, 0); ++ if (ret < 0) { ++ goto fail_broken_refcounts; ++ } ++ ++ s->l1_table_offset = 3 * s->cluster_size; ++ ++ new_reftable = g_try_new0(uint64_t, s->cluster_size / REFTABLE_ENTRY_SIZE); ++ if (!new_reftable) { ++ ret = -ENOMEM; ++ goto fail_broken_refcounts; ++ } ++ ++ s->refcount_table_offset = s->cluster_size; ++ s->refcount_table_size = s->cluster_size / REFTABLE_ENTRY_SIZE; ++ s->max_refcount_table_index = 0; ++ ++ g_free(s->refcount_table); ++ s->refcount_table = new_reftable; ++ new_reftable = NULL; ++ ++ /* Now the in-memory refcount information again corresponds to the on-disk ++ * information (reftable is empty and no refblocks (the refblock cache is ++ * empty)); however, this means some clusters (e.g. the image header) are ++ * referenced, but not refcounted, but the normal qcow2 code assumes that ++ * the in-memory information is always correct */ ++ ++ BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC); ++ ++ /* Enter the first refblock into the reftable */ ++ rt_entry = cpu_to_be64(2 * s->cluster_size); ++ ret = bdrv_pwrite_sync(bs->file, s->cluster_size, sizeof(rt_entry), ++ &rt_entry, 0); ++ if (ret < 0) { ++ goto fail_broken_refcounts; ++ } ++ s->refcount_table[0] = 2 * s->cluster_size; ++ ++ s->free_cluster_index = 0; ++ assert(3 + l1_clusters <= s->refcount_block_size); ++ offset = qcow2_alloc_clusters(bs, 3 * s->cluster_size + l1_size2); ++ if (offset < 0) { ++ ret = offset; ++ goto fail_broken_refcounts; ++ } else if (offset > 0) { ++ error_report("First cluster in emptied image is in use"); ++ abort(); ++ } ++ ++ /* Now finally the in-memory information corresponds to the on-disk ++ * structures and is correct */ ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ goto fail; ++ } ++ ++ ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size, false, ++ PREALLOC_MODE_OFF, 0, &local_err); ++ if (ret < 0) { ++ error_report_err(local_err); ++ goto fail; ++ } ++ ++ return 0; ++ ++fail_broken_refcounts: ++ /* The BDS is unusable at this point. If we wanted to make it usable, we ++ * would have to call qcow2_refcount_close(), qcow2_refcount_init(), ++ * qcow2_check_refcounts(), qcow2_refcount_close() and qcow2_refcount_init() ++ * again. However, because the functions which could have caused this error ++ * path to be taken are used by those functions as well, it's very likely ++ * that that sequence will fail as well. Therefore, just eject the BDS. */ ++ bs->drv = NULL; ++ ++fail: ++ g_free(new_reftable); ++ return ret; ++} ++ ++static int GRAPH_RDLOCK qcow2_make_empty(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ uint64_t offset, end_offset; ++ int step = QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size); ++ int l1_clusters, ret = 0; ++ ++ l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / L1E_SIZE); ++ ++ if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps && ++ 3 + l1_clusters <= s->refcount_block_size && ++ s->crypt_method_header != QCOW_CRYPT_LUKS && ++ !has_data_file(bs)) { ++ /* The following function only works for qcow2 v3 images (it ++ * requires the dirty flag) and only as long as there are no ++ * features that reserve extra clusters (such as snapshots, ++ * LUKS header, or persistent bitmaps), because it completely ++ * empties the image. Furthermore, the L1 table and three ++ * additional clusters (image header, refcount table, one ++ * refcount block) have to fit inside one refcount block. It ++ * only resets the image file, i.e. does not work with an ++ * external data file. */ ++ return make_completely_empty(bs); ++ } ++ ++ /* This fallback code simply discards every active cluster; this is slow, ++ * but works in all cases */ ++ end_offset = bs->total_sectors * BDRV_SECTOR_SIZE; ++ for (offset = 0; offset < end_offset; offset += step) { ++ /* As this function is generally used after committing an external ++ * snapshot, QCOW2_DISCARD_SNAPSHOT seems appropriate. Also, the ++ * default action for this kind of discard is to pass the discard, ++ * which will ideally result in an actually smaller image file, as ++ * is probably desired. */ ++ ret = qcow2_cluster_discard(bs, offset, MIN(step, end_offset - offset), ++ QCOW2_DISCARD_SNAPSHOT, true); ++ if (ret < 0) { ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static coroutine_fn GRAPH_RDLOCK int qcow2_co_flush_to_os(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int ret; ++ ++ qemu_co_mutex_lock(&s->lock); ++ ret = qcow2_write_caches(bs); ++ qemu_co_mutex_unlock(&s->lock); ++ ++ return ret; ++} ++ ++static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ BlockMeasureInfo *info; ++ uint64_t required = 0; /* bytes that contribute to required size */ ++ uint64_t virtual_size; /* disk size as seen by guest */ ++ uint64_t refcount_bits; ++ uint64_t l2_tables; ++ uint64_t luks_payload_size = 0; ++ size_t cluster_size; ++ int version; ++ char *optstr; ++ PreallocMode prealloc; ++ bool has_backing_file; ++ bool has_luks; ++ bool extended_l2; ++ size_t l2e_size; ++ ++ /* Parse image creation options */ ++ extended_l2 = qemu_opt_get_bool_del(opts, BLOCK_OPT_EXTL2, false); ++ ++ cluster_size = qcow2_opt_get_cluster_size_del(opts, extended_l2, ++ &local_err); ++ if (local_err) { ++ goto err; ++ } ++ ++ version = qcow2_opt_get_version_del(opts, &local_err); ++ if (local_err) { ++ goto err; ++ } ++ ++ refcount_bits = qcow2_opt_get_refcount_bits_del(opts, version, &local_err); ++ if (local_err) { ++ goto err; ++ } ++ ++ optstr = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); ++ prealloc = qapi_enum_parse(&PreallocMode_lookup, optstr, ++ PREALLOC_MODE_OFF, &local_err); ++ g_free(optstr); ++ if (local_err) { ++ goto err; ++ } ++ ++ optstr = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); ++ has_backing_file = !!optstr; ++ g_free(optstr); ++ ++ optstr = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); ++ has_luks = optstr && strcmp(optstr, "luks") == 0; ++ g_free(optstr); ++ ++ if (has_luks) { ++ g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; ++ QDict *cryptoopts = qcow2_extract_crypto_opts(opts, "luks", errp); ++ size_t headerlen; ++ ++ create_opts = block_crypto_create_opts_init(cryptoopts, errp); ++ qobject_unref(cryptoopts); ++ if (!create_opts) { ++ goto err; ++ } ++ ++ if (!qcrypto_block_calculate_payload_offset(create_opts, ++ "encrypt.", ++ &headerlen, ++ &local_err)) { ++ goto err; ++ } ++ ++ luks_payload_size = ROUND_UP(headerlen, cluster_size); ++ } ++ ++ virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); ++ virtual_size = ROUND_UP(virtual_size, cluster_size); ++ ++ /* Check that virtual disk size is valid */ ++ l2e_size = extended_l2 ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL; ++ l2_tables = DIV_ROUND_UP(virtual_size / cluster_size, ++ cluster_size / l2e_size); ++ if (l2_tables * L1E_SIZE > QCOW_MAX_L1_SIZE) { ++ error_setg(&local_err, "The image size is too large " ++ "(try using a larger cluster size)"); ++ goto err; ++ } ++ ++ /* Account for input image */ ++ if (in_bs) { ++ int64_t ssize = bdrv_getlength(in_bs); ++ if (ssize < 0) { ++ error_setg_errno(&local_err, -ssize, ++ "Unable to get image virtual_size"); ++ goto err; ++ } ++ ++ virtual_size = ROUND_UP(ssize, cluster_size); ++ ++ if (has_backing_file) { ++ /* We don't how much of the backing chain is shared by the input ++ * image and the new image file. In the worst case the new image's ++ * backing file has nothing in common with the input image. Be ++ * conservative and assume all clusters need to be written. ++ */ ++ required = virtual_size; ++ } else { ++ int64_t offset; ++ int64_t pnum = 0; ++ ++ for (offset = 0; offset < ssize; offset += pnum) { ++ int ret; ++ ++ ret = bdrv_block_status_above(in_bs, NULL, offset, ++ ssize - offset, &pnum, NULL, ++ NULL); ++ if (ret < 0) { ++ error_setg_errno(&local_err, -ret, ++ "Unable to get block status"); ++ goto err; ++ } ++ ++ if (ret & BDRV_BLOCK_ZERO) { ++ /* Skip zero regions (safe with no backing file) */ ++ } else if ((ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED)) == ++ (BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED)) { ++ /* Extend pnum to end of cluster for next iteration */ ++ pnum = ROUND_UP(offset + pnum, cluster_size) - offset; ++ ++ /* Count clusters we've seen */ ++ required += offset % cluster_size + pnum; ++ } ++ } ++ } ++ } ++ ++ /* Take into account preallocation. Nothing special is needed for ++ * PREALLOC_MODE_METADATA since metadata is always counted. ++ */ ++ if (prealloc == PREALLOC_MODE_FULL || prealloc == PREALLOC_MODE_FALLOC) { ++ required = virtual_size; ++ } ++ ++ info = g_new0(BlockMeasureInfo, 1); ++ info->fully_allocated = luks_payload_size + ++ qcow2_calc_prealloc_size(virtual_size, cluster_size, ++ ctz32(refcount_bits), extended_l2); ++ ++ /* ++ * Remove data clusters that are not required. This overestimates the ++ * required size because metadata needed for the fully allocated file is ++ * still counted. Show bitmaps only if both source and destination ++ * would support them. ++ */ ++ info->required = info->fully_allocated - virtual_size + required; ++ info->has_bitmaps = version >= 3 && in_bs && ++ bdrv_supports_persistent_dirty_bitmap(in_bs); ++ if (info->has_bitmaps) { ++ info->bitmaps = qcow2_get_persistent_dirty_bitmap_size(in_bs, ++ cluster_size); ++ } ++ return info; ++ ++err: ++ error_propagate(errp, local_err); ++ return NULL; ++} ++ ++static int coroutine_fn ++qcow2_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ bdi->cluster_size = s->cluster_size; ++ bdi->subcluster_size = s->subcluster_size; ++ bdi->vm_state_offset = qcow2_vm_state_offset(s); ++ bdi->is_dirty = s->incompatible_features & QCOW2_INCOMPAT_DIRTY; ++ return 0; ++} ++ ++static ImageInfoSpecific * GRAPH_RDLOCK ++qcow2_get_specific_info(BlockDriverState *bs, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ ImageInfoSpecific *spec_info; ++ QCryptoBlockInfo *encrypt_info = NULL; ++ ++ if (s->crypto != NULL) { ++ encrypt_info = qcrypto_block_get_info(s->crypto, errp); ++ if (!encrypt_info) { ++ return NULL; ++ } ++ } ++ ++ spec_info = g_new(ImageInfoSpecific, 1); ++ *spec_info = (ImageInfoSpecific){ ++ .type = IMAGE_INFO_SPECIFIC_KIND_QCOW2, ++ .u.qcow2.data = g_new0(ImageInfoSpecificQCow2, 1), ++ }; ++ if (s->qcow_version == 2) { ++ *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ ++ .compat = g_strdup("0.10"), ++ .refcount_bits = s->refcount_bits, ++ }; ++ } else if (s->qcow_version == 3) { ++ Qcow2BitmapInfoList *bitmaps; ++ if (!qcow2_get_bitmap_info_list(bs, &bitmaps, errp)) { ++ qapi_free_ImageInfoSpecific(spec_info); ++ qapi_free_QCryptoBlockInfo(encrypt_info); ++ return NULL; ++ } ++ *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ ++ .compat = g_strdup("1.1"), ++ .lazy_refcounts = s->compatible_features & ++ QCOW2_COMPAT_LAZY_REFCOUNTS, ++ .has_lazy_refcounts = true, ++ .corrupt = s->incompatible_features & ++ QCOW2_INCOMPAT_CORRUPT, ++ .has_corrupt = true, ++ .has_extended_l2 = true, ++ .extended_l2 = has_subclusters(s), ++ .refcount_bits = s->refcount_bits, ++ .has_bitmaps = !!bitmaps, ++ .bitmaps = bitmaps, ++ .data_file = g_strdup(s->image_data_file), ++ .has_data_file_raw = has_data_file(bs), ++ .data_file_raw = data_file_is_raw(bs), ++ .compression_type = s->compression_type, ++ }; ++ } else { ++ /* if this assertion fails, this probably means a new version was ++ * added without having it covered here */ ++ assert(false); ++ } ++ ++ if (encrypt_info) { ++ ImageInfoSpecificQCow2Encryption *qencrypt = ++ g_new(ImageInfoSpecificQCow2Encryption, 1); ++ switch (encrypt_info->format) { ++ case Q_CRYPTO_BLOCK_FORMAT_QCOW: ++ qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES; ++ break; ++ case Q_CRYPTO_BLOCK_FORMAT_LUKS: ++ qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS; ++ qencrypt->u.luks = encrypt_info->u.luks; ++ break; ++ default: ++ abort(); ++ } ++ /* Since we did shallow copy above, erase any pointers ++ * in the original info */ ++ memset(&encrypt_info->u, 0, sizeof(encrypt_info->u)); ++ qapi_free_QCryptoBlockInfo(encrypt_info); ++ ++ spec_info->u.qcow2.data->encrypt = qencrypt; ++ } ++ ++ return spec_info; ++} ++ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++qcow2_has_zero_init(BlockDriverState *bs) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ bool preallocated; ++ ++ if (qemu_in_coroutine()) { ++ qemu_co_mutex_lock(&s->lock); ++ } ++ /* ++ * Check preallocation status: Preallocated images have all L2 ++ * tables allocated, nonpreallocated images have none. It is ++ * therefore enough to check the first one. ++ */ ++ preallocated = s->l1_size > 0 && s->l1_table[0] != 0; ++ if (qemu_in_coroutine()) { ++ qemu_co_mutex_unlock(&s->lock); ++ } ++ ++ if (!preallocated) { ++ return 1; ++ } else if (bs->encrypted) { ++ return 0; ++ } else { ++ return bdrv_has_zero_init(s->data_file->bs); ++ } ++} ++ ++/* ++ * Check the request to vmstate. On success return ++ * qcow2_vm_state_offset(bs) + @pos ++ */ ++static int64_t qcow2_check_vmstate_request(BlockDriverState *bs, ++ QEMUIOVector *qiov, int64_t pos) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int64_t vmstate_offset = qcow2_vm_state_offset(s); ++ int ret; ++ ++ /* Incoming requests must be OK */ ++ bdrv_check_qiov_request(pos, qiov->size, qiov, 0, &error_abort); ++ ++ if (INT64_MAX - pos < vmstate_offset) { ++ return -EIO; ++ } ++ ++ pos += vmstate_offset; ++ ret = bdrv_check_qiov_request(pos, qiov->size, qiov, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ return pos; ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); ++ if (offset < 0) { ++ return offset; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); ++ return bs->drv->bdrv_co_pwritev_part(bs, offset, qiov->size, qiov, 0, 0); ++} ++ ++static int coroutine_fn GRAPH_RDLOCK ++qcow2_co_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) ++{ ++ int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos); ++ if (offset < 0) { ++ return offset; ++ } ++ ++ BLKDBG_CO_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); ++ return bs->drv->bdrv_co_preadv_part(bs, offset, qiov->size, qiov, 0, 0); ++} ++ ++static int GRAPH_RDLOCK qcow2_has_compressed_clusters(BlockDriverState *bs) ++{ ++ int64_t offset = 0; ++ int64_t bytes = bdrv_getlength(bs); ++ ++ if (bytes < 0) { ++ return bytes; ++ } ++ ++ while (bytes != 0) { ++ int ret; ++ QCow2SubclusterType type; ++ unsigned int cur_bytes = MIN(INT_MAX, bytes); ++ uint64_t host_offset; ++ ++ ret = qcow2_get_host_offset(bs, offset, &cur_bytes, &host_offset, ++ &type); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ if (type == QCOW2_SUBCLUSTER_COMPRESSED) { ++ return 1; ++ } ++ ++ offset += cur_bytes; ++ bytes -= cur_bytes; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Downgrades an image's version. To achieve this, any incompatible features ++ * have to be removed. ++ */ ++static int GRAPH_RDLOCK ++qcow2_downgrade(BlockDriverState *bs, int target_version, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int current_version = s->qcow_version; ++ int ret; ++ int i; ++ ++ /* This is qcow2_downgrade(), not qcow2_upgrade() */ ++ assert(target_version < current_version); ++ ++ /* There are no other versions (now) that you can downgrade to */ ++ assert(target_version == 2); ++ ++ if (s->refcount_order != 4) { ++ error_setg(errp, "compat=0.10 requires refcount_bits=16"); ++ return -ENOTSUP; ++ } ++ ++ if (has_data_file(bs)) { ++ error_setg(errp, "Cannot downgrade an image with a data file"); ++ return -ENOTSUP; ++ } ++ ++ /* ++ * If any internal snapshot has a different size than the current ++ * image size, or VM state size that exceeds 32 bits, downgrading ++ * is unsafe. Even though we would still use v3-compliant output ++ * to preserve that data, other v2 programs might not realize ++ * those optional fields are important. ++ */ ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (s->snapshots[i].vm_state_size > UINT32_MAX || ++ s->snapshots[i].disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) { ++ error_setg(errp, "Internal snapshots prevent downgrade of image"); ++ return -ENOTSUP; ++ } ++ } ++ ++ /* clear incompatible features */ ++ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to make the image clean"); ++ return ret; ++ } ++ } ++ ++ /* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in ++ * the first place; if that happens nonetheless, returning -ENOTSUP is the ++ * best thing to do anyway */ ++ ++ if (s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION) { ++ error_setg(errp, "Cannot downgrade an image with incompatible features " ++ "0x%" PRIx64 " set", ++ s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION); ++ return -ENOTSUP; ++ } ++ ++ /* since we can ignore compatible features, we can set them to 0 as well */ ++ s->compatible_features = 0; ++ /* if lazy refcounts have been used, they have already been fixed through ++ * clearing the dirty flag */ ++ ++ /* clearing autoclear features is trivial */ ++ s->autoclear_features = 0; ++ ++ ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to turn zero into data clusters"); ++ return ret; ++ } ++ ++ if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION) { ++ ret = qcow2_has_compressed_clusters(bs); ++ if (ret < 0) { ++ error_setg(errp, "Failed to check block status"); ++ return -EINVAL; ++ } ++ if (ret) { ++ error_setg(errp, "Cannot downgrade an image with zstd compression " ++ "type and existing compressed clusters"); ++ return -ENOTSUP; ++ } ++ /* ++ * No compressed clusters for now, so just chose default zlib ++ * compression. ++ */ ++ s->incompatible_features &= ~QCOW2_INCOMPAT_COMPRESSION; ++ s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB; ++ } ++ ++ assert(s->incompatible_features == 0); ++ ++ s->qcow_version = target_version; ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->qcow_version = current_version; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ return 0; ++} ++ ++/* ++ * Upgrades an image's version. While newer versions encompass all ++ * features of older versions, some things may have to be presented ++ * differently. ++ */ ++static int GRAPH_RDLOCK ++qcow2_upgrade(BlockDriverState *bs, int target_version, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ bool need_snapshot_update; ++ int current_version = s->qcow_version; ++ int i; ++ int ret; ++ ++ /* This is qcow2_upgrade(), not qcow2_downgrade() */ ++ assert(target_version > current_version); ++ ++ /* There are no other versions (yet) that you can upgrade to */ ++ assert(target_version == 3); ++ ++ status_cb(bs, 0, 2, cb_opaque); ++ ++ /* ++ * In v2, snapshots do not need to have extra data. v3 requires ++ * the 64-bit VM state size and the virtual disk size to be ++ * present. ++ * qcow2_write_snapshots() will always write the list in the ++ * v3-compliant format. ++ */ ++ need_snapshot_update = false; ++ for (i = 0; i < s->nb_snapshots; i++) { ++ if (s->snapshots[i].extra_data_size < ++ sizeof_field(QCowSnapshotExtraData, vm_state_size_large) + ++ sizeof_field(QCowSnapshotExtraData, disk_size)) ++ { ++ need_snapshot_update = true; ++ break; ++ } ++ } ++ if (need_snapshot_update) { ++ ret = qcow2_write_snapshots(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update the snapshot table"); ++ return ret; ++ } ++ } ++ status_cb(bs, 1, 2, cb_opaque); ++ ++ s->qcow_version = target_version; ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->qcow_version = current_version; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ status_cb(bs, 2, 2, cb_opaque); ++ ++ return 0; ++} ++ ++typedef enum Qcow2AmendOperation { ++ /* This is the value Qcow2AmendHelperCBInfo::last_operation will be ++ * statically initialized to so that the helper CB can discern the first ++ * invocation from an operation change */ ++ QCOW2_NO_OPERATION = 0, ++ ++ QCOW2_UPGRADING, ++ QCOW2_UPDATING_ENCRYPTION, ++ QCOW2_CHANGING_REFCOUNT_ORDER, ++ QCOW2_DOWNGRADING, ++} Qcow2AmendOperation; ++ ++typedef struct Qcow2AmendHelperCBInfo { ++ /* The code coordinating the amend operations should only modify ++ * these four fields; the rest will be managed by the CB */ ++ BlockDriverAmendStatusCB *original_status_cb; ++ void *original_cb_opaque; ++ ++ Qcow2AmendOperation current_operation; ++ ++ /* Total number of operations to perform (only set once) */ ++ int total_operations; ++ ++ /* The following fields are managed by the CB */ ++ ++ /* Number of operations completed */ ++ int operations_completed; ++ ++ /* Cumulative offset of all completed operations */ ++ int64_t offset_completed; ++ ++ Qcow2AmendOperation last_operation; ++ int64_t last_work_size; ++} Qcow2AmendHelperCBInfo; ++ ++static void qcow2_amend_helper_cb(BlockDriverState *bs, ++ int64_t operation_offset, ++ int64_t operation_work_size, void *opaque) ++{ ++ Qcow2AmendHelperCBInfo *info = opaque; ++ int64_t current_work_size; ++ int64_t projected_work_size; ++ ++ if (info->current_operation != info->last_operation) { ++ if (info->last_operation != QCOW2_NO_OPERATION) { ++ info->offset_completed += info->last_work_size; ++ info->operations_completed++; ++ } ++ ++ info->last_operation = info->current_operation; ++ } ++ ++ assert(info->total_operations > 0); ++ assert(info->operations_completed < info->total_operations); ++ ++ info->last_work_size = operation_work_size; ++ ++ current_work_size = info->offset_completed + operation_work_size; ++ ++ /* current_work_size is the total work size for (operations_completed + 1) ++ * operations (which includes this one), so multiply it by the number of ++ * operations not covered and divide it by the number of operations ++ * covered to get a projection for the operations not covered */ ++ projected_work_size = current_work_size * (info->total_operations - ++ info->operations_completed - 1) ++ / (info->operations_completed + 1); ++ ++ info->original_status_cb(bs, info->offset_completed + operation_offset, ++ current_work_size + projected_work_size, ++ info->original_cb_opaque); ++} ++ ++static int GRAPH_RDLOCK ++qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, ++ BlockDriverAmendStatusCB *status_cb, void *cb_opaque, ++ bool force, Error **errp) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ int old_version = s->qcow_version, new_version = old_version; ++ uint64_t new_size = 0; ++ const char *backing_file = NULL, *backing_format = NULL, *data_file = NULL; ++ bool lazy_refcounts = s->use_lazy_refcounts; ++ bool data_file_raw = data_file_is_raw(bs); ++ const char *compat = NULL; ++ int refcount_bits = s->refcount_bits; ++ int ret; ++ QemuOptDesc *desc = opts->list->desc; ++ Qcow2AmendHelperCBInfo helper_cb_info; ++ bool encryption_update = false; ++ ++ while (desc && desc->name) { ++ if (!qemu_opt_find(opts, desc->name)) { ++ /* only change explicitly defined options */ ++ desc++; ++ continue; ++ } ++ ++ if (!strcmp(desc->name, BLOCK_OPT_COMPAT_LEVEL)) { ++ compat = qemu_opt_get(opts, BLOCK_OPT_COMPAT_LEVEL); ++ if (!compat) { ++ /* preserve default */ ++ } else if (!strcmp(compat, "0.10") || !strcmp(compat, "v2")) { ++ new_version = 2; ++ } else if (!strcmp(compat, "1.1") || !strcmp(compat, "v3")) { ++ new_version = 3; ++ } else { ++ error_setg(errp, "Unknown compatibility level %s", compat); ++ return -EINVAL; ++ } ++ } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { ++ new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); ++ } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) { ++ backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); ++ } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { ++ backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); ++ } else if (g_str_has_prefix(desc->name, "encrypt.")) { ++ if (!s->crypto) { ++ error_setg(errp, ++ "Can't amend encryption options - encryption not present"); ++ return -EINVAL; ++ } ++ if (s->crypt_method_header != QCOW_CRYPT_LUKS) { ++ error_setg(errp, ++ "Only LUKS encryption options can be amended"); ++ return -ENOTSUP; ++ } ++ encryption_update = true; ++ } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { ++ lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, ++ lazy_refcounts); ++ } else if (!strcmp(desc->name, BLOCK_OPT_REFCOUNT_BITS)) { ++ refcount_bits = qemu_opt_get_number(opts, BLOCK_OPT_REFCOUNT_BITS, ++ refcount_bits); ++ ++ if (refcount_bits <= 0 || refcount_bits > 64 || ++ !is_power_of_2(refcount_bits)) ++ { ++ error_setg(errp, "Refcount width must be a power of two and " ++ "may not exceed 64 bits"); ++ return -EINVAL; ++ } ++ } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE)) { ++ data_file = qemu_opt_get(opts, BLOCK_OPT_DATA_FILE); ++ if (data_file && !has_data_file(bs)) { ++ error_setg(errp, "data-file can only be set for images that " ++ "use an external data file"); ++ return -EINVAL; ++ } ++ } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE_RAW)) { ++ data_file_raw = qemu_opt_get_bool(opts, BLOCK_OPT_DATA_FILE_RAW, ++ data_file_raw); ++ if (data_file_raw && !data_file_is_raw(bs)) { ++ error_setg(errp, "data-file-raw cannot be set on existing " ++ "images"); ++ return -EINVAL; ++ } ++ } else { ++ /* if this point is reached, this probably means a new option was ++ * added without having it covered here */ ++ abort(); ++ } ++ ++ desc++; ++ } ++ ++ helper_cb_info = (Qcow2AmendHelperCBInfo){ ++ .original_status_cb = status_cb, ++ .original_cb_opaque = cb_opaque, ++ .total_operations = (new_version != old_version) ++ + (s->refcount_bits != refcount_bits) + ++ (encryption_update == true) ++ }; ++ ++ /* Upgrade first (some features may require compat=1.1) */ ++ if (new_version > old_version) { ++ helper_cb_info.current_operation = QCOW2_UPGRADING; ++ ret = qcow2_upgrade(bs, new_version, &qcow2_amend_helper_cb, ++ &helper_cb_info, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (encryption_update) { ++ QDict *amend_opts_dict; ++ QCryptoBlockAmendOptions *amend_opts; ++ ++ helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION; ++ amend_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp); ++ if (!amend_opts_dict) { ++ return -EINVAL; ++ } ++ amend_opts = block_crypto_amend_opts_init(amend_opts_dict, errp); ++ qobject_unref(amend_opts_dict); ++ if (!amend_opts) { ++ return -EINVAL; ++ } ++ ret = qcrypto_block_amend_options(s->crypto, ++ qcow2_crypto_hdr_read_func, ++ qcow2_crypto_hdr_write_func, ++ bs, ++ amend_opts, ++ force, ++ errp); ++ qapi_free_QCryptoBlockAmendOptions(amend_opts); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ if (s->refcount_bits != refcount_bits) { ++ int refcount_order = ctz32(refcount_bits); ++ ++ if (new_version < 3 && refcount_bits != 16) { ++ error_setg(errp, "Refcount widths other than 16 bits require " ++ "compatibility level 1.1 or above (use compat=1.1 or " ++ "greater)"); ++ return -EINVAL; ++ } ++ ++ helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER; ++ ret = qcow2_change_refcount_order(bs, refcount_order, ++ &qcow2_amend_helper_cb, ++ &helper_cb_info, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* data-file-raw blocks backing files, so clear it first if requested */ ++ if (data_file_raw) { ++ s->autoclear_features |= QCOW2_AUTOCLEAR_DATA_FILE_RAW; ++ } else { ++ s->autoclear_features &= ~QCOW2_AUTOCLEAR_DATA_FILE_RAW; ++ } ++ ++ if (data_file) { ++ g_free(s->image_data_file); ++ s->image_data_file = *data_file ? g_strdup(data_file) : NULL; ++ } ++ ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ ++ if (backing_file || backing_format) { ++ if (g_strcmp0(backing_file, s->image_backing_file) || ++ g_strcmp0(backing_format, s->image_backing_format)) { ++ error_setg(errp, "Cannot amend the backing file"); ++ error_append_hint(errp, ++ "You can use 'qemu-img rebase' instead.\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (s->use_lazy_refcounts != lazy_refcounts) { ++ if (lazy_refcounts) { ++ if (new_version < 3) { ++ error_setg(errp, "Lazy refcounts only supported with " ++ "compatibility level 1.1 and above (use compat=1.1 " ++ "or greater)"); ++ return -EINVAL; ++ } ++ s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ s->use_lazy_refcounts = true; ++ } else { ++ /* make image clean first */ ++ ret = qcow2_mark_clean(bs); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to make the image clean"); ++ return ret; ++ } ++ /* now disallow lazy refcounts */ ++ s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; ++ ret = qcow2_update_header(bs); ++ if (ret < 0) { ++ s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; ++ error_setg_errno(errp, -ret, "Failed to update the image header"); ++ return ret; ++ } ++ s->use_lazy_refcounts = false; ++ } ++ } ++ ++ if (new_size) { ++ BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, ++ errp); ++ if (!blk) { ++ return -EPERM; ++ } ++ ++ /* ++ * Amending image options should ensure that the image has ++ * exactly the given new values, so pass exact=true here. ++ */ ++ ret = blk_truncate(blk, new_size, true, PREALLOC_MODE_OFF, 0, errp); ++ blk_unref(blk); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ /* Downgrade last (so unsupported features can be removed before) */ ++ if (new_version < old_version) { ++ helper_cb_info.current_operation = QCOW2_DOWNGRADING; ++ ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb, ++ &helper_cb_info, errp); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, ++ BlockdevAmendOptions *opts, ++ bool force, ++ Error **errp) ++{ ++ BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2; ++ BDRVQcow2State *s = bs->opaque; ++ int ret = 0; ++ ++ if (qopts->encrypt) { ++ if (!s->crypto) { ++ error_setg(errp, "image is not encrypted, can't amend"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) { ++ error_setg(errp, ++ "Amend can't be used to change the qcow2 encryption format"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (s->crypt_method_header != QCOW_CRYPT_LUKS) { ++ error_setg(errp, ++ "Only LUKS encryption options can be amended for qcow2 with blockdev-amend"); ++ return -EOPNOTSUPP; ++ } ++ ++ ret = qcrypto_block_amend_options(s->crypto, ++ qcow2_crypto_hdr_read_func, ++ qcow2_crypto_hdr_write_func, ++ bs, ++ qopts->encrypt, ++ force, ++ errp); ++ } ++ return ret; ++} ++ ++/* ++ * If offset or size are negative, respectively, they will not be included in ++ * the BLOCK_IMAGE_CORRUPTED event emitted. ++ * fatal will be ignored for read-only BDS; corruptions found there will always ++ * be considered non-fatal. ++ */ ++void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, ++ int64_t size, const char *message_format, ...) ++{ ++ BDRVQcow2State *s = bs->opaque; ++ const char *node_name; ++ char *message; ++ va_list ap; ++ ++ fatal = fatal && bdrv_is_writable(bs); ++ ++ if (s->signaled_corruption && ++ (!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT))) ++ { ++ return; ++ } ++ ++ va_start(ap, message_format); ++ message = g_strdup_vprintf(message_format, ap); ++ va_end(ap); ++ ++ if (fatal) { ++ fprintf(stderr, "qcow2: Marking image as corrupt: %s; further " ++ "corruption events will be suppressed\n", message); ++ } else { ++ fprintf(stderr, "qcow2: Image is corrupt: %s; further non-fatal " ++ "corruption events will be suppressed\n", message); ++ } ++ ++ node_name = bdrv_get_node_name(bs); ++ qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs), ++ *node_name ? node_name : NULL, ++ message, offset >= 0, offset, ++ size >= 0, size, ++ fatal); ++ g_free(message); ++ ++ if (fatal) { ++ qcow2_mark_corrupt(bs); ++ bs->drv = NULL; /* make BDS unusable */ ++ } ++ ++ s->signaled_corruption = true; ++} ++ ++#define QCOW_COMMON_OPTIONS \ ++ { \ ++ .name = BLOCK_OPT_SIZE, \ ++ .type = QEMU_OPT_SIZE, \ ++ .help = "Virtual disk size" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_COMPAT_LEVEL, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Compatibility level (v2 [0.10] or v3 [1.1])" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_BACKING_FILE, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "File name of a base image" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_BACKING_FMT, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Image format of the base image" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_DATA_FILE, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "File name of an external data file" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_DATA_FILE_RAW, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "The external data file must stay valid " \ ++ "as a raw image" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_LAZY_REFCOUNTS, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "Postpone refcount updates", \ ++ .def_value_str = "off" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_REFCOUNT_BITS, \ ++ .type = QEMU_OPT_NUMBER, \ ++ .help = "Width of a reference count entry in bits", \ ++ .def_value_str = "16" \ ++ } ++ ++static QemuOptsList qcow2_create_opts = { ++ .name = "qcow2-create-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head), ++ .desc = { ++ { \ ++ .name = BLOCK_OPT_ENCRYPT, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "Encrypt the image with format 'aes'. (Deprecated " \ ++ "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_ENCRYPT_FORMAT, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Encrypt the image, format choices: 'aes', 'luks'", \ ++ }, \ ++ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \ ++ "ID of secret providing qcow AES key or LUKS passphrase"), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \ ++ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \ ++ { \ ++ .name = BLOCK_OPT_CLUSTER_SIZE, \ ++ .type = QEMU_OPT_SIZE, \ ++ .help = "qcow2 cluster size", \ ++ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_EXTL2, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "Extended L2 tables", \ ++ .def_value_str = "off" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_PREALLOC, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Preallocation mode (allowed values: off, " \ ++ "metadata, falloc, full)" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_COMPRESSION_TYPE, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Compression method used for image cluster " \ ++ "compression", \ ++ .def_value_str = "zlib" \ ++ }, ++ QCOW_COMMON_OPTIONS, ++ { /* end of list */ } ++ } ++}; ++ ++static QemuOptsList qcow2_amend_opts = { ++ .name = "qcow2-amend-opts", ++ .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head), ++ .desc = { ++ BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."), ++ BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."), ++ BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."), ++ BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("encrypt."), ++ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), ++ QCOW_COMMON_OPTIONS, ++ { /* end of list */ } ++ } ++}; ++ ++static const char *const qcow2_strong_runtime_opts[] = { ++ "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, ++ ++ NULL ++}; ++ ++BlockDriver bdrv_qcow2 = { ++ .format_name = "qcow2", ++ .instance_size = sizeof(BDRVQcow2State), ++ .bdrv_probe = qcow2_probe, ++ .bdrv_open = qcow2_open, ++ .bdrv_close = qcow2_close, ++ .bdrv_reopen_prepare = qcow2_reopen_prepare, ++ .bdrv_reopen_commit = qcow2_reopen_commit, ++ .bdrv_reopen_commit_post = qcow2_reopen_commit_post, ++ .bdrv_reopen_abort = qcow2_reopen_abort, ++ .bdrv_join_options = qcow2_join_options, ++ .bdrv_child_perm = bdrv_default_perms, ++ .bdrv_co_create_opts = qcow2_co_create_opts, ++ .bdrv_co_create = qcow2_co_create, ++ .bdrv_has_zero_init = qcow2_has_zero_init, ++ .bdrv_co_block_status = qcow2_co_block_status, ++ ++ .bdrv_co_preadv_part = qcow2_co_preadv_part, ++ .bdrv_co_pwritev_part = qcow2_co_pwritev_part, ++ .bdrv_co_flush_to_os = qcow2_co_flush_to_os, ++ ++ .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, ++ .bdrv_co_pdiscard = qcow2_co_pdiscard, ++ .bdrv_co_copy_range_from = qcow2_co_copy_range_from, ++ .bdrv_co_copy_range_to = qcow2_co_copy_range_to, ++ .bdrv_co_truncate = qcow2_co_truncate, ++ .bdrv_co_pwritev_compressed_part = qcow2_co_pwritev_compressed_part, ++ .bdrv_make_empty = qcow2_make_empty, ++ ++ .bdrv_snapshot_create = qcow2_snapshot_create, ++ .bdrv_snapshot_goto = qcow2_snapshot_goto, ++ .bdrv_snapshot_delete = qcow2_snapshot_delete, ++ .bdrv_snapshot_list = qcow2_snapshot_list, ++ .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, ++ .bdrv_measure = qcow2_measure, ++ .bdrv_co_get_info = qcow2_co_get_info, ++ .bdrv_get_specific_info = qcow2_get_specific_info, ++ ++ .bdrv_co_save_vmstate = qcow2_co_save_vmstate, ++ .bdrv_co_load_vmstate = qcow2_co_load_vmstate, ++ ++ .is_format = true, ++ .supports_backing = true, ++ .bdrv_co_change_backing_file = qcow2_co_change_backing_file, ++ ++ .bdrv_refresh_limits = qcow2_refresh_limits, ++ .bdrv_co_invalidate_cache = qcow2_co_invalidate_cache, ++ .bdrv_inactivate = qcow2_inactivate, ++ ++ .create_opts = &qcow2_create_opts, ++ .amend_opts = &qcow2_amend_opts, ++ .strong_runtime_opts = qcow2_strong_runtime_opts, ++ .mutable_opts = mutable_opts, ++ .bdrv_co_check = qcow2_co_check, ++ .bdrv_amend_options = qcow2_amend_options, ++ .bdrv_co_amend = qcow2_co_amend, ++ ++ .bdrv_detach_aio_context = qcow2_detach_aio_context, ++ .bdrv_attach_aio_context = qcow2_attach_aio_context, ++ ++ .bdrv_supports_persistent_dirty_bitmap = ++ qcow2_supports_persistent_dirty_bitmap, ++ .bdrv_co_can_store_new_dirty_bitmap = qcow2_co_can_store_new_dirty_bitmap, ++ .bdrv_co_remove_persistent_dirty_bitmap = ++ qcow2_co_remove_persistent_dirty_bitmap, ++}; ++ ++static void bdrv_qcow2_init(void) ++{ ++ bdrv_register(&bdrv_qcow2); ++} ++ ++block_init(bdrv_qcow2_init); +diff --git a/qcow2/lib/qobject/block-qdict.c b/qcow2/lib/qobject/block-qdict.c +new file mode 100644 +index 00000000..4a83bda2 +--- /dev/null ++++ b/qcow2/lib/qobject/block-qdict.c +@@ -0,0 +1,738 @@ ++/* ++ * Special QDict functions used by the block layer ++ * ++ * Copyright (c) 2013-2018 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/qdict.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qemu/cutils.h" ++#include "qapi/error.h" ++ ++/** ++ * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the ++ * value of 'key' in 'src' is copied there (and the refcount increased ++ * accordingly). ++ */ ++void qdict_copy_default(QDict *dst, QDict *src, const char *key) ++{ ++ QObject *val; ++ ++ if (qdict_haskey(dst, key)) { ++ return; ++ } ++ ++ val = qdict_get(src, key); ++ if (val) { ++ qdict_put_obj(dst, key, qobject_ref(val)); ++ } ++} ++ ++/** ++ * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a ++ * new QString initialised by 'val' is put there. ++ */ ++void qdict_set_default_str(QDict *dst, const char *key, const char *val) ++{ ++ if (qdict_haskey(dst, key)) { ++ return; ++ } ++ ++ qdict_put_str(dst, key, val); ++} ++ ++static void qdict_flatten_qdict(QDict *qdict, QDict *target, ++ const char *prefix); ++ ++static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) ++{ ++ QObject *value; ++ const QListEntry *entry; ++ QDict *dict_val; ++ QList *list_val; ++ char *new_key; ++ int i; ++ ++ /* This function is never called with prefix == NULL, i.e., it is always ++ * called from within qdict_flatten_q(list|dict)(). Therefore, it does not ++ * need to remove list entries during the iteration (the whole list will be ++ * deleted eventually anyway from qdict_flatten_qdict()). */ ++ assert(prefix); ++ ++ entry = qlist_first(qlist); ++ ++ for (i = 0; entry; entry = qlist_next(entry), i++) { ++ value = qlist_entry_obj(entry); ++ dict_val = qobject_to(QDict, value); ++ list_val = qobject_to(QList, value); ++ new_key = g_strdup_printf("%s.%i", prefix, i); ++ ++ /* ++ * Flatten non-empty QDict and QList recursively into @target, ++ * copy other objects to @target ++ */ ++ if (dict_val && qdict_size(dict_val)) { ++ qdict_flatten_qdict(dict_val, target, new_key); ++ } else if (list_val && !qlist_empty(list_val)) { ++ qdict_flatten_qlist(list_val, target, new_key); ++ } else { ++ qdict_put_obj(target, new_key, qobject_ref(value)); ++ } ++ ++ g_free(new_key); ++ } ++} ++ ++static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) ++{ ++ QObject *value; ++ const QDictEntry *entry, *next; ++ QDict *dict_val; ++ QList *list_val; ++ char *key, *new_key; ++ ++ entry = qdict_first(qdict); ++ ++ while (entry != NULL) { ++ next = qdict_next(qdict, entry); ++ value = qdict_entry_value(entry); ++ dict_val = qobject_to(QDict, value); ++ list_val = qobject_to(QList, value); ++ ++ if (prefix) { ++ key = new_key = g_strdup_printf("%s.%s", prefix, entry->key); ++ } else { ++ key = entry->key; ++ new_key = NULL; ++ } ++ ++ /* ++ * Flatten non-empty QDict and QList recursively into @target, ++ * copy other objects to @target. ++ * On the root level (if @qdict == @target), remove flattened ++ * nested QDicts and QLists from @qdict. ++ * ++ * (Note that we do not need to remove entries from nested ++ * dicts or lists. Their reference count is decremented on ++ * the root level, so there are no leaks. In fact, if they ++ * have a reference count greater than one, we are probably ++ * well advised not to modify them altogether.) ++ */ ++ if (dict_val && qdict_size(dict_val)) { ++ qdict_flatten_qdict(dict_val, target, key); ++ if (target == qdict) { ++ qdict_del(qdict, entry->key); ++ } ++ } else if (list_val && !qlist_empty(list_val)) { ++ qdict_flatten_qlist(list_val, target, key); ++ if (target == qdict) { ++ qdict_del(qdict, entry->key); ++ } ++ } else if (target != qdict) { ++ qdict_put_obj(target, key, qobject_ref(value)); ++ } ++ ++ g_free(new_key); ++ entry = next; ++ } ++} ++ ++/** ++ * qdict_flatten(): For each nested non-empty QDict with key x, all ++ * fields with key y are moved to this QDict and their key is renamed ++ * to "x.y". For each nested non-empty QList with key x, the field at ++ * index y is moved to this QDict with the key "x.y" (i.e., the ++ * reverse of what qdict_array_split() does). ++ * This operation is applied recursively for nested QDicts and QLists. ++ */ ++void qdict_flatten(QDict *qdict) ++{ ++ qdict_flatten_qdict(qdict, qdict, NULL); ++} ++ ++/* extract all the src QDict entries starting by start into dst. ++ * If dst is NULL then the entries are simply removed from src. */ ++void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) ++ ++{ ++ const QDictEntry *entry, *next; ++ const char *p; ++ ++ if (dst) { ++ *dst = qdict_new(); ++ } ++ entry = qdict_first(src); ++ ++ while (entry != NULL) { ++ next = qdict_next(src, entry); ++ if (strstart(entry->key, start, &p)) { ++ if (dst) { ++ qdict_put_obj(*dst, p, qobject_ref(entry->value)); ++ } ++ qdict_del(src, entry->key); ++ } ++ entry = next; ++ } ++} ++ ++static int qdict_count_prefixed_entries(const QDict *src, const char *start) ++{ ++ const QDictEntry *entry; ++ int count = 0; ++ ++ for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { ++ if (strstart(entry->key, start, NULL)) { ++ if (count == INT_MAX) { ++ return -ERANGE; ++ } ++ count++; ++ } ++ } ++ ++ return count; ++} ++ ++/** ++ * qdict_array_split(): This function moves array-like elements of a QDict into ++ * a new QList. Every entry in the original QDict with a key "%u" or one ++ * prefixed "%u.", where %u designates an unsigned integer starting at 0 and ++ * incrementally counting up, will be moved to a new QDict at index %u in the ++ * output QList with the key prefix removed, if that prefix is "%u.". If the ++ * whole key is just "%u", the whole QObject will be moved unchanged without ++ * creating a new QDict. The function terminates when there is no entry in the ++ * QDict with a prefix directly (incrementally) following the last one; it also ++ * returns if there are both entries with "%u" and "%u." for the same index %u. ++ * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} ++ * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) ++ * => [{"a": 42, "b": 23}, {"x": 0}, 66] ++ * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) ++ */ ++void qdict_array_split(QDict *src, QList **dst) ++{ ++ unsigned i; ++ ++ *dst = qlist_new(); ++ ++ for (i = 0; i < UINT_MAX; i++) { ++ QObject *subqobj; ++ bool is_subqdict; ++ QDict *subqdict; ++ char indexstr[32], prefix[32]; ++ size_t snprintf_ret; ++ ++ snprintf_ret = snprintf(indexstr, 32, "%u", i); ++ assert(snprintf_ret < 32); ++ ++ subqobj = qdict_get(src, indexstr); ++ ++ snprintf_ret = snprintf(prefix, 32, "%u.", i); ++ assert(snprintf_ret < 32); ++ ++ /* Overflow is the same as positive non-zero results */ ++ is_subqdict = qdict_count_prefixed_entries(src, prefix); ++ ++ /* ++ * There may be either a single subordinate object (named ++ * "%u") or multiple objects (each with a key prefixed "%u."), ++ * but not both. ++ */ ++ if (!subqobj == !is_subqdict) { ++ break; ++ } ++ ++ if (is_subqdict) { ++ qdict_extract_subqdict(src, &subqdict, prefix); ++ assert(qdict_size(subqdict) > 0); ++ qlist_append_obj(*dst, QOBJECT(subqdict)); ++ } else { ++ qobject_ref(subqobj); ++ qdict_del(src, indexstr); ++ qlist_append_obj(*dst, subqobj); ++ } ++ } ++} ++ ++/** ++ * qdict_split_flat_key: ++ * @key: the key string to split ++ * @prefix: non-NULL pointer to hold extracted prefix ++ * @suffix: non-NULL pointer to remaining suffix ++ * ++ * Given a flattened key such as 'foo.0.bar', split it into two parts ++ * at the first '.' separator. Allows double dot ('..') to escape the ++ * normal separator. ++ * ++ * e.g. ++ * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' ++ * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' ++ * ++ * The '..' sequence will be unescaped in the returned 'prefix' ++ * string. The 'suffix' string will be left in escaped format, so it ++ * can be fed back into the qdict_split_flat_key() key as the input ++ * later. ++ * ++ * The caller is responsible for freeing the string returned in @prefix ++ * using g_free(). ++ */ ++static void qdict_split_flat_key(const char *key, char **prefix, ++ const char **suffix) ++{ ++ const char *separator; ++ size_t i, j; ++ ++ /* Find first '.' separator, but if there is a pair '..' ++ * that acts as an escape, so skip over '..' */ ++ separator = NULL; ++ do { ++ if (separator) { ++ separator += 2; ++ } else { ++ separator = key; ++ } ++ separator = strchr(separator, '.'); ++ } while (separator && separator[1] == '.'); ++ ++ if (separator) { ++ *prefix = g_strndup(key, separator - key); ++ *suffix = separator + 1; ++ } else { ++ *prefix = g_strdup(key); ++ *suffix = NULL; ++ } ++ ++ /* Unescape the '..' sequence into '.' */ ++ for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { ++ if ((*prefix)[i] == '.') { ++ assert((*prefix)[i + 1] == '.'); ++ i++; ++ } ++ (*prefix)[j] = (*prefix)[i]; ++ } ++ (*prefix)[j] = '\0'; ++} ++ ++/** ++ * qdict_is_list: ++ * @maybe_list: dict to check if keys represent list elements. ++ * ++ * Determine whether all keys in @maybe_list are valid list elements. ++ * If @maybe_list is non-zero in length and all the keys look like ++ * valid list indexes, this will return 1. If @maybe_list is zero ++ * length or all keys are non-numeric then it will return 0 to indicate ++ * it is a normal qdict. If there is a mix of numeric and non-numeric ++ * keys, or the list indexes are non-contiguous, an error is reported. ++ * ++ * Returns: 1 if a valid list, 0 if a dict, -1 on error ++ */ ++static int qdict_is_list(QDict *maybe_list, Error **errp) ++{ ++ const QDictEntry *ent; ++ ssize_t len = 0; ++ ssize_t max = -1; ++ int is_list = -1; ++ int64_t val; ++ ++ for (ent = qdict_first(maybe_list); ent != NULL; ++ ent = qdict_next(maybe_list, ent)) { ++ int is_index = !qemu_strtoi64(ent->key, NULL, 10, &val); ++ ++ if (is_list == -1) { ++ is_list = is_index; ++ } ++ ++ if (is_index != is_list) { ++ error_setg(errp, "Cannot mix list and non-list keys"); ++ return -1; ++ } ++ ++ if (is_index) { ++ len++; ++ if (val > max) { ++ max = val; ++ } ++ } ++ } ++ ++ if (is_list == -1) { ++ assert(!qdict_size(maybe_list)); ++ is_list = 0; ++ } ++ ++ /* NB this isn't a perfect check - e.g. it won't catch ++ * a list containing '1', '+1', '01', '3', but that ++ * does not matter - we've still proved that the ++ * input is a list. It is up the caller to do a ++ * stricter check if desired */ ++ if (len != (max + 1)) { ++ error_setg(errp, "List indices are not contiguous, " ++ "saw %zd elements but %zd largest index", ++ len, max); ++ return -1; ++ } ++ ++ return is_list; ++} ++ ++/** ++ * qdict_crumple: ++ * @src: the original flat dictionary (only scalar values) to crumple ++ * ++ * Takes a flat dictionary whose keys use '.' separator to indicate ++ * nesting, and values are scalars, empty dictionaries or empty lists, ++ * and crumples it into a nested structure. ++ * ++ * To include a literal '.' in a key name, it must be escaped as '..' ++ * ++ * For example, an input of: ++ * ++ * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', ++ * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } ++ * ++ * will result in an output of: ++ * ++ * { ++ * 'foo': [ ++ * { 'bar': 'one', 'wizz': '1' }, ++ * { 'bar': 'two', 'wizz': '2' } ++ * ], ++ * } ++ * ++ * The following scenarios in the input dict will result in an ++ * error being returned: ++ * ++ * - Any values in @src are non-scalar types ++ * - If keys in @src imply that a particular level is both a ++ * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". ++ * - If keys in @src imply that a particular level is a list, ++ * but the indices are non-contiguous. e.g. "foo.0.bar" and ++ * "foo.2.bar" without any "foo.1.bar" present. ++ * - If keys in @src represent list indexes, but are not in ++ * the "%zu" format. e.g. "foo.+0.bar" ++ * ++ * Returns: either a QDict or QList for the nested data structure, or NULL ++ * on error ++ */ ++QObject *qdict_crumple(const QDict *src, Error **errp) ++{ ++ const QDictEntry *ent; ++ QDict *two_level, *multi_level = NULL, *child_dict; ++ QDict *dict_val; ++ QList *list_val; ++ QObject *dst = NULL, *child; ++ size_t i; ++ char *prefix = NULL; ++ const char *suffix = NULL; ++ int is_list; ++ ++ two_level = qdict_new(); ++ ++ /* Step 1: split our totally flat dict into a two level dict */ ++ for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { ++ dict_val = qobject_to(QDict, ent->value); ++ list_val = qobject_to(QList, ent->value); ++ if ((dict_val && qdict_size(dict_val)) ++ || (list_val && !qlist_empty(list_val))) { ++ error_setg(errp, "Value %s is not flat", ent->key); ++ goto error; ++ } ++ ++ qdict_split_flat_key(ent->key, &prefix, &suffix); ++ child = qdict_get(two_level, prefix); ++ child_dict = qobject_to(QDict, child); ++ ++ if (child) { ++ /* ++ * If @child_dict, then all previous keys with this prefix ++ * had a suffix. If @suffix, this one has one as well, ++ * and we're good, else there's a clash. ++ */ ++ if (!child_dict || !suffix) { ++ error_setg(errp, "Cannot mix scalar and non-scalar keys"); ++ goto error; ++ } ++ } ++ ++ if (suffix) { ++ if (!child_dict) { ++ child_dict = qdict_new(); ++ qdict_put(two_level, prefix, child_dict); ++ } ++ qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); ++ } else { ++ qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); ++ } ++ ++ g_free(prefix); ++ prefix = NULL; ++ } ++ ++ /* Step 2: optionally process the two level dict recursively ++ * into a multi-level dict */ ++ multi_level = qdict_new(); ++ for (ent = qdict_first(two_level); ent != NULL; ++ ent = qdict_next(two_level, ent)) { ++ dict_val = qobject_to(QDict, ent->value); ++ if (dict_val && qdict_size(dict_val)) { ++ child = qdict_crumple(dict_val, errp); ++ if (!child) { ++ goto error; ++ } ++ ++ qdict_put_obj(multi_level, ent->key, child); ++ } else { ++ qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); ++ } ++ } ++ qobject_unref(two_level); ++ two_level = NULL; ++ ++ /* Step 3: detect if we need to turn our dict into list */ ++ is_list = qdict_is_list(multi_level, errp); ++ if (is_list < 0) { ++ goto error; ++ } ++ ++ if (is_list) { ++ dst = QOBJECT(qlist_new()); ++ ++ for (i = 0; i < qdict_size(multi_level); i++) { ++ char *key = g_strdup_printf("%zu", i); ++ ++ child = qdict_get(multi_level, key); ++ g_free(key); ++ ++ if (!child) { ++ error_setg(errp, "Missing list index %zu", i); ++ goto error; ++ } ++ ++ qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); ++ } ++ qobject_unref(multi_level); ++ multi_level = NULL; ++ } else { ++ dst = QOBJECT(multi_level); ++ } ++ ++ return dst; ++ ++ error: ++ g_free(prefix); ++ qobject_unref(multi_level); ++ qobject_unref(two_level); ++ qobject_unref(dst); ++ return NULL; ++} ++ ++/** ++ * qdict_crumple_for_keyval_qiv: ++ * @src: the flat dictionary (only scalar values) to crumple ++ * @errp: location to store error ++ * ++ * Like qdict_crumple(), but additionally transforms scalar values so ++ * the result can be passed to qobject_input_visitor_new_keyval(). ++ * ++ * The block subsystem uses this function to prepare its flat QDict ++ * with possibly confused scalar types for a visit. It should not be ++ * used for anything else, and it should go away once the block ++ * subsystem has been cleaned up. ++ */ ++static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) ++{ ++ QDict *tmp = NULL; ++ char *buf; ++ const char *s; ++ const QDictEntry *ent; ++ QObject *dst; ++ ++ for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) { ++ buf = NULL; ++ switch (qobject_type(ent->value)) { ++ case QTYPE_QNULL: ++ case QTYPE_QSTRING: ++ continue; ++ case QTYPE_QNUM: ++ s = buf = qnum_to_string(qobject_to(QNum, ent->value)); ++ break; ++ case QTYPE_QDICT: ++ case QTYPE_QLIST: ++ /* @src isn't flat; qdict_crumple() will fail */ ++ continue; ++ case QTYPE_QBOOL: ++ s = qbool_get_bool(qobject_to(QBool, ent->value)) ++ ? "on" : "off"; ++ break; ++ default: ++ abort(); ++ } ++ ++ if (!tmp) { ++ tmp = qdict_clone_shallow(src); ++ } ++ qdict_put_str(tmp, ent->key, s); ++ g_free(buf); ++ } ++ ++ dst = qdict_crumple(tmp ?: src, errp); ++ qobject_unref(tmp); ++ return dst; ++} ++ ++/** ++ * qdict_array_entries(): Returns the number of direct array entries if the ++ * sub-QDict of src specified by the prefix in subqdict (or src itself for ++ * prefix == "") is valid as an array, i.e. the length of the created list if ++ * the sub-QDict would become empty after calling qdict_array_split() on it. If ++ * the array is not valid, -EINVAL is returned. ++ */ ++int qdict_array_entries(QDict *src, const char *subqdict) ++{ ++ const QDictEntry *entry; ++ unsigned i; ++ unsigned entries = 0; ++ size_t subqdict_len = strlen(subqdict); ++ ++ assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); ++ ++ /* qdict_array_split() loops until UINT_MAX, but as we want to return ++ * negative errors, we only have a signed return value here. Any additional ++ * entries will lead to -EINVAL. */ ++ for (i = 0; i < INT_MAX; i++) { ++ QObject *subqobj; ++ int subqdict_entries; ++ char *prefix = g_strdup_printf("%s%u.", subqdict, i); ++ ++ subqdict_entries = qdict_count_prefixed_entries(src, prefix); ++ ++ /* Remove ending "." */ ++ prefix[strlen(prefix) - 1] = 0; ++ subqobj = qdict_get(src, prefix); ++ ++ g_free(prefix); ++ ++ if (subqdict_entries < 0) { ++ return subqdict_entries; ++ } ++ ++ /* There may be either a single subordinate object (named "%u") or ++ * multiple objects (each with a key prefixed "%u."), but not both. */ ++ if (subqobj && subqdict_entries) { ++ return -EINVAL; ++ } else if (!subqobj && !subqdict_entries) { ++ break; ++ } ++ ++ entries += subqdict_entries ? subqdict_entries : 1; ++ } ++ ++ /* Consider everything handled that isn't part of the given sub-QDict */ ++ for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { ++ if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { ++ entries++; ++ } ++ } ++ ++ /* Anything left in the sub-QDict that wasn't handled? */ ++ if (qdict_size(src) != entries) { ++ return -EINVAL; ++ } ++ ++ return i; ++} ++ ++/** ++ * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all ++ * elements from src to dest. ++ * ++ * If an element from src has a key already present in dest, it will not be ++ * moved unless overwrite is true. ++ * ++ * If overwrite is true, the conflicting values in dest will be discarded and ++ * replaced by the corresponding values from src. ++ * ++ * Therefore, with overwrite being true, the src QDict will always be empty when ++ * this function returns. If overwrite is false, the src QDict will be empty ++ * iff there were no conflicts. ++ */ ++void qdict_join(QDict *dest, QDict *src, bool overwrite) ++{ ++ const QDictEntry *entry, *next; ++ ++ entry = qdict_first(src); ++ while (entry) { ++ next = qdict_next(src, entry); ++ ++ if (overwrite || !qdict_haskey(dest, entry->key)) { ++ qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); ++ qdict_del(src, entry->key); ++ } ++ ++ entry = next; ++ } ++} ++ ++/** ++ * qdict_rename_keys(): Rename keys in qdict according to the replacements ++ * specified in the array renames. The array must be terminated by an entry ++ * with from = NULL. ++ * ++ * The renames are performed individually in the order of the array, so entries ++ * may be renamed multiple times and may or may not conflict depending on the ++ * order of the renames array. ++ * ++ * Returns true for success, false in error cases. ++ */ ++bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) ++{ ++ QObject *qobj; ++ ++ while (renames->from) { ++ if (qdict_haskey(qdict, renames->from)) { ++ if (qdict_haskey(qdict, renames->to)) { ++ error_setg(errp, "'%s' and its alias '%s' can't be used at the " ++ "same time", renames->to, renames->from); ++ return false; ++ } ++ ++ qobj = qdict_get(qdict, renames->from); ++ qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); ++ qdict_del(qdict, renames->from); ++ } ++ ++ renames++; ++ } ++ return true; ++} ++ ++/* ++ * Create a QObject input visitor for flat @qdict with possibly ++ * confused scalar types. ++ * ++ * The block subsystem uses this function to visit its flat QDict with ++ * possibly confused scalar types. It should not be used for anything ++ * else, and it should go away once the block subsystem has been ++ * cleaned up. ++ */ ++Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, ++ Error **errp) ++{ ++ QObject *crumpled; ++ Visitor *v; ++ ++ crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); ++ if (!crumpled) { ++ return NULL; ++ } ++ ++ v = qobject_input_visitor_new_keyval(crumpled); ++ qobject_unref(crumpled); ++ return v; ++} +diff --git a/qcow2/lib/qobject/json-lexer.c b/qcow2/lib/qobject/json-lexer.c +new file mode 100644 +index 00000000..51341d96 +--- /dev/null ++++ b/qcow2/lib/qobject/json-lexer.c +@@ -0,0 +1,365 @@ ++/* ++ * JSON lexer ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "json-parser-int.h" ++ ++#define MAX_TOKEN_SIZE (64ULL << 20) ++ ++/* ++ * From RFC 8259 "The JavaScript Object Notation (JSON) Data ++ * Interchange Format", with [comments in brackets]: ++ * ++ * The set of tokens includes six structural characters, strings, ++ * numbers, and three literal names. ++ * ++ * These are the six structural characters: ++ * ++ * begin-array = ws %x5B ws ; [ left square bracket ++ * begin-object = ws %x7B ws ; { left curly bracket ++ * end-array = ws %x5D ws ; ] right square bracket ++ * end-object = ws %x7D ws ; } right curly bracket ++ * name-separator = ws %x3A ws ; : colon ++ * value-separator = ws %x2C ws ; , comma ++ * ++ * Insignificant whitespace is allowed before or after any of the six ++ * structural characters. ++ * [This lexer accepts it before or after any token, which is actually ++ * the same, as the grammar always has structural characters between ++ * other tokens.] ++ * ++ * ws = *( ++ * %x20 / ; Space ++ * %x09 / ; Horizontal tab ++ * %x0A / ; Line feed or New line ++ * %x0D ) ; Carriage return ++ * ++ * [...] three literal names: ++ * false null true ++ * [This lexer accepts [a-z]+, and leaves rejecting unknown literal ++ * names to the parser.] ++ * ++ * [Numbers:] ++ * ++ * number = [ minus ] int [ frac ] [ exp ] ++ * decimal-point = %x2E ; . ++ * digit1-9 = %x31-39 ; 1-9 ++ * e = %x65 / %x45 ; e E ++ * exp = e [ minus / plus ] 1*DIGIT ++ * frac = decimal-point 1*DIGIT ++ * int = zero / ( digit1-9 *DIGIT ) ++ * minus = %x2D ; - ++ * plus = %x2B ; + ++ * zero = %x30 ; 0 ++ * ++ * [Strings:] ++ * string = quotation-mark *char quotation-mark ++ * ++ * char = unescaped / ++ * escape ( ++ * %x22 / ; " quotation mark U+0022 ++ * %x5C / ; \ reverse solidus U+005C ++ * %x2F / ; / solidus U+002F ++ * %x62 / ; b backspace U+0008 ++ * %x66 / ; f form feed U+000C ++ * %x6E / ; n line feed U+000A ++ * %x72 / ; r carriage return U+000D ++ * %x74 / ; t tab U+0009 ++ * %x75 4HEXDIG ) ; uXXXX U+XXXX ++ * escape = %x5C ; \ ++ * quotation-mark = %x22 ; " ++ * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF ++ * [This lexer accepts any non-control character after escape, and ++ * leaves rejecting invalid ones to the parser.] ++ * ++ * ++ * Extensions over RFC 8259: ++ * - Extra escape sequence in strings: ++ * 0x27 (apostrophe) is recognized after escape, too ++ * - Single-quoted strings: ++ * Like double-quoted strings, except they're delimited by %x27 ++ * (apostrophe) instead of %x22 (quotation mark), and can't contain ++ * unescaped apostrophe, but can contain unescaped quotation mark. ++ * - Interpolation, if enabled: ++ * The lexer accepts %[A-Za-z0-9]*, and leaves rejecting invalid ++ * ones to the parser. ++ * ++ * Note: ++ * - Input must be encoded in modified UTF-8. ++ * - Decoding and validating is left to the parser. ++ */ ++ ++enum json_lexer_state { ++ IN_RECOVERY = 1, ++ IN_DQ_STRING_ESCAPE, ++ IN_DQ_STRING, ++ IN_SQ_STRING_ESCAPE, ++ IN_SQ_STRING, ++ IN_ZERO, ++ IN_EXP_DIGITS, ++ IN_EXP_SIGN, ++ IN_EXP_E, ++ IN_MANTISSA, ++ IN_MANTISSA_DIGITS, ++ IN_DIGITS, ++ IN_SIGN, ++ IN_KEYWORD, ++ IN_INTERP, ++ IN_START, ++ IN_START_INTERP, /* must be IN_START + 1 */ ++}; ++ ++QEMU_BUILD_BUG_ON(JSON_ERROR != 0); ++QEMU_BUILD_BUG_ON(IN_RECOVERY != JSON_ERROR + 1); ++QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START_INTERP); ++QEMU_BUILD_BUG_ON(JSON_MAX >= 0x80); ++QEMU_BUILD_BUG_ON(IN_START_INTERP != IN_START + 1); ++ ++#define LOOKAHEAD 0x80 ++#define TERMINAL(state) [0 ... 0xFF] = ((state) | LOOKAHEAD) ++ ++static const uint8_t json_lexer[][256] = { ++ /* Relies on default initialization to IN_ERROR! */ ++ ++ /* error recovery */ ++ [IN_RECOVERY] = { ++ /* ++ * Skip characters until a structural character, an ASCII ++ * control character other than '\t', or impossible UTF-8 ++ * bytes '\xFE', '\xFF'. Structural characters and line ++ * endings are promising resynchronization points. Clients ++ * may use the others to force the JSON parser into known-good ++ * state; see docs/interop/qmp-spec.rst. ++ */ ++ [0 ... 0x1F] = IN_START | LOOKAHEAD, ++ [0x20 ... 0xFD] = IN_RECOVERY, ++ [0xFE ... 0xFF] = IN_START | LOOKAHEAD, ++ ['\t'] = IN_RECOVERY, ++ ['['] = IN_START | LOOKAHEAD, ++ [']'] = IN_START | LOOKAHEAD, ++ ['{'] = IN_START | LOOKAHEAD, ++ ['}'] = IN_START | LOOKAHEAD, ++ [':'] = IN_START | LOOKAHEAD, ++ [','] = IN_START | LOOKAHEAD, ++ }, ++ ++ /* double quote string */ ++ [IN_DQ_STRING_ESCAPE] = { ++ [0x20 ... 0xFD] = IN_DQ_STRING, ++ }, ++ [IN_DQ_STRING] = { ++ [0x20 ... 0xFD] = IN_DQ_STRING, ++ ['\\'] = IN_DQ_STRING_ESCAPE, ++ ['"'] = JSON_STRING, ++ }, ++ ++ /* single quote string */ ++ [IN_SQ_STRING_ESCAPE] = { ++ [0x20 ... 0xFD] = IN_SQ_STRING, ++ }, ++ [IN_SQ_STRING] = { ++ [0x20 ... 0xFD] = IN_SQ_STRING, ++ ['\\'] = IN_SQ_STRING_ESCAPE, ++ ['\''] = JSON_STRING, ++ }, ++ ++ /* Zero */ ++ [IN_ZERO] = { ++ TERMINAL(JSON_INTEGER), ++ ['0' ... '9'] = JSON_ERROR, ++ ['.'] = IN_MANTISSA, ++ }, ++ ++ /* Float */ ++ [IN_EXP_DIGITS] = { ++ TERMINAL(JSON_FLOAT), ++ ['0' ... '9'] = IN_EXP_DIGITS, ++ }, ++ ++ [IN_EXP_SIGN] = { ++ ['0' ... '9'] = IN_EXP_DIGITS, ++ }, ++ ++ [IN_EXP_E] = { ++ ['-'] = IN_EXP_SIGN, ++ ['+'] = IN_EXP_SIGN, ++ ['0' ... '9'] = IN_EXP_DIGITS, ++ }, ++ ++ [IN_MANTISSA_DIGITS] = { ++ TERMINAL(JSON_FLOAT), ++ ['0' ... '9'] = IN_MANTISSA_DIGITS, ++ ['e'] = IN_EXP_E, ++ ['E'] = IN_EXP_E, ++ }, ++ ++ [IN_MANTISSA] = { ++ ['0' ... '9'] = IN_MANTISSA_DIGITS, ++ }, ++ ++ /* Number */ ++ [IN_DIGITS] = { ++ TERMINAL(JSON_INTEGER), ++ ['0' ... '9'] = IN_DIGITS, ++ ['e'] = IN_EXP_E, ++ ['E'] = IN_EXP_E, ++ ['.'] = IN_MANTISSA, ++ }, ++ ++ [IN_SIGN] = { ++ ['0'] = IN_ZERO, ++ ['1' ... '9'] = IN_DIGITS, ++ }, ++ ++ /* keywords */ ++ [IN_KEYWORD] = { ++ TERMINAL(JSON_KEYWORD), ++ ['a' ... 'z'] = IN_KEYWORD, ++ }, ++ ++ /* interpolation */ ++ [IN_INTERP] = { ++ TERMINAL(JSON_INTERP), ++ ['A' ... 'Z'] = IN_INTERP, ++ ['a' ... 'z'] = IN_INTERP, ++ ['0' ... '9'] = IN_INTERP, ++ }, ++ ++ /* ++ * Two start states: ++ * - IN_START recognizes JSON tokens with our string extensions ++ * - IN_START_INTERP additionally recognizes interpolation. ++ */ ++ [IN_START ... IN_START_INTERP] = { ++ ['"'] = IN_DQ_STRING, ++ ['\''] = IN_SQ_STRING, ++ ['0'] = IN_ZERO, ++ ['1' ... '9'] = IN_DIGITS, ++ ['-'] = IN_SIGN, ++ ['{'] = JSON_LCURLY, ++ ['}'] = JSON_RCURLY, ++ ['['] = JSON_LSQUARE, ++ [']'] = JSON_RSQUARE, ++ [','] = JSON_COMMA, ++ [':'] = JSON_COLON, ++ ['a' ... 'z'] = IN_KEYWORD, ++ [' '] = IN_START, ++ ['\t'] = IN_START, ++ ['\r'] = IN_START, ++ ['\n'] = IN_START, ++ }, ++ [IN_START_INTERP]['%'] = IN_INTERP, ++}; ++ ++static inline uint8_t next_state(JSONLexer *lexer, char ch, bool flush, ++ bool *char_consumed) ++{ ++ uint8_t next; ++ ++ assert(lexer->state < ARRAY_SIZE(json_lexer)); ++ next = json_lexer[lexer->state][(uint8_t)ch]; ++ *char_consumed = !flush && !(next & LOOKAHEAD); ++ return next & ~LOOKAHEAD; ++} ++ ++void json_lexer_init(JSONLexer *lexer, bool enable_interpolation) ++{ ++ lexer->start_state = lexer->state = enable_interpolation ++ ? IN_START_INTERP : IN_START; ++ lexer->token = g_string_sized_new(3); ++ lexer->x = lexer->y = 0; ++} ++ ++static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) ++{ ++ int new_state; ++ bool char_consumed = false; ++ ++ lexer->x++; ++ if (ch == '\n') { ++ lexer->x = 0; ++ lexer->y++; ++ } ++ ++ while (flush ? lexer->state != lexer->start_state : !char_consumed) { ++ new_state = next_state(lexer, ch, flush, &char_consumed); ++ if (char_consumed) { ++ assert(!flush); ++ g_string_append_c(lexer->token, ch); ++ } ++ ++ switch (new_state) { ++ case JSON_LCURLY: ++ case JSON_RCURLY: ++ case JSON_LSQUARE: ++ case JSON_RSQUARE: ++ case JSON_COLON: ++ case JSON_COMMA: ++ case JSON_INTERP: ++ case JSON_INTEGER: ++ case JSON_FLOAT: ++ case JSON_KEYWORD: ++ case JSON_STRING: ++ json_message_process_token(lexer, lexer->token, new_state, ++ lexer->x, lexer->y); ++ /* fall through */ ++ case IN_START: ++ g_string_truncate(lexer->token, 0); ++ new_state = lexer->start_state; ++ break; ++ case JSON_ERROR: ++ json_message_process_token(lexer, lexer->token, JSON_ERROR, ++ lexer->x, lexer->y); ++ new_state = IN_RECOVERY; ++ /* fall through */ ++ case IN_RECOVERY: ++ g_string_truncate(lexer->token, 0); ++ break; ++ default: ++ break; ++ } ++ lexer->state = new_state; ++ } ++ ++ /* Do not let a single token grow to an arbitrarily large size, ++ * this is a security consideration. ++ */ ++ if (lexer->token->len > MAX_TOKEN_SIZE) { ++ json_message_process_token(lexer, lexer->token, lexer->state, ++ lexer->x, lexer->y); ++ g_string_truncate(lexer->token, 0); ++ lexer->state = lexer->start_state; ++ } ++} ++ ++void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; i++) { ++ json_lexer_feed_char(lexer, buffer[i], false); ++ } ++} ++ ++void json_lexer_flush(JSONLexer *lexer) ++{ ++ json_lexer_feed_char(lexer, 0, true); ++ assert(lexer->state == lexer->start_state); ++ json_message_process_token(lexer, lexer->token, JSON_END_OF_INPUT, ++ lexer->x, lexer->y); ++} ++ ++void json_lexer_destroy(JSONLexer *lexer) ++{ ++ g_string_free(lexer->token, true); ++} +diff --git a/qcow2/lib/qobject/json-parser-int.h b/qcow2/lib/qobject/json-parser-int.h +new file mode 100644 +index 00000000..16a25d00 +--- /dev/null ++++ b/qcow2/lib/qobject/json-parser-int.h +@@ -0,0 +1,54 @@ ++/* ++ * JSON Parser ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#ifndef JSON_PARSER_INT_H ++#define JSON_PARSER_INT_H ++ ++#include "qapi/qmp/json-parser.h" ++ ++typedef enum json_token_type { ++ JSON_ERROR = 0, /* must be zero, see json_lexer[] */ ++ /* Gap for lexer states */ ++ JSON_LCURLY = 100, ++ JSON_MIN = JSON_LCURLY, ++ JSON_RCURLY, ++ JSON_LSQUARE, ++ JSON_RSQUARE, ++ JSON_COLON, ++ JSON_COMMA, ++ JSON_INTEGER, ++ JSON_FLOAT, ++ JSON_KEYWORD, ++ JSON_STRING, ++ JSON_INTERP, ++ JSON_END_OF_INPUT, ++ JSON_MAX = JSON_END_OF_INPUT ++} JSONTokenType; ++ ++typedef struct JSONToken JSONToken; ++ ++/* json-lexer.c */ ++void json_lexer_init(JSONLexer *lexer, bool enable_interpolation); ++void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); ++void json_lexer_flush(JSONLexer *lexer); ++void json_lexer_destroy(JSONLexer *lexer); ++ ++/* json-streamer.c */ ++void json_message_process_token(JSONLexer *lexer, GString *input, ++ JSONTokenType type, int x, int y); ++ ++/* json-parser.c */ ++JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); ++QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp); ++ ++#endif +diff --git a/qcow2/lib/qobject/json-parser.c b/qcow2/lib/qobject/json-parser.c +new file mode 100644 +index 00000000..d498db6e +--- /dev/null ++++ b/qcow2/lib/qobject/json-parser.c +@@ -0,0 +1,590 @@ ++/* ++ * JSON Parser ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/ctype.h" ++#include "qemu/cutils.h" ++#include "qemu/unicode.h" ++#include "qapi/error.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "json-parser-int.h" ++ ++struct JSONToken { ++ JSONTokenType type; ++ int x; ++ int y; ++ char str[]; ++}; ++ ++typedef struct JSONParserContext { ++ Error *err; ++ JSONToken *current; ++ GQueue *buf; ++ va_list *ap; ++} JSONParserContext; ++ ++#define BUG_ON(cond) assert(!(cond)) ++ ++/** ++ * TODO ++ * ++ * 0) make errors meaningful again ++ * 1) add geometry information to tokens ++ * 3) should we return a parsed size? ++ * 4) deal with premature EOI ++ */ ++ ++static QObject *parse_value(JSONParserContext *ctxt); ++ ++/** ++ * Error handler ++ */ ++static void G_GNUC_PRINTF(3, 4) parse_error(JSONParserContext *ctxt, ++ JSONToken *token, const char *msg, ...) ++{ ++ va_list ap; ++ char message[1024]; ++ ++ if (ctxt->err) { ++ return; ++ } ++ va_start(ap, msg); ++ vsnprintf(message, sizeof(message), msg, ap); ++ va_end(ap); ++ error_setg(&ctxt->err, "JSON parse error, %s", message); ++} ++ ++static int cvt4hex(const char *s) ++{ ++ int cp, i; ++ ++ cp = 0; ++ for (i = 0; i < 4; i++) { ++ if (!qemu_isxdigit(s[i])) { ++ return -1; ++ } ++ cp <<= 4; ++ if (s[i] >= '0' && s[i] <= '9') { ++ cp |= s[i] - '0'; ++ } else if (s[i] >= 'a' && s[i] <= 'f') { ++ cp |= 10 + s[i] - 'a'; ++ } else if (s[i] >= 'A' && s[i] <= 'F') { ++ cp |= 10 + s[i] - 'A'; ++ } else { ++ return -1; ++ } ++ } ++ return cp; ++} ++ ++/** ++ * parse_string(): Parse a JSON string ++ * ++ * From RFC 8259 "The JavaScript Object Notation (JSON) Data ++ * Interchange Format": ++ * ++ * char = unescaped / ++ * escape ( ++ * %x22 / ; " quotation mark U+0022 ++ * %x5C / ; \ reverse solidus U+005C ++ * %x2F / ; / solidus U+002F ++ * %x62 / ; b backspace U+0008 ++ * %x66 / ; f form feed U+000C ++ * %x6E / ; n line feed U+000A ++ * %x72 / ; r carriage return U+000D ++ * %x74 / ; t tab U+0009 ++ * %x75 4HEXDIG ) ; uXXXX U+XXXX ++ * escape = %x5C ; \ ++ * quotation-mark = %x22 ; " ++ * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF ++ * ++ * Extensions over RFC 8259: ++ * - Extra escape sequence in strings: ++ * 0x27 (apostrophe) is recognized after escape, too ++ * - Single-quoted strings: ++ * Like double-quoted strings, except they're delimited by %x27 ++ * (apostrophe) instead of %x22 (quotation mark), and can't contain ++ * unescaped apostrophe, but can contain unescaped quotation mark. ++ * ++ * Note: ++ * - Encoding is modified UTF-8. ++ * - Invalid Unicode characters are rejected. ++ * - Control characters \x00..\x1F are rejected by the lexer. ++ */ ++static QString *parse_string(JSONParserContext *ctxt, JSONToken *token) ++{ ++ const char *ptr = token->str; ++ GString *str; ++ char quote; ++ const char *beg; ++ int cp, trailing; ++ char *end; ++ ssize_t len; ++ char utf8_buf[5]; ++ ++ assert(*ptr == '"' || *ptr == '\''); ++ quote = *ptr++; ++ str = g_string_new(NULL); ++ ++ while (*ptr != quote) { ++ assert(*ptr); ++ switch (*ptr) { ++ case '\\': ++ beg = ptr++; ++ switch (*ptr++) { ++ case '"': ++ g_string_append_c(str, '"'); ++ break; ++ case '\'': ++ g_string_append_c(str, '\''); ++ break; ++ case '\\': ++ g_string_append_c(str, '\\'); ++ break; ++ case '/': ++ g_string_append_c(str, '/'); ++ break; ++ case 'b': ++ g_string_append_c(str, '\b'); ++ break; ++ case 'f': ++ g_string_append_c(str, '\f'); ++ break; ++ case 'n': ++ g_string_append_c(str, '\n'); ++ break; ++ case 'r': ++ g_string_append_c(str, '\r'); ++ break; ++ case 't': ++ g_string_append_c(str, '\t'); ++ break; ++ case 'u': ++ cp = cvt4hex(ptr); ++ ptr += 4; ++ ++ /* handle surrogate pairs */ ++ if (cp >= 0xD800 && cp <= 0xDBFF ++ && ptr[0] == '\\' && ptr[1] == 'u') { ++ /* leading surrogate followed by \u */ ++ cp = 0x10000 + ((cp & 0x3FF) << 10); ++ trailing = cvt4hex(ptr + 2); ++ if (trailing >= 0xDC00 && trailing <= 0xDFFF) { ++ /* followed by trailing surrogate */ ++ cp |= trailing & 0x3FF; ++ ptr += 6; ++ } else { ++ cp = -1; /* invalid */ ++ } ++ } ++ ++ if (mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp) < 0) { ++ parse_error(ctxt, token, ++ "%.*s is not a valid Unicode character", ++ (int)(ptr - beg), beg); ++ goto out; ++ } ++ g_string_append(str, utf8_buf); ++ break; ++ default: ++ parse_error(ctxt, token, "invalid escape sequence in string"); ++ goto out; ++ } ++ break; ++ case '%': ++ if (ctxt->ap) { ++ if (ptr[1] != '%') { ++ parse_error(ctxt, token, "can't interpolate into string"); ++ goto out; ++ } ++ ptr++; ++ } ++ /* fall through */ ++ default: ++ cp = mod_utf8_codepoint(ptr, 6, &end); ++ if (cp < 0) { ++ parse_error(ctxt, token, "invalid UTF-8 sequence in string"); ++ goto out; ++ } ++ ptr = end; ++ len = mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp); ++ assert(len >= 0); ++ g_string_append(str, utf8_buf); ++ } ++ } ++ ++ return qstring_from_gstring(str); ++ ++out: ++ g_string_free(str, true); ++ return NULL; ++} ++ ++/* Note: the token object returned by parser_context_peek_token or ++ * parser_context_pop_token is deleted as soon as parser_context_pop_token ++ * is called again. ++ */ ++static JSONToken *parser_context_pop_token(JSONParserContext *ctxt) ++{ ++ g_free(ctxt->current); ++ ctxt->current = g_queue_pop_head(ctxt->buf); ++ return ctxt->current; ++} ++ ++static JSONToken *parser_context_peek_token(JSONParserContext *ctxt) ++{ ++ return g_queue_peek_head(ctxt->buf); ++} ++ ++/** ++ * Parsing rules ++ */ ++static int parse_pair(JSONParserContext *ctxt, QDict *dict) ++{ ++ QObject *key_obj = NULL; ++ QString *key; ++ QObject *value; ++ JSONToken *peek, *token; ++ ++ peek = parser_context_peek_token(ctxt); ++ if (peek == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ key_obj = parse_value(ctxt); ++ key = qobject_to(QString, key_obj); ++ if (!key) { ++ parse_error(ctxt, peek, "key is not a string in object"); ++ goto out; ++ } ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ if (token->type != JSON_COLON) { ++ parse_error(ctxt, token, "missing : in object pair"); ++ goto out; ++ } ++ ++ value = parse_value(ctxt); ++ if (value == NULL) { ++ parse_error(ctxt, token, "Missing value in dict"); ++ goto out; ++ } ++ ++ if (qdict_haskey(dict, qstring_get_str(key))) { ++ parse_error(ctxt, token, "duplicate key"); ++ goto out; ++ } ++ ++ qdict_put_obj(dict, qstring_get_str(key), value); ++ ++ qobject_unref(key_obj); ++ return 0; ++ ++out: ++ qobject_unref(key_obj); ++ return -1; ++} ++ ++static QObject *parse_object(JSONParserContext *ctxt) ++{ ++ QDict *dict = NULL; ++ JSONToken *token, *peek; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token && token->type == JSON_LCURLY); ++ ++ dict = qdict_new(); ++ ++ peek = parser_context_peek_token(ctxt); ++ if (peek == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ if (peek->type != JSON_RCURLY) { ++ if (parse_pair(ctxt, dict) == -1) { ++ goto out; ++ } ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ while (token->type != JSON_RCURLY) { ++ if (token->type != JSON_COMMA) { ++ parse_error(ctxt, token, "expected separator in dict"); ++ goto out; ++ } ++ ++ if (parse_pair(ctxt, dict) == -1) { ++ goto out; ++ } ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ } ++ } else { ++ (void)parser_context_pop_token(ctxt); ++ } ++ ++ return QOBJECT(dict); ++ ++out: ++ qobject_unref(dict); ++ return NULL; ++} ++ ++static QObject *parse_array(JSONParserContext *ctxt) ++{ ++ QList *list = NULL; ++ JSONToken *token, *peek; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token && token->type == JSON_LSQUARE); ++ ++ list = qlist_new(); ++ ++ peek = parser_context_peek_token(ctxt); ++ if (peek == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ if (peek->type != JSON_RSQUARE) { ++ QObject *obj; ++ ++ obj = parse_value(ctxt); ++ if (obj == NULL) { ++ parse_error(ctxt, token, "expecting value"); ++ goto out; ++ } ++ ++ qlist_append_obj(list, obj); ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ ++ while (token->type != JSON_RSQUARE) { ++ if (token->type != JSON_COMMA) { ++ parse_error(ctxt, token, "expected separator in list"); ++ goto out; ++ } ++ ++ obj = parse_value(ctxt); ++ if (obj == NULL) { ++ parse_error(ctxt, token, "expecting value"); ++ goto out; ++ } ++ ++ qlist_append_obj(list, obj); ++ ++ token = parser_context_pop_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ goto out; ++ } ++ } ++ } else { ++ (void)parser_context_pop_token(ctxt); ++ } ++ ++ return QOBJECT(list); ++ ++out: ++ qobject_unref(list); ++ return NULL; ++} ++ ++static QObject *parse_keyword(JSONParserContext *ctxt) ++{ ++ JSONToken *token; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token && token->type == JSON_KEYWORD); ++ ++ if (!strcmp(token->str, "true")) { ++ return QOBJECT(qbool_from_bool(true)); ++ } else if (!strcmp(token->str, "false")) { ++ return QOBJECT(qbool_from_bool(false)); ++ } else if (!strcmp(token->str, "null")) { ++ return QOBJECT(qnull()); ++ } ++ parse_error(ctxt, token, "invalid keyword '%s'", token->str); ++ return NULL; ++} ++ ++static QObject *parse_interpolation(JSONParserContext *ctxt) ++{ ++ JSONToken *token; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token && token->type == JSON_INTERP); ++ ++ if (!strcmp(token->str, "%p")) { ++ return va_arg(*ctxt->ap, QObject *); ++ } else if (!strcmp(token->str, "%i")) { ++ return QOBJECT(qbool_from_bool(va_arg(*ctxt->ap, int))); ++ } else if (!strcmp(token->str, "%d")) { ++ return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int))); ++ } else if (!strcmp(token->str, "%ld")) { ++ return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long))); ++ } else if (!strcmp(token->str, "%lld")) { ++ return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long long))); ++ } else if (!strcmp(token->str, "%" PRId64)) { ++ return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int64_t))); ++ } else if (!strcmp(token->str, "%u")) { ++ return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned int))); ++ } else if (!strcmp(token->str, "%lu")) { ++ return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long))); ++ } else if (!strcmp(token->str, "%llu")) { ++ return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long long))); ++ } else if (!strcmp(token->str, "%" PRIu64)) { ++ return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, uint64_t))); ++ } else if (!strcmp(token->str, "%s")) { ++ return QOBJECT(qstring_from_str(va_arg(*ctxt->ap, const char *))); ++ } else if (!strcmp(token->str, "%f")) { ++ return QOBJECT(qnum_from_double(va_arg(*ctxt->ap, double))); ++ } ++ parse_error(ctxt, token, "invalid interpolation '%s'", token->str); ++ return NULL; ++} ++ ++static QObject *parse_literal(JSONParserContext *ctxt) ++{ ++ JSONToken *token; ++ ++ token = parser_context_pop_token(ctxt); ++ assert(token); ++ ++ switch (token->type) { ++ case JSON_STRING: ++ return QOBJECT(parse_string(ctxt, token)); ++ case JSON_INTEGER: { ++ /* ++ * Represent JSON_INTEGER as QNUM_I64 if possible, else as ++ * QNUM_U64, else as QNUM_DOUBLE. Note that qemu_strtoi64() ++ * and qemu_strtou64() fail with ERANGE when it's not ++ * possible. ++ * ++ * qnum_get_int() will then work for any signed 64-bit ++ * JSON_INTEGER, qnum_get_uint() for any unsigned 64-bit ++ * integer, and qnum_get_double() both for any JSON_INTEGER ++ * and any JSON_FLOAT (with precision loss for integers beyond ++ * 53 bits) ++ */ ++ int ret; ++ int64_t value; ++ uint64_t uvalue; ++ ++ ret = qemu_strtoi64(token->str, NULL, 10, &value); ++ if (!ret) { ++ return QOBJECT(qnum_from_int(value)); ++ } ++ assert(ret == -ERANGE); ++ ++ if (token->str[0] != '-') { ++ ret = qemu_strtou64(token->str, NULL, 10, &uvalue); ++ if (!ret) { ++ return QOBJECT(qnum_from_uint(uvalue)); ++ } ++ assert(ret == -ERANGE); ++ } ++ } ++ /* fall through to JSON_FLOAT */ ++ case JSON_FLOAT: ++ /* FIXME dependent on locale; a pervasive issue in QEMU */ ++ /* FIXME our lexer matches RFC 8259 in forbidding Inf or NaN, ++ * but those might be useful extensions beyond JSON */ ++ return QOBJECT(qnum_from_double(strtod(token->str, NULL))); ++ default: ++ abort(); ++ } ++} ++ ++static QObject *parse_value(JSONParserContext *ctxt) ++{ ++ JSONToken *token; ++ ++ token = parser_context_peek_token(ctxt); ++ if (token == NULL) { ++ parse_error(ctxt, NULL, "premature EOI"); ++ return NULL; ++ } ++ ++ switch (token->type) { ++ case JSON_LCURLY: ++ return parse_object(ctxt); ++ case JSON_LSQUARE: ++ return parse_array(ctxt); ++ case JSON_INTERP: ++ return parse_interpolation(ctxt); ++ case JSON_INTEGER: ++ case JSON_FLOAT: ++ case JSON_STRING: ++ return parse_literal(ctxt); ++ case JSON_KEYWORD: ++ return parse_keyword(ctxt); ++ default: ++ parse_error(ctxt, token, "expecting value"); ++ return NULL; ++ } ++} ++ ++JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr) ++{ ++ JSONToken *token = g_malloc(sizeof(JSONToken) + tokstr->len + 1); ++ ++ token->type = type; ++ memcpy(token->str, tokstr->str, tokstr->len); ++ token->str[tokstr->len] = 0; ++ token->x = x; ++ token->y = y; ++ return token; ++} ++ ++QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp) ++{ ++ JSONParserContext ctxt = { .buf = tokens, .ap = ap }; ++ QObject *result; ++ ++ result = parse_value(&ctxt); ++ assert(ctxt.err || g_queue_is_empty(ctxt.buf)); ++ ++ error_propagate(errp, ctxt.err); ++ ++ while (!g_queue_is_empty(ctxt.buf)) { ++ parser_context_pop_token(&ctxt); ++ } ++ g_free(ctxt.current); ++ ++ return result; ++} +diff --git a/qcow2/lib/qobject/json-streamer.c b/qcow2/lib/qobject/json-streamer.c +new file mode 100644 +index 00000000..b93d97b9 +--- /dev/null ++++ b/qcow2/lib/qobject/json-streamer.c +@@ -0,0 +1,134 @@ ++/* ++ * JSON streaming support ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "json-parser-int.h" ++ ++#define MAX_TOKEN_SIZE (64ULL << 20) ++#define MAX_TOKEN_COUNT (2ULL << 20) ++#define MAX_NESTING (1 << 10) ++ ++static void json_message_free_tokens(JSONMessageParser *parser) ++{ ++ JSONToken *token; ++ ++ while ((token = g_queue_pop_head(&parser->tokens))) { ++ g_free(token); ++ } ++} ++ ++void json_message_process_token(JSONLexer *lexer, GString *input, ++ JSONTokenType type, int x, int y) ++{ ++ JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); ++ QObject *json = NULL; ++ Error *err = NULL; ++ JSONToken *token; ++ ++ switch (type) { ++ case JSON_LCURLY: ++ parser->brace_count++; ++ break; ++ case JSON_RCURLY: ++ parser->brace_count--; ++ break; ++ case JSON_LSQUARE: ++ parser->bracket_count++; ++ break; ++ case JSON_RSQUARE: ++ parser->bracket_count--; ++ break; ++ case JSON_ERROR: ++ error_setg(&err, "JSON parse error, stray '%s'", input->str); ++ goto out_emit; ++ case JSON_END_OF_INPUT: ++ if (g_queue_is_empty(&parser->tokens)) { ++ return; ++ } ++ json = json_parser_parse(&parser->tokens, parser->ap, &err); ++ goto out_emit; ++ default: ++ break; ++ } ++ ++ /* ++ * Security consideration, we limit total memory allocated per object ++ * and the maximum recursion depth that a message can force. ++ */ ++ if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) { ++ error_setg(&err, "JSON token size limit exceeded"); ++ goto out_emit; ++ } ++ if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) { ++ error_setg(&err, "JSON token count limit exceeded"); ++ goto out_emit; ++ } ++ if (parser->bracket_count + parser->brace_count > MAX_NESTING) { ++ error_setg(&err, "JSON nesting depth limit exceeded"); ++ goto out_emit; ++ } ++ ++ token = json_token(type, x, y, input); ++ parser->token_size += input->len; ++ ++ g_queue_push_tail(&parser->tokens, token); ++ ++ if ((parser->brace_count > 0 || parser->bracket_count > 0) ++ && parser->brace_count >= 0 && parser->bracket_count >= 0) { ++ return; ++ } ++ ++ json = json_parser_parse(&parser->tokens, parser->ap, &err); ++ ++out_emit: ++ parser->brace_count = 0; ++ parser->bracket_count = 0; ++ json_message_free_tokens(parser); ++ parser->token_size = 0; ++ parser->emit(parser->opaque, json, err); ++} ++ ++void json_message_parser_init(JSONMessageParser *parser, ++ void (*emit)(void *opaque, QObject *json, ++ Error *err), ++ void *opaque, va_list *ap) ++{ ++ parser->emit = emit; ++ parser->opaque = opaque; ++ parser->ap = ap; ++ parser->brace_count = 0; ++ parser->bracket_count = 0; ++ g_queue_init(&parser->tokens); ++ parser->token_size = 0; ++ ++ json_lexer_init(&parser->lexer, !!ap); ++} ++ ++void json_message_parser_feed(JSONMessageParser *parser, ++ const char *buffer, size_t size) ++{ ++ json_lexer_feed(&parser->lexer, buffer, size); ++} ++ ++void json_message_parser_flush(JSONMessageParser *parser) ++{ ++ json_lexer_flush(&parser->lexer); ++ assert(g_queue_is_empty(&parser->tokens)); ++} ++ ++void json_message_parser_destroy(JSONMessageParser *parser) ++{ ++ json_lexer_destroy(&parser->lexer); ++ json_message_free_tokens(parser); ++} +diff --git a/qcow2/lib/qobject/json-writer.c b/qcow2/lib/qobject/json-writer.c +new file mode 100644 +index 00000000..309a31d5 +--- /dev/null ++++ b/qcow2/lib/qobject/json-writer.c +@@ -0,0 +1,247 @@ ++/* ++ * JSON Writer ++ * ++ * Copyright IBM, Corp. 2009 ++ * Copyright (c) 2010-2020 Red Hat Inc. ++ * ++ * Authors: ++ * Anthony Liguori ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/json-writer.h" ++#include "qemu/unicode.h" ++ ++struct JSONWriter { ++ bool pretty; ++ bool need_comma; ++ GString *contents; ++ GByteArray *container_is_array; ++}; ++ ++JSONWriter *json_writer_new(bool pretty) ++{ ++ JSONWriter *writer = g_new(JSONWriter, 1); ++ ++ writer->pretty = pretty; ++ writer->need_comma = false; ++ writer->contents = g_string_new(NULL); ++ writer->container_is_array = g_byte_array_new(); ++ return writer; ++} ++ ++const char *json_writer_get(JSONWriter *writer) ++{ ++ g_assert(!writer->container_is_array->len); ++ return writer->contents->str; ++} ++ ++GString *json_writer_get_and_free(JSONWriter *writer) ++{ ++ GString *contents = writer->contents; ++ ++ writer->contents = NULL; ++ g_byte_array_free(writer->container_is_array, true); ++ g_free(writer); ++ return contents; ++} ++ ++void json_writer_free(JSONWriter *writer) ++{ ++ if (writer) { ++ g_string_free(json_writer_get_and_free(writer), true); ++ } ++} ++ ++static void enter_container(JSONWriter *writer, bool is_array) ++{ ++ unsigned depth = writer->container_is_array->len; ++ ++ g_byte_array_set_size(writer->container_is_array, depth + 1); ++ writer->container_is_array->data[depth] = is_array; ++ writer->need_comma = false; ++} ++ ++static void leave_container(JSONWriter *writer, bool is_array) ++{ ++ unsigned depth = writer->container_is_array->len; ++ ++ assert(depth); ++ assert(writer->container_is_array->data[depth - 1] == is_array); ++ g_byte_array_set_size(writer->container_is_array, depth - 1); ++ writer->need_comma = true; ++} ++ ++static bool in_object(JSONWriter *writer) ++{ ++ unsigned depth = writer->container_is_array->len; ++ ++ return depth && !writer->container_is_array->data[depth - 1]; ++} ++ ++static void pretty_newline(JSONWriter *writer) ++{ ++ if (writer->pretty) { ++ g_string_append_printf(writer->contents, "\n%*s", ++ writer->container_is_array->len * 4, ""); ++ } ++} ++ ++static void pretty_newline_or_space(JSONWriter *writer) ++{ ++ if (writer->pretty) { ++ g_string_append_printf(writer->contents, "\n%*s", ++ writer->container_is_array->len * 4, ""); ++ } else { ++ g_string_append_c(writer->contents, ' '); ++ } ++} ++ ++static void quoted_str(JSONWriter *writer, const char *str) ++{ ++ const char *ptr; ++ char *end; ++ int cp; ++ ++ g_string_append_c(writer->contents, '"'); ++ ++ for (ptr = str; *ptr; ptr = end) { ++ cp = mod_utf8_codepoint(ptr, 6, &end); ++ switch (cp) { ++ case '\"': ++ g_string_append(writer->contents, "\\\""); ++ break; ++ case '\\': ++ g_string_append(writer->contents, "\\\\"); ++ break; ++ case '\b': ++ g_string_append(writer->contents, "\\b"); ++ break; ++ case '\f': ++ g_string_append(writer->contents, "\\f"); ++ break; ++ case '\n': ++ g_string_append(writer->contents, "\\n"); ++ break; ++ case '\r': ++ g_string_append(writer->contents, "\\r"); ++ break; ++ case '\t': ++ g_string_append(writer->contents, "\\t"); ++ break; ++ default: ++ if (cp < 0) { ++ cp = 0xFFFD; /* replacement character */ ++ } ++ if (cp > 0xFFFF) { ++ /* beyond BMP; need a surrogate pair */ ++ g_string_append_printf(writer->contents, "\\u%04X\\u%04X", ++ 0xD800 + ((cp - 0x10000) >> 10), ++ 0xDC00 + ((cp - 0x10000) & 0x3FF)); ++ } else if (cp < 0x20 || cp >= 0x7F) { ++ g_string_append_printf(writer->contents, "\\u%04X", cp); ++ } else { ++ g_string_append_c(writer->contents, cp); ++ } ++ } ++ }; ++ ++ g_string_append_c(writer->contents, '"'); ++} ++ ++static void maybe_comma_name(JSONWriter *writer, const char *name) ++{ ++ if (writer->need_comma) { ++ g_string_append_c(writer->contents, ','); ++ pretty_newline_or_space(writer); ++ } else { ++ if (writer->contents->len) { ++ pretty_newline(writer); ++ } ++ writer->need_comma = true; ++ } ++ ++ if (in_object(writer)) { ++ quoted_str(writer, name); ++ g_string_append(writer->contents, ": "); ++ } ++} ++ ++void json_writer_start_object(JSONWriter *writer, const char *name) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append_c(writer->contents, '{'); ++ enter_container(writer, false); ++} ++ ++void json_writer_end_object(JSONWriter *writer) ++{ ++ leave_container(writer, false); ++ pretty_newline(writer); ++ g_string_append_c(writer->contents, '}'); ++} ++ ++void json_writer_start_array(JSONWriter *writer, const char *name) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append_c(writer->contents, '['); ++ enter_container(writer, true); ++} ++ ++void json_writer_end_array(JSONWriter *writer) ++{ ++ leave_container(writer, true); ++ pretty_newline(writer); ++ g_string_append_c(writer->contents, ']'); ++} ++ ++void json_writer_bool(JSONWriter *writer, const char *name, bool val) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append(writer->contents, val ? "true" : "false"); ++} ++ ++void json_writer_null(JSONWriter *writer, const char *name) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append(writer->contents, "null"); ++} ++ ++void json_writer_int64(JSONWriter *writer, const char *name, int64_t val) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append_printf(writer->contents, "%" PRId64, val); ++} ++ ++void json_writer_uint64(JSONWriter *writer, const char *name, uint64_t val) ++{ ++ maybe_comma_name(writer, name); ++ g_string_append_printf(writer->contents, "%" PRIu64, val); ++} ++ ++void json_writer_double(JSONWriter *writer, const char *name, double val) ++{ ++ maybe_comma_name(writer, name); ++ ++ /* ++ * FIXME: g_string_append_printf() is locale dependent; but JSON ++ * requires numbers to be formatted as if in the C locale. ++ * Dependence on C locale is a pervasive issue in QEMU. ++ */ ++ /* ++ * FIXME: This risks printing Inf or NaN, which are not valid ++ * JSON values. ++ */ ++ g_string_append_printf(writer->contents, "%.17g", val); ++} ++ ++void json_writer_str(JSONWriter *writer, const char *name, const char *str) ++{ ++ maybe_comma_name(writer, name); ++ quoted_str(writer, str); ++} +diff --git a/qcow2/lib/qobject/qbool.c b/qcow2/lib/qobject/qbool.c +new file mode 100644 +index 00000000..c7049c0c +--- /dev/null ++++ b/qcow2/lib/qobject/qbool.c +@@ -0,0 +1,63 @@ ++/* ++ * QBool Module ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qbool.h" ++#include "qobject-internal.h" ++ ++/** ++ * qbool_from_bool(): Create a new QBool from a bool ++ * ++ * Return strong reference. ++ */ ++QBool *qbool_from_bool(bool value) ++{ ++ QBool *qb; ++ ++ qb = g_malloc(sizeof(*qb)); ++ qobject_init(QOBJECT(qb), QTYPE_QBOOL); ++ qb->value = value; ++ ++ return qb; ++} ++ ++/** ++ * qbool_get_bool(): Get the stored bool ++ */ ++bool qbool_get_bool(const QBool *qb) ++{ ++ return qb->value; ++} ++ ++/** ++ * qbool_is_equal(): Test whether the two QBools are equal ++ */ ++bool qbool_is_equal(const QObject *x, const QObject *y) ++{ ++ return qobject_to(QBool, x)->value == qobject_to(QBool, y)->value; ++} ++ ++/** ++ * qbool_destroy_obj(): Free all memory allocated by a ++ * QBool object ++ */ ++void qbool_destroy_obj(QObject *obj) ++{ ++ assert(obj != NULL); ++ g_free(qobject_to(QBool, obj)); ++} ++ ++void qbool_unref(QBool *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qdict.c b/qcow2/lib/qobject/qdict.c +new file mode 100644 +index 00000000..8faff230 +--- /dev/null ++++ b/qcow2/lib/qobject/qdict.c +@@ -0,0 +1,449 @@ ++/* ++ * QDict Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qstring.h" ++#include "qobject-internal.h" ++ ++/** ++ * qdict_new(): Create a new QDict ++ * ++ * Return strong reference. ++ */ ++QDict *qdict_new(void) ++{ ++ QDict *qdict; ++ ++ qdict = g_malloc0(sizeof(*qdict)); ++ qobject_init(QOBJECT(qdict), QTYPE_QDICT); ++ ++ return qdict; ++} ++ ++/** ++ * tdb_hash(): based on the hash algorithm from gdbm, via tdb ++ * (from module-init-tools) ++ */ ++static unsigned int tdb_hash(const char *name) ++{ ++ unsigned value; /* Used to compute the hash value. */ ++ unsigned i; /* Used to cycle through random values. */ ++ ++ /* Set the initial value from the key size. */ ++ for (value = 0x238F13AF * strlen(name), i = 0; name[i]; i++) { ++ value = (value + (((const unsigned char *)name)[i] << (i * 5 % 24))); ++ } ++ ++ return (1103515243 * value + 12345); ++} ++ ++/** ++ * alloc_entry(): allocate a new QDictEntry ++ */ ++static QDictEntry *alloc_entry(const char *key, QObject *value) ++{ ++ QDictEntry *entry; ++ ++ entry = g_malloc0(sizeof(*entry)); ++ entry->key = g_strdup(key); ++ entry->value = value; ++ ++ return entry; ++} ++ ++/** ++ * qdict_entry_value(): Return qdict entry value ++ * ++ * Return weak reference. ++ */ ++QObject *qdict_entry_value(const QDictEntry *entry) ++{ ++ return entry->value; ++} ++ ++/** ++ * qdict_entry_key(): Return qdict entry key ++ * ++ * Return a *pointer* to the string, it has to be duplicated before being ++ * stored. ++ */ ++const char *qdict_entry_key(const QDictEntry *entry) ++{ ++ return entry->key; ++} ++ ++/** ++ * qdict_find(): List lookup function ++ */ ++static QDictEntry *qdict_find(const QDict *qdict, ++ const char *key, unsigned int bucket) ++{ ++ QDictEntry *entry; ++ ++ QLIST_FOREACH(entry, &qdict->table[bucket], next) ++ if (!strcmp(entry->key, key)) { ++ return entry; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * qdict_put_obj(): Put a new QObject into the dictionary ++ * ++ * Insert the pair 'key:value' into 'qdict', if 'key' already exists ++ * its 'value' will be replaced. ++ * ++ * This is done by freeing the reference to the stored QObject and ++ * storing the new one in the same entry. ++ * ++ * NOTE: ownership of 'value' is transferred to the QDict ++ */ ++void qdict_put_obj(QDict *qdict, const char *key, QObject *value) ++{ ++ unsigned int bucket; ++ QDictEntry *entry; ++ ++ bucket = tdb_hash(key) % QDICT_BUCKET_MAX; ++ entry = qdict_find(qdict, key, bucket); ++ if (entry) { ++ /* replace key's value */ ++ qobject_unref(entry->value); ++ entry->value = value; ++ } else { ++ /* allocate a new entry */ ++ entry = alloc_entry(key, value); ++ QLIST_INSERT_HEAD(&qdict->table[bucket], entry, next); ++ qdict->size++; ++ } ++} ++ ++void qdict_put_int(QDict *qdict, const char *key, int64_t value) ++{ ++ qdict_put(qdict, key, qnum_from_int(value)); ++} ++ ++void qdict_put_bool(QDict *qdict, const char *key, bool value) ++{ ++ qdict_put(qdict, key, qbool_from_bool(value)); ++} ++ ++void qdict_put_str(QDict *qdict, const char *key, const char *value) ++{ ++ qdict_put(qdict, key, qstring_from_str(value)); ++} ++ ++void qdict_put_null(QDict *qdict, const char *key) ++{ ++ qdict_put(qdict, key, qnull()); ++} ++ ++/** ++ * qdict_get(): Lookup for a given 'key' ++ * ++ * Return a weak reference to the QObject associated with 'key' if ++ * 'key' is present in the dictionary, NULL otherwise. ++ */ ++QObject *qdict_get(const QDict *qdict, const char *key) ++{ ++ QDictEntry *entry; ++ ++ entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); ++ return (entry == NULL ? NULL : entry->value); ++} ++ ++/** ++ * qdict_haskey(): Check if 'key' exists ++ * ++ * Return 1 if 'key' exists in the dict, 0 otherwise ++ */ ++int qdict_haskey(const QDict *qdict, const char *key) ++{ ++ unsigned int bucket = tdb_hash(key) % QDICT_BUCKET_MAX; ++ return (qdict_find(qdict, key, bucket) == NULL ? 0 : 1); ++} ++ ++/** ++ * qdict_size(): Return the size of the dictionary ++ */ ++size_t qdict_size(const QDict *qdict) ++{ ++ return qdict->size; ++} ++ ++/** ++ * qdict_get_double(): Get an number mapped by 'key' ++ * ++ * This function assumes that 'key' exists and it stores a QNum. ++ * ++ * Return number mapped by 'key'. ++ */ ++double qdict_get_double(const QDict *qdict, const char *key) ++{ ++ return qnum_get_double(qobject_to(QNum, qdict_get(qdict, key))); ++} ++ ++/** ++ * qdict_get_int(): Get an integer mapped by 'key' ++ * ++ * This function assumes that 'key' exists and it stores a ++ * QNum representable as int. ++ * ++ * Return integer mapped by 'key'. ++ */ ++int64_t qdict_get_int(const QDict *qdict, const char *key) ++{ ++ return qnum_get_int(qobject_to(QNum, qdict_get(qdict, key))); ++} ++ ++/** ++ * qdict_get_bool(): Get a bool mapped by 'key' ++ * ++ * This function assumes that 'key' exists and it stores a ++ * QBool object. ++ * ++ * Return bool mapped by 'key'. ++ */ ++bool qdict_get_bool(const QDict *qdict, const char *key) ++{ ++ return qbool_get_bool(qobject_to(QBool, qdict_get(qdict, key))); ++} ++ ++/** ++ * qdict_get_qlist(): If @qdict maps @key to a QList, return it, else NULL. ++ */ ++QList *qdict_get_qlist(const QDict *qdict, const char *key) ++{ ++ return qobject_to(QList, qdict_get(qdict, key)); ++} ++ ++/** ++ * qdict_get_qdict(): If @qdict maps @key to a QDict, return it, else NULL. ++ */ ++QDict *qdict_get_qdict(const QDict *qdict, const char *key) ++{ ++ return qobject_to(QDict, qdict_get(qdict, key)); ++} ++ ++/** ++ * qdict_get_str(): Get a pointer to the stored string mapped ++ * by 'key' ++ * ++ * This function assumes that 'key' exists and it stores a ++ * QString object. ++ * ++ * Return pointer to the string mapped by 'key'. ++ */ ++const char *qdict_get_str(const QDict *qdict, const char *key) ++{ ++ return qstring_get_str(qobject_to(QString, qdict_get(qdict, key))); ++} ++ ++/** ++ * qdict_get_try_int(): Try to get integer mapped by 'key' ++ * ++ * Return integer mapped by 'key', if it is not present in the ++ * dictionary or if the stored object is not a QNum representing an ++ * integer, 'def_value' will be returned. ++ */ ++int64_t qdict_get_try_int(const QDict *qdict, const char *key, ++ int64_t def_value) ++{ ++ QNum *qnum = qobject_to(QNum, qdict_get(qdict, key)); ++ int64_t val; ++ ++ if (!qnum || !qnum_get_try_int(qnum, &val)) { ++ return def_value; ++ } ++ ++ return val; ++} ++ ++/** ++ * qdict_get_try_bool(): Try to get a bool mapped by 'key' ++ * ++ * Return bool mapped by 'key', if it is not present in the ++ * dictionary or if the stored object is not of QBool type ++ * 'def_value' will be returned. ++ */ ++bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value) ++{ ++ QBool *qbool = qobject_to(QBool, qdict_get(qdict, key)); ++ ++ return qbool ? qbool_get_bool(qbool) : def_value; ++} ++ ++/** ++ * qdict_get_try_str(): Try to get a pointer to the stored string ++ * mapped by 'key' ++ * ++ * Return a pointer to the string mapped by 'key', if it is not present ++ * in the dictionary or if the stored object is not of QString type ++ * NULL will be returned. ++ */ ++const char *qdict_get_try_str(const QDict *qdict, const char *key) ++{ ++ QString *qstr = qobject_to(QString, qdict_get(qdict, key)); ++ ++ return qstr ? qstring_get_str(qstr) : NULL; ++} ++ ++static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket) ++{ ++ int i; ++ ++ for (i = first_bucket; i < QDICT_BUCKET_MAX; i++) { ++ if (!QLIST_EMPTY(&qdict->table[i])) { ++ return QLIST_FIRST(&qdict->table[i]); ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * qdict_first(): Return first qdict entry for iteration. ++ */ ++const QDictEntry *qdict_first(const QDict *qdict) ++{ ++ return qdict_next_entry(qdict, 0); ++} ++ ++/** ++ * qdict_next(): Return next qdict entry in an iteration. ++ */ ++const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry) ++{ ++ QDictEntry *ret; ++ ++ ret = QLIST_NEXT(entry, next); ++ if (!ret) { ++ unsigned int bucket = tdb_hash(entry->key) % QDICT_BUCKET_MAX; ++ ret = qdict_next_entry(qdict, bucket + 1); ++ } ++ ++ return ret; ++} ++ ++/** ++ * qdict_clone_shallow(): Clones a given QDict. Its entries are not copied, but ++ * another reference is added. ++ */ ++QDict *qdict_clone_shallow(const QDict *src) ++{ ++ QDict *dest; ++ QDictEntry *entry; ++ int i; ++ ++ dest = qdict_new(); ++ ++ for (i = 0; i < QDICT_BUCKET_MAX; i++) { ++ QLIST_FOREACH(entry, &src->table[i], next) { ++ qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); ++ } ++ } ++ ++ return dest; ++} ++ ++/** ++ * qentry_destroy(): Free all the memory allocated by a QDictEntry ++ */ ++static void qentry_destroy(QDictEntry *e) ++{ ++ assert(e != NULL); ++ assert(e->key != NULL); ++ assert(e->value != NULL); ++ ++ qobject_unref(e->value); ++ g_free(e->key); ++ g_free(e); ++} ++ ++/** ++ * qdict_del(): Delete a 'key:value' pair from the dictionary ++ * ++ * This will destroy all data allocated by this entry. ++ */ ++void qdict_del(QDict *qdict, const char *key) ++{ ++ QDictEntry *entry; ++ ++ entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); ++ if (entry) { ++ QLIST_REMOVE(entry, next); ++ qentry_destroy(entry); ++ qdict->size--; ++ } ++} ++ ++/** ++ * qdict_is_equal(): Test whether the two QDicts are equal ++ * ++ * Here, equality means whether they contain the same keys and whether ++ * the respective values are in turn equal (i.e. invoking ++ * qobject_is_equal() on them yields true). ++ */ ++bool qdict_is_equal(const QObject *x, const QObject *y) ++{ ++ const QDict *dict_x = qobject_to(QDict, x); ++ const QDict *dict_y = qobject_to(QDict, y); ++ const QDictEntry *e; ++ ++ if (qdict_size(dict_x) != qdict_size(dict_y)) { ++ return false; ++ } ++ ++ for (e = qdict_first(dict_x); e; e = qdict_next(dict_x, e)) { ++ const QObject *obj_x = qdict_entry_value(e); ++ const QObject *obj_y = qdict_get(dict_y, qdict_entry_key(e)); ++ ++ if (!qobject_is_equal(obj_x, obj_y)) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/** ++ * qdict_destroy_obj(): Free all the memory allocated by a QDict ++ */ ++void qdict_destroy_obj(QObject *obj) ++{ ++ int i; ++ QDict *qdict; ++ ++ assert(obj != NULL); ++ qdict = qobject_to(QDict, obj); ++ ++ for (i = 0; i < QDICT_BUCKET_MAX; i++) { ++ QDictEntry *entry = QLIST_FIRST(&qdict->table[i]); ++ while (entry) { ++ QDictEntry *tmp = QLIST_NEXT(entry, next); ++ QLIST_REMOVE(entry, next); ++ qentry_destroy(entry); ++ entry = tmp; ++ } ++ } ++ ++ g_free(qdict); ++} ++ ++void qdict_unref(QDict *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qjson.c b/qcow2/lib/qobject/qjson.c +new file mode 100644 +index 00000000..167fcb42 +--- /dev/null ++++ b/qcow2/lib/qobject/qjson.c +@@ -0,0 +1,232 @@ ++/* ++ * QObject JSON integration ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qapi/qmp/json-parser.h" ++#include "qapi/qmp/json-writer.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++ ++typedef struct JSONParsingState { ++ JSONMessageParser parser; ++ QObject *result; ++ Error *err; ++} JSONParsingState; ++ ++static void consume_json(void *opaque, QObject *json, Error *err) ++{ ++ JSONParsingState *s = opaque; ++ ++ assert(!json != !err); ++ assert(!s->result || !s->err); ++ ++ if (s->result) { ++ qobject_unref(s->result); ++ s->result = NULL; ++ error_setg(&s->err, "Expecting at most one JSON value"); ++ } ++ if (s->err) { ++ qobject_unref(json); ++ error_free(err); ++ return; ++ } ++ s->result = json; ++ s->err = err; ++} ++ ++/* ++ * Parse @string as JSON value. ++ * If @ap is non-null, interpolate %-escapes. ++ * Takes ownership of %p arguments. ++ * On success, return the JSON value. ++ * On failure, store an error through @errp and return NULL. ++ * Ownership of %p arguments becomes indeterminate then. To avoid ++ * leaks, callers passing %p must terminate on error, e.g. by passing ++ * &error_abort. ++ */ ++static QObject *qobject_from_jsonv(const char *string, va_list *ap, ++ Error **errp) ++{ ++ JSONParsingState state = {}; ++ ++ json_message_parser_init(&state.parser, consume_json, &state, ap); ++ json_message_parser_feed(&state.parser, string, strlen(string)); ++ json_message_parser_flush(&state.parser); ++ json_message_parser_destroy(&state.parser); ++ ++ if (!state.result && !state.err) { ++ error_setg(&state.err, "Expecting a JSON value"); ++ } ++ ++ error_propagate(errp, state.err); ++ return state.result; ++} ++ ++QObject *qobject_from_json(const char *string, Error **errp) ++{ ++ return qobject_from_jsonv(string, NULL, errp); ++} ++ ++/* ++ * Parse @string as JSON value with %-escapes interpolated. ++ * Abort on error. Do not use with untrusted @string. ++ * Return the resulting QObject. It is never null. ++ */ ++QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap) ++{ ++ va_list ap_copy; ++ QObject *obj; ++ ++ /* va_copy() is needed when va_list is an array type */ ++ va_copy(ap_copy, ap); ++ obj = qobject_from_jsonv(string, &ap_copy, &error_abort); ++ va_end(ap_copy); ++ ++ assert(obj); ++ return obj; ++} ++ ++/* ++ * Parse @string as JSON value with %-escapes interpolated. ++ * Abort on error. Do not use with untrusted @string. ++ * Return the resulting QObject. It is never null. ++ */ ++QObject *qobject_from_jsonf_nofail(const char *string, ...) ++{ ++ QObject *obj; ++ va_list ap; ++ ++ va_start(ap, string); ++ obj = qobject_from_vjsonf_nofail(string, ap); ++ va_end(ap); ++ ++ return obj; ++} ++ ++/* ++ * Parse @string as JSON object with %-escapes interpolated. ++ * Abort on error. Do not use with untrusted @string. ++ * Return the resulting QDict. It is never null. ++ */ ++QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap) ++{ ++ QDict *qdict; ++ ++ qdict = qobject_to(QDict, qobject_from_vjsonf_nofail(string, ap)); ++ assert(qdict); ++ return qdict; ++} ++ ++/* ++ * Parse @string as JSON object with %-escapes interpolated. ++ * Abort on error. Do not use with untrusted @string. ++ * Return the resulting QDict. It is never null. ++ */ ++QDict *qdict_from_jsonf_nofail(const char *string, ...) ++{ ++ QDict *qdict; ++ va_list ap; ++ ++ va_start(ap, string); ++ qdict = qdict_from_vjsonf_nofail(string, ap); ++ va_end(ap); ++ return qdict; ++} ++ ++static void to_json(JSONWriter *writer, const char *name, ++ const QObject *obj) ++{ ++ switch (qobject_type(obj)) { ++ case QTYPE_QNULL: ++ json_writer_null(writer, name); ++ break; ++ case QTYPE_QNUM: { ++ QNum *val = qobject_to(QNum, obj); ++ ++ switch (val->kind) { ++ case QNUM_I64: ++ json_writer_int64(writer, name, val->u.i64); ++ break; ++ case QNUM_U64: ++ json_writer_uint64(writer, name, val->u.u64); ++ break; ++ case QNUM_DOUBLE: ++ json_writer_double(writer, name, val->u.dbl); ++ break; ++ default: ++ abort(); ++ } ++ break; ++ } ++ case QTYPE_QSTRING: { ++ QString *val = qobject_to(QString, obj); ++ ++ json_writer_str(writer, name, qstring_get_str(val)); ++ break; ++ } ++ case QTYPE_QDICT: { ++ QDict *val = qobject_to(QDict, obj); ++ const QDictEntry *entry; ++ ++ json_writer_start_object(writer, name); ++ ++ for (entry = qdict_first(val); ++ entry; ++ entry = qdict_next(val, entry)) { ++ to_json(writer, qdict_entry_key(entry), qdict_entry_value(entry)); ++ } ++ ++ json_writer_end_object(writer); ++ break; ++ } ++ case QTYPE_QLIST: { ++ QList *val = qobject_to(QList, obj); ++ QListEntry *entry; ++ ++ json_writer_start_array(writer, name); ++ ++ QLIST_FOREACH_ENTRY(val, entry) { ++ to_json(writer, NULL, qlist_entry_obj(entry)); ++ } ++ ++ json_writer_end_array(writer); ++ break; ++ } ++ case QTYPE_QBOOL: { ++ QBool *val = qobject_to(QBool, obj); ++ ++ json_writer_bool(writer, name, qbool_get_bool(val)); ++ break; ++ } ++ default: ++ abort(); ++ } ++} ++ ++GString *qobject_to_json_pretty(const QObject *obj, bool pretty) ++{ ++ JSONWriter *writer = json_writer_new(pretty); ++ ++ to_json(writer, NULL, obj); ++ return json_writer_get_and_free(writer); ++} ++ ++GString *qobject_to_json(const QObject *obj) ++{ ++ return qobject_to_json_pretty(obj, false); ++} +diff --git a/qcow2/lib/qobject/qlist.c b/qcow2/lib/qobject/qlist.c +new file mode 100644 +index 00000000..356ad946 +--- /dev/null ++++ b/qcow2/lib/qobject/qlist.c +@@ -0,0 +1,189 @@ ++/* ++ * QList Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/queue.h" ++#include "qobject-internal.h" ++ ++/** ++ * qlist_new(): Create a new QList ++ * ++ * Return strong reference. ++ */ ++QList *qlist_new(void) ++{ ++ QList *qlist; ++ ++ qlist = g_malloc(sizeof(*qlist)); ++ qobject_init(QOBJECT(qlist), QTYPE_QLIST); ++ QTAILQ_INIT(&qlist->head); ++ ++ return qlist; ++} ++ ++QList *qlist_copy(QList *src) ++{ ++ QList *dst = qlist_new(); ++ QListEntry *entry; ++ QObject *elt; ++ ++ QLIST_FOREACH_ENTRY(src, entry) { ++ elt = qlist_entry_obj(entry); ++ qobject_ref(elt); ++ qlist_append_obj(dst, elt); ++ } ++ return dst; ++} ++ ++/** ++ * qlist_append_obj(): Append an QObject into QList ++ * ++ * NOTE: ownership of 'value' is transferred to the QList ++ */ ++void qlist_append_obj(QList *qlist, QObject *value) ++{ ++ QListEntry *entry; ++ ++ entry = g_malloc(sizeof(*entry)); ++ entry->value = value; ++ ++ QTAILQ_INSERT_TAIL(&qlist->head, entry, next); ++} ++ ++void qlist_append_int(QList *qlist, int64_t value) ++{ ++ qlist_append(qlist, qnum_from_int(value)); ++} ++ ++void qlist_append_bool(QList *qlist, bool value) ++{ ++ qlist_append(qlist, qbool_from_bool(value)); ++} ++ ++void qlist_append_str(QList *qlist, const char *value) ++{ ++ qlist_append(qlist, qstring_from_str(value)); ++} ++ ++void qlist_append_null(QList *qlist) ++{ ++ qlist_append(qlist, qnull()); ++} ++ ++QObject *qlist_pop(QList *qlist) ++{ ++ QListEntry *entry; ++ QObject *ret; ++ ++ if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) { ++ return NULL; ++ } ++ ++ entry = QTAILQ_FIRST(&qlist->head); ++ QTAILQ_REMOVE(&qlist->head, entry, next); ++ ++ ret = entry->value; ++ g_free(entry); ++ ++ return ret; ++} ++ ++QObject *qlist_peek(QList *qlist) ++{ ++ QListEntry *entry; ++ ++ if (qlist == NULL || QTAILQ_EMPTY(&qlist->head)) { ++ return NULL; ++ } ++ ++ entry = QTAILQ_FIRST(&qlist->head); ++ ++ return entry->value; ++} ++ ++int qlist_empty(const QList *qlist) ++{ ++ return QTAILQ_EMPTY(&qlist->head); ++} ++ ++size_t qlist_size(const QList *qlist) ++{ ++ size_t count = 0; ++ QListEntry *entry; ++ ++ QLIST_FOREACH_ENTRY(qlist, entry) { ++ count++; ++ } ++ return count; ++} ++ ++/** ++ * qlist_is_equal(): Test whether the two QLists are equal ++ * ++ * In order to be considered equal, the respective two objects at each ++ * index of the two lists have to compare equal (regarding ++ * qobject_is_equal()), and both lists have to have the same number of ++ * elements. ++ * That means both lists have to contain equal objects in equal order. ++ */ ++bool qlist_is_equal(const QObject *x, const QObject *y) ++{ ++ const QList *list_x = qobject_to(QList, x); ++ const QList *list_y = qobject_to(QList, y); ++ const QListEntry *entry_x, *entry_y; ++ ++ entry_x = qlist_first(list_x); ++ entry_y = qlist_first(list_y); ++ ++ while (entry_x && entry_y) { ++ if (!qobject_is_equal(qlist_entry_obj(entry_x), ++ qlist_entry_obj(entry_y))) ++ { ++ return false; ++ } ++ ++ entry_x = qlist_next(entry_x); ++ entry_y = qlist_next(entry_y); ++ } ++ ++ return !entry_x && !entry_y; ++} ++ ++/** ++ * qlist_destroy_obj(): Free all the memory allocated by a QList ++ */ ++void qlist_destroy_obj(QObject *obj) ++{ ++ QList *qlist; ++ QListEntry *entry, *next_entry; ++ ++ assert(obj != NULL); ++ qlist = qobject_to(QList, obj); ++ ++ QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) { ++ QTAILQ_REMOVE(&qlist->head, entry, next); ++ qobject_unref(entry->value); ++ g_free(entry); ++ } ++ ++ g_free(qlist); ++} ++ ++void qlist_unref(QList *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qnull.c b/qcow2/lib/qobject/qnull.c +new file mode 100644 +index 00000000..445a5db7 +--- /dev/null ++++ b/qcow2/lib/qobject/qnull.c +@@ -0,0 +1,36 @@ ++/* ++ * QNull ++ * ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qnull.h" ++#include "qobject-internal.h" ++ ++QNull qnull_ = { ++ .base = { ++ .type = QTYPE_QNULL, ++ .refcnt = 1, ++ }, ++}; ++ ++/** ++ * qnull_is_equal(): Always return true because any two QNull objects ++ * are equal. ++ */ ++bool qnull_is_equal(const QObject *x, const QObject *y) ++{ ++ return true; ++} ++ ++void qnull_unref(QNull *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qnum.c b/qcow2/lib/qobject/qnum.c +new file mode 100644 +index 00000000..2bbeaedc +--- /dev/null ++++ b/qcow2/lib/qobject/qnum.c +@@ -0,0 +1,246 @@ ++/* ++ * QNum Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * Anthony Liguori ++ * Marc-AndrĆ© Lureau ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qnum.h" ++#include "qobject-internal.h" ++ ++/** ++ * qnum_from_int(): Create a new QNum from an int64_t ++ * ++ * Return strong reference. ++ */ ++QNum *qnum_from_int(int64_t value) ++{ ++ QNum *qn = g_new(QNum, 1); ++ ++ qobject_init(QOBJECT(qn), QTYPE_QNUM); ++ qn->kind = QNUM_I64; ++ qn->u.i64 = value; ++ ++ return qn; ++} ++ ++/** ++ * qnum_from_uint(): Create a new QNum from an uint64_t ++ * ++ * Return strong reference. ++ */ ++QNum *qnum_from_uint(uint64_t value) ++{ ++ QNum *qn = g_new(QNum, 1); ++ ++ qobject_init(QOBJECT(qn), QTYPE_QNUM); ++ qn->kind = QNUM_U64; ++ qn->u.u64 = value; ++ ++ return qn; ++} ++ ++/** ++ * qnum_from_double(): Create a new QNum from a double ++ * ++ * Return strong reference. ++ */ ++QNum *qnum_from_double(double value) ++{ ++ QNum *qn = g_new(QNum, 1); ++ ++ qobject_init(QOBJECT(qn), QTYPE_QNUM); ++ qn->kind = QNUM_DOUBLE; ++ qn->u.dbl = value; ++ ++ return qn; ++} ++ ++/** ++ * qnum_get_try_int(): Get an integer representation of the number ++ * ++ * Return true on success. ++ */ ++bool qnum_get_try_int(const QNum *qn, int64_t *val) ++{ ++ switch (qn->kind) { ++ case QNUM_I64: ++ *val = qn->u.i64; ++ return true; ++ case QNUM_U64: ++ if (qn->u.u64 > INT64_MAX) { ++ return false; ++ } ++ *val = qn->u.u64; ++ return true; ++ case QNUM_DOUBLE: ++ return false; ++ } ++ ++ assert(0); ++ return false; ++} ++ ++/** ++ * qnum_get_int(): Get an integer representation of the number ++ * ++ * assert() on failure. ++ */ ++int64_t qnum_get_int(const QNum *qn) ++{ ++ int64_t val; ++ bool success = qnum_get_try_int(qn, &val); ++ assert(success); ++ return val; ++} ++ ++/** ++ * qnum_get_uint(): Get an unsigned integer from the number ++ * ++ * Return true on success. ++ */ ++bool qnum_get_try_uint(const QNum *qn, uint64_t *val) ++{ ++ switch (qn->kind) { ++ case QNUM_I64: ++ if (qn->u.i64 < 0) { ++ return false; ++ } ++ *val = qn->u.i64; ++ return true; ++ case QNUM_U64: ++ *val = qn->u.u64; ++ return true; ++ case QNUM_DOUBLE: ++ return false; ++ } ++ ++ assert(0); ++ return false; ++} ++ ++/** ++ * qnum_get_uint(): Get an unsigned integer from the number ++ * ++ * assert() on failure. ++ */ ++uint64_t qnum_get_uint(const QNum *qn) ++{ ++ uint64_t val; ++ bool success = qnum_get_try_uint(qn, &val); ++ assert(success); ++ return val; ++} ++ ++/** ++ * qnum_get_double(): Get a float representation of the number ++ * ++ * qnum_get_double() loses precision for integers beyond 53 bits. ++ */ ++double qnum_get_double(QNum *qn) ++{ ++ switch (qn->kind) { ++ case QNUM_I64: ++ return qn->u.i64; ++ case QNUM_U64: ++ return qn->u.u64; ++ case QNUM_DOUBLE: ++ return qn->u.dbl; ++ } ++ ++ assert(0); ++ return 0.0; ++} ++ ++char *qnum_to_string(QNum *qn) ++{ ++ switch (qn->kind) { ++ case QNUM_I64: ++ return g_strdup_printf("%" PRId64, qn->u.i64); ++ case QNUM_U64: ++ return g_strdup_printf("%" PRIu64, qn->u.u64); ++ case QNUM_DOUBLE: ++ /* 17 digits suffice for IEEE double */ ++ return g_strdup_printf("%.17g", qn->u.dbl); ++ } ++ ++ assert(0); ++ return NULL; ++} ++ ++/** ++ * qnum_is_equal(): Test whether the two QNums are equal ++ * ++ * Negative integers are never considered equal to unsigned integers, ++ * but positive integers in the range [0, INT64_MAX] are considered ++ * equal independently of whether the QNum's kind is i64 or u64. ++ * ++ * Doubles are never considered equal to integers. ++ */ ++bool qnum_is_equal(const QObject *x, const QObject *y) ++{ ++ QNum *num_x = qobject_to(QNum, x); ++ QNum *num_y = qobject_to(QNum, y); ++ ++ switch (num_x->kind) { ++ case QNUM_I64: ++ switch (num_y->kind) { ++ case QNUM_I64: ++ /* Comparison in native int64_t type */ ++ return num_x->u.i64 == num_y->u.i64; ++ case QNUM_U64: ++ /* Implicit conversion of x to uin64_t, so we have to ++ * check its sign before */ ++ return num_x->u.i64 >= 0 && num_x->u.i64 == num_y->u.u64; ++ case QNUM_DOUBLE: ++ return false; ++ } ++ abort(); ++ case QNUM_U64: ++ switch (num_y->kind) { ++ case QNUM_I64: ++ return qnum_is_equal(y, x); ++ case QNUM_U64: ++ /* Comparison in native uint64_t type */ ++ return num_x->u.u64 == num_y->u.u64; ++ case QNUM_DOUBLE: ++ return false; ++ } ++ abort(); ++ case QNUM_DOUBLE: ++ switch (num_y->kind) { ++ case QNUM_I64: ++ case QNUM_U64: ++ return false; ++ case QNUM_DOUBLE: ++ /* Comparison in native double type */ ++ return num_x->u.dbl == num_y->u.dbl; ++ } ++ abort(); ++ } ++ ++ abort(); ++} ++ ++/** ++ * qnum_destroy_obj(): Free all memory allocated by a ++ * QNum object ++ */ ++void qnum_destroy_obj(QObject *obj) ++{ ++ assert(obj != NULL); ++ g_free(qobject_to(QNum, obj)); ++} ++ ++void qnum_unref(QNum *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qobject/qobject-internal.h b/qcow2/lib/qobject/qobject-internal.h +new file mode 100644 +index 00000000..b310c8e1 +--- /dev/null ++++ b/qcow2/lib/qobject/qobject-internal.h +@@ -0,0 +1,39 @@ ++/* ++ * QObject internals ++ * ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#ifndef QOBJECT_INTERNAL_H ++#define QOBJECT_INTERNAL_H ++ ++#include "qapi/qmp/qobject.h" ++ ++static inline void qobject_init(QObject *obj, QType type) ++{ ++ assert(QTYPE_NONE < type && type < QTYPE__MAX); ++ obj->base.refcnt = 1; ++ obj->base.type = type; ++} ++ ++void qbool_destroy_obj(QObject *obj); ++bool qbool_is_equal(const QObject *x, const QObject *y); ++ ++void qdict_destroy_obj(QObject *obj); ++bool qdict_is_equal(const QObject *x, const QObject *y); ++ ++void qlist_destroy_obj(QObject *obj); ++bool qlist_is_equal(const QObject *x, const QObject *y); ++ ++bool qnull_is_equal(const QObject *x, const QObject *y); ++ ++void qnum_destroy_obj(QObject *obj); ++bool qnum_is_equal(const QObject *x, const QObject *y); ++ ++void qstring_destroy_obj(QObject *obj); ++bool qstring_is_equal(const QObject *x, const QObject *y); ++ ++#endif +diff --git a/qcow2/lib/qobject/qobject.c b/qcow2/lib/qobject/qobject.c +new file mode 100644 +index 00000000..d7077b8f +--- /dev/null ++++ b/qcow2/lib/qobject/qobject.c +@@ -0,0 +1,72 @@ ++/* ++ * QObject ++ * ++ * Copyright (C) 2015 Red Hat, Inc. ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qnull.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qstring.h" ++#include "qobject-internal.h" ++ ++QEMU_BUILD_BUG_MSG( ++ offsetof(QNull, base) != 0 || ++ offsetof(QNum, base) != 0 || ++ offsetof(QString, base) != 0 || ++ offsetof(QDict, base) != 0 || ++ offsetof(QList, base) != 0 || ++ offsetof(QBool, base) != 0, ++ "base qobject must be at offset 0"); ++ ++static void (*qdestroy[QTYPE__MAX])(QObject *) = { ++ [QTYPE_NONE] = NULL, /* No such object exists */ ++ [QTYPE_QNULL] = NULL, /* qnull_ is indestructible */ ++ [QTYPE_QNUM] = qnum_destroy_obj, ++ [QTYPE_QSTRING] = qstring_destroy_obj, ++ [QTYPE_QDICT] = qdict_destroy_obj, ++ [QTYPE_QLIST] = qlist_destroy_obj, ++ [QTYPE_QBOOL] = qbool_destroy_obj, ++}; ++ ++void qobject_destroy(QObject *obj) ++{ ++ assert(!obj->base.refcnt); ++ assert(QTYPE_QNULL < obj->base.type && obj->base.type < QTYPE__MAX); ++ qdestroy[obj->base.type](obj); ++} ++ ++ ++static bool (*qis_equal[QTYPE__MAX])(const QObject *, const QObject *) = { ++ [QTYPE_NONE] = NULL, /* No such object exists */ ++ [QTYPE_QNULL] = qnull_is_equal, ++ [QTYPE_QNUM] = qnum_is_equal, ++ [QTYPE_QSTRING] = qstring_is_equal, ++ [QTYPE_QDICT] = qdict_is_equal, ++ [QTYPE_QLIST] = qlist_is_equal, ++ [QTYPE_QBOOL] = qbool_is_equal, ++}; ++ ++bool qobject_is_equal(const QObject *x, const QObject *y) ++{ ++ /* We cannot test x == y because an object does not need to be ++ * equal to itself (e.g. NaN floats are not). */ ++ ++ if (!x && !y) { ++ return true; ++ } ++ ++ if (!x || !y || x->base.type != y->base.type) { ++ return false; ++ } ++ ++ assert(QTYPE_NONE < x->base.type && x->base.type < QTYPE__MAX); ++ ++ return qis_equal[x->base.type](x, y); ++} +diff --git a/qcow2/lib/qobject/qstring.c b/qcow2/lib/qobject/qstring.c +new file mode 100644 +index 00000000..794f8c93 +--- /dev/null ++++ b/qcow2/lib/qobject/qstring.c +@@ -0,0 +1,107 @@ ++/* ++ * QString Module ++ * ++ * Copyright (C) 2009 Red Hat Inc. ++ * ++ * Authors: ++ * Luiz Capitulino ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/qmp/qstring.h" ++#include "qobject-internal.h" ++ ++/** ++ * qstring_new(): Create a new empty QString ++ * ++ * Return strong reference. ++ */ ++QString *qstring_new(void) ++{ ++ return qstring_from_str(""); ++} ++ ++/** ++ * qstring_from_substr(): Create a new QString from a C string substring ++ * ++ * Return string reference ++ */ ++QString *qstring_from_substr(const char *str, size_t start, size_t end) ++{ ++ QString *qstring; ++ ++ assert(start <= end); ++ qstring = g_malloc(sizeof(*qstring)); ++ qobject_init(QOBJECT(qstring), QTYPE_QSTRING); ++ qstring->string = g_strndup(str + start, end - start); ++ return qstring; ++} ++ ++/** ++ * qstring_from_str(): Create a new QString from a regular C string ++ * ++ * Return strong reference. ++ */ ++QString *qstring_from_str(const char *str) ++{ ++ return qstring_from_substr(str, 0, strlen(str)); ++} ++ ++/** ++ * qstring_from_gstring(): Convert a GString to a QString ++ * ++ * Return strong reference. ++ */ ++ ++QString *qstring_from_gstring(GString *gstr) ++{ ++ QString *qstring; ++ ++ qstring = g_malloc(sizeof(*qstring)); ++ qobject_init(QOBJECT(qstring), QTYPE_QSTRING); ++ qstring->string = g_string_free(gstr, false); ++ return qstring; ++} ++ ++ ++/** ++ * qstring_get_str(): Return a pointer to the stored string ++ * ++ * NOTE: Should be used with caution, if the object is deallocated ++ * this pointer becomes invalid. ++ */ ++const char *qstring_get_str(const QString *qstring) ++{ ++ return qstring->string; ++} ++ ++/** ++ * qstring_is_equal(): Test whether the two QStrings are equal ++ */ ++bool qstring_is_equal(const QObject *x, const QObject *y) ++{ ++ return !strcmp(qobject_to(QString, x)->string, ++ qobject_to(QString, y)->string); ++} ++ ++/** ++ * qstring_destroy_obj(): Free all memory allocated by a QString ++ * object ++ */ ++void qstring_destroy_obj(QObject *obj) ++{ ++ QString *qs; ++ ++ assert(obj != NULL); ++ qs = qobject_to(QString, obj); ++ g_free((char *)qs->string); ++ g_free(qs); ++} ++ ++void qstring_unref(QString *q) ++{ ++ qobject_unref(q); ++} +diff --git a/qcow2/lib/qom/container.c b/qcow2/lib/qom/container.c +new file mode 100644 +index 00000000..455e8410 +--- /dev/null ++++ b/qcow2/lib/qom/container.c +@@ -0,0 +1,52 @@ ++/* ++ * Device Container ++ * ++ * Copyright IBM, Corp. 2012 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qom/object.h" ++#include "qemu/module.h" ++ ++static const TypeInfo container_info = { ++ .name = "container", ++ .parent = TYPE_OBJECT, ++}; ++ ++static void container_register_types(void) ++{ ++ type_register_static(&container_info); ++} ++ ++Object *container_get(Object *root, const char *path) ++{ ++ Object *obj, *child; ++ char **parts; ++ int i; ++ ++ parts = g_strsplit(path, "/", 0); ++ assert(parts != NULL && parts[0] != NULL && !parts[0][0]); ++ obj = root; ++ ++ for (i = 1; parts[i] != NULL; i++, obj = child) { ++ child = object_resolve_path_component(obj, parts[i]); ++ if (!child) { ++ child = object_new("container"); ++ object_property_add_child(obj, parts[i], child); ++ object_unref(child); ++ } ++ } ++ ++ g_strfreev(parts); ++ ++ return obj; ++} ++ ++ ++type_init(container_register_types) +diff --git a/qcow2/lib/qom/object.c b/qcow2/lib/qom/object.c +new file mode 100644 +index 00000000..157a45c5 +--- /dev/null ++++ b/qcow2/lib/qom/object.c +@@ -0,0 +1,2899 @@ ++/* ++ * QEMU Object Model ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "hw/qdev-core.h" ++#include "qapi/error.h" ++#include "qom/object.h" ++#include "qom/object_interfaces.h" ++#include "qemu/cutils.h" ++#include "qemu/memalign.h" ++#include "qapi/visitor.h" ++#include "qapi/string-input-visitor.h" ++#include "qapi/string-output-visitor.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/forward-visitor.h" ++#include "qapi/qapi-builtin-visit.h" ++#include "qapi/qmp/qjson.h" ++#include "trace.h" ++ ++/* TODO: replace QObject with a simpler visitor to avoid a dependency ++ * of the QOM core on QObject? */ ++#include "qom/qom-qobject.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qlist.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qemu/error-report.h" ++ ++#define MAX_INTERFACES 32 ++ ++typedef struct InterfaceImpl InterfaceImpl; ++typedef struct TypeImpl TypeImpl; ++ ++struct InterfaceImpl ++{ ++ const char *typename; ++}; ++ ++struct TypeImpl ++{ ++ const char *name; ++ ++ size_t class_size; ++ ++ size_t instance_size; ++ size_t instance_align; ++ ++ void (*class_init)(ObjectClass *klass, void *data); ++ void (*class_base_init)(ObjectClass *klass, void *data); ++ ++ void *class_data; ++ ++ void (*instance_init)(Object *obj); ++ void (*instance_post_init)(Object *obj); ++ void (*instance_finalize)(Object *obj); ++ ++ bool abstract; ++ ++ const char *parent; ++ TypeImpl *parent_type; ++ ++ ObjectClass *class; ++ ++ int num_interfaces; ++ InterfaceImpl interfaces[MAX_INTERFACES]; ++}; ++ ++static Type type_interface; ++ ++static GHashTable *type_table_get(void) ++{ ++ static GHashTable *type_table; ++ ++ if (type_table == NULL) { ++ type_table = g_hash_table_new(g_str_hash, g_str_equal); ++ } ++ ++ return type_table; ++} ++ ++static bool enumerating_types; ++ ++static void type_table_add(TypeImpl *ti) ++{ ++ assert(!enumerating_types); ++ g_hash_table_insert(type_table_get(), (void *)ti->name, ti); ++} ++ ++static TypeImpl *type_table_lookup(const char *name) ++{ ++ return g_hash_table_lookup(type_table_get(), name); ++} ++ ++static TypeImpl *type_new(const TypeInfo *info) ++{ ++ TypeImpl *ti = g_malloc0(sizeof(*ti)); ++ int i; ++ ++ g_assert(info->name != NULL); ++ ++ if (type_table_lookup(info->name) != NULL) { ++ fprintf(stderr, "Registering `%s' which already exists\n", info->name); ++ abort(); ++ } ++ ++ ti->name = g_strdup(info->name); ++ ti->parent = g_strdup(info->parent); ++ ++ ti->class_size = info->class_size; ++ ti->instance_size = info->instance_size; ++ ti->instance_align = info->instance_align; ++ ++ ti->class_init = info->class_init; ++ ti->class_base_init = info->class_base_init; ++ ti->class_data = info->class_data; ++ ++ ti->instance_init = info->instance_init; ++ ti->instance_post_init = info->instance_post_init; ++ ti->instance_finalize = info->instance_finalize; ++ ++ ti->abstract = info->abstract; ++ ++ for (i = 0; info->interfaces && info->interfaces[i].type; i++) { ++ ti->interfaces[i].typename = g_strdup(info->interfaces[i].type); ++ } ++ ti->num_interfaces = i; ++ ++ return ti; ++} ++ ++static bool type_name_is_valid(const char *name) ++{ ++ const int slen = strlen(name); ++ int plen; ++ ++ g_assert(slen > 1); ++ ++ /* ++ * Ideally, the name should start with a letter - however, we've got ++ * too many names starting with a digit already, so allow digits here, ++ * too (except '0' which is not used yet) ++ */ ++ if (!g_ascii_isalnum(name[0]) || name[0] == '0') { ++ return false; ++ } ++ ++ plen = strspn(name, "abcdefghijklmnopqrstuvwxyz" ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++ "0123456789-_."); ++ ++ return plen == slen; ++} ++ ++static TypeImpl *type_register_internal(const TypeInfo *info) ++{ ++ TypeImpl *ti; ++ ++ if (!type_name_is_valid(info->name)) { ++ fprintf(stderr, "Registering '%s' with illegal type name\n", info->name); ++ abort(); ++ } ++ ++ ti = type_new(info); ++ ++ type_table_add(ti); ++ return ti; ++} ++ ++TypeImpl *type_register(const TypeInfo *info) ++{ ++ assert(info->parent); ++ return type_register_internal(info); ++} ++ ++TypeImpl *type_register_static(const TypeInfo *info) ++{ ++ return type_register(info); ++} ++ ++void type_register_static_array(const TypeInfo *infos, int nr_infos) ++{ ++ int i; ++ ++ for (i = 0; i < nr_infos; i++) { ++ type_register_static(&infos[i]); ++ } ++} ++ ++static TypeImpl *type_get_by_name(const char *name) ++{ ++ if (name == NULL) { ++ return NULL; ++ } ++ ++ return type_table_lookup(name); ++} ++ ++static TypeImpl *type_get_parent(TypeImpl *type) ++{ ++ if (!type->parent_type && type->parent) { ++ type->parent_type = type_get_by_name(type->parent); ++ if (!type->parent_type) { ++ fprintf(stderr, "Type '%s' is missing its parent '%s'\n", ++ type->name, type->parent); ++ abort(); ++ } ++ } ++ ++ return type->parent_type; ++} ++ ++static bool type_has_parent(TypeImpl *type) ++{ ++ return (type->parent != NULL); ++} ++ ++static size_t type_class_get_size(TypeImpl *ti) ++{ ++ if (ti->class_size) { ++ return ti->class_size; ++ } ++ ++ if (type_has_parent(ti)) { ++ return type_class_get_size(type_get_parent(ti)); ++ } ++ ++ return sizeof(ObjectClass); ++} ++ ++static size_t type_object_get_size(TypeImpl *ti) ++{ ++ if (ti->instance_size) { ++ return ti->instance_size; ++ } ++ ++ if (type_has_parent(ti)) { ++ return type_object_get_size(type_get_parent(ti)); ++ } ++ ++ return 0; ++} ++ ++static size_t type_object_get_align(TypeImpl *ti) ++{ ++ if (ti->instance_align) { ++ return ti->instance_align; ++ } ++ ++ if (type_has_parent(ti)) { ++ return type_object_get_align(type_get_parent(ti)); ++ } ++ ++ return 0; ++} ++ ++size_t object_type_get_instance_size(const char *typename) ++{ ++ TypeImpl *type = type_get_by_name(typename); ++ ++ g_assert(type != NULL); ++ return type_object_get_size(type); ++} ++ ++static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) ++{ ++ assert(target_type); ++ ++ /* Check if target_type is a direct ancestor of type */ ++ while (type) { ++ if (type == target_type) { ++ return true; ++ } ++ ++ type = type_get_parent(type); ++ } ++ ++ return false; ++} ++ ++static void type_initialize(TypeImpl *ti); ++ ++static void type_initialize_interface(TypeImpl *ti, TypeImpl *interface_type, ++ TypeImpl *parent_type) ++{ ++ InterfaceClass *new_iface; ++ TypeInfo info = { }; ++ TypeImpl *iface_impl; ++ ++ info.parent = parent_type->name; ++ info.name = g_strdup_printf("%s::%s", ti->name, interface_type->name); ++ info.abstract = true; ++ ++ iface_impl = type_new(&info); ++ iface_impl->parent_type = parent_type; ++ type_initialize(iface_impl); ++ g_free((char *)info.name); ++ ++ new_iface = (InterfaceClass *)iface_impl->class; ++ new_iface->concrete_class = ti->class; ++ new_iface->interface_type = interface_type; ++ ++ ti->class->interfaces = g_slist_append(ti->class->interfaces, new_iface); ++} ++ ++static void object_property_free(gpointer data) ++{ ++ ObjectProperty *prop = data; ++ ++ if (prop->defval) { ++ qobject_unref(prop->defval); ++ prop->defval = NULL; ++ } ++ g_free(prop->name); ++ g_free(prop->type); ++ g_free(prop->description); ++ g_free(prop); ++} ++ ++static void type_initialize(TypeImpl *ti) ++{ ++ TypeImpl *parent; ++ ++ if (ti->class) { ++ return; ++ } ++ ++ ti->class_size = type_class_get_size(ti); ++ ti->instance_size = type_object_get_size(ti); ++ ti->instance_align = type_object_get_align(ti); ++ /* Any type with zero instance_size is implicitly abstract. ++ * This means interface types are all abstract. ++ */ ++ if (ti->instance_size == 0) { ++ ti->abstract = true; ++ } ++ if (type_is_ancestor(ti, type_interface)) { ++ assert(ti->instance_size == 0); ++ assert(ti->abstract); ++ assert(!ti->instance_init); ++ assert(!ti->instance_post_init); ++ assert(!ti->instance_finalize); ++ assert(!ti->num_interfaces); ++ } ++ ti->class = g_malloc0(ti->class_size); ++ ++ parent = type_get_parent(ti); ++ if (parent) { ++ type_initialize(parent); ++ GSList *e; ++ int i; ++ ++ g_assert(parent->class_size <= ti->class_size); ++ g_assert(parent->instance_size <= ti->instance_size); ++ memcpy(ti->class, parent->class, parent->class_size); ++ ti->class->interfaces = NULL; ++ ++ for (e = parent->class->interfaces; e; e = e->next) { ++ InterfaceClass *iface = e->data; ++ ObjectClass *klass = OBJECT_CLASS(iface); ++ ++ type_initialize_interface(ti, iface->interface_type, klass->type); ++ } ++ ++ for (i = 0; i < ti->num_interfaces; i++) { ++ TypeImpl *t = type_get_by_name(ti->interfaces[i].typename); ++ if (!t) { ++ error_report("missing interface '%s' for object '%s'", ++ ti->interfaces[i].typename, parent->name); ++ abort(); ++ } ++ for (e = ti->class->interfaces; e; e = e->next) { ++ TypeImpl *target_type = OBJECT_CLASS(e->data)->type; ++ ++ if (type_is_ancestor(target_type, t)) { ++ break; ++ } ++ } ++ ++ if (e) { ++ continue; ++ } ++ ++ type_initialize_interface(ti, t, t); ++ } ++ } ++ ++ ti->class->properties = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, ++ object_property_free); ++ ++ ti->class->type = ti; ++ ++ while (parent) { ++ if (parent->class_base_init) { ++ parent->class_base_init(ti->class, ti->class_data); ++ } ++ parent = type_get_parent(parent); ++ } ++ ++ if (ti->class_init) { ++ ti->class_init(ti->class, ti->class_data); ++ } ++} ++ ++static void object_init_with_type(Object *obj, TypeImpl *ti) ++{ ++ if (type_has_parent(ti)) { ++ object_init_with_type(obj, type_get_parent(ti)); ++ } ++ ++ if (ti->instance_init) { ++ ti->instance_init(obj); ++ } ++} ++ ++static void object_post_init_with_type(Object *obj, TypeImpl *ti) ++{ ++ if (ti->instance_post_init) { ++ ti->instance_post_init(obj); ++ } ++ ++ if (type_has_parent(ti)) { ++ object_post_init_with_type(obj, type_get_parent(ti)); ++ } ++} ++ ++bool object_apply_global_props(Object *obj, const GPtrArray *props, ++ Error **errp) ++{ ++ int i; ++ ++ if (!props) { ++ return true; ++ } ++ ++ for (i = 0; i < props->len; i++) { ++ GlobalProperty *p = g_ptr_array_index(props, i); ++ Error *err = NULL; ++ ++ if (object_dynamic_cast(obj, p->driver) == NULL) { ++ continue; ++ } ++ if (p->optional && !object_property_find(obj, p->property)) { ++ continue; ++ } ++ p->used = true; ++ if (!object_property_parse(obj, p->property, p->value, &err)) { ++ error_prepend(&err, "can't apply global %s.%s=%s: ", ++ p->driver, p->property, p->value); ++ /* ++ * If errp != NULL, propagate error and return. ++ * If errp == NULL, report a warning, but keep going ++ * with the remaining globals. ++ */ ++ if (errp) { ++ error_propagate(errp, err); ++ return false; ++ } else { ++ warn_report_err(err); ++ } ++ } ++ } ++ ++ return true; ++} ++ ++/* ++ * Global property defaults ++ * Slot 0: accelerator's global property defaults ++ * Slot 1: machine's global property defaults ++ * Slot 2: global properties from legacy command line option ++ * Each is a GPtrArray of of GlobalProperty. ++ * Applied in order, later entries override earlier ones. ++ */ ++static GPtrArray *object_compat_props[3]; ++ ++/* ++ * Retrieve @GPtrArray for global property defined with options ++ * other than "-global". These are generally used for syntactic ++ * sugar and legacy command line options. ++ */ ++void object_register_sugar_prop(const char *driver, const char *prop, ++ const char *value, bool optional) ++{ ++ GlobalProperty *g; ++ if (!object_compat_props[2]) { ++ object_compat_props[2] = g_ptr_array_new(); ++ } ++ g = g_new0(GlobalProperty, 1); ++ g->driver = g_strdup(driver); ++ g->property = g_strdup(prop); ++ g->value = g_strdup(value); ++ g->optional = optional; ++ g_ptr_array_add(object_compat_props[2], g); ++} ++ ++/* ++ * Set machine's global property defaults to @compat_props. ++ * May be called at most once. ++ */ ++void object_set_machine_compat_props(GPtrArray *compat_props) ++{ ++ assert(!object_compat_props[1]); ++ object_compat_props[1] = compat_props; ++} ++ ++/* ++ * Set accelerator's global property defaults to @compat_props. ++ * May be called at most once. ++ */ ++void object_set_accelerator_compat_props(GPtrArray *compat_props) ++{ ++ assert(!object_compat_props[0]); ++ object_compat_props[0] = compat_props; ++} ++ ++void object_apply_compat_props(Object *obj) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(object_compat_props); i++) { ++ object_apply_global_props(obj, object_compat_props[i], ++ i == 2 ? &error_fatal : &error_abort); ++ } ++} ++ ++static void object_class_property_init_all(Object *obj) ++{ ++ ObjectPropertyIterator iter; ++ ObjectProperty *prop; ++ ++ object_class_property_iter_init(&iter, object_get_class(obj)); ++ while ((prop = object_property_iter_next(&iter))) { ++ if (prop->init) { ++ prop->init(obj, prop); ++ } ++ } ++} ++ ++static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type) ++{ ++ type_initialize(type); ++ ++ g_assert(type->instance_size >= sizeof(Object)); ++ g_assert(type->abstract == false); ++ g_assert(size >= type->instance_size); ++ ++ memset(obj, 0, type->instance_size); ++ obj->class = type->class; ++ object_ref(obj); ++ object_class_property_init_all(obj); ++ obj->properties = g_hash_table_new_full(g_str_hash, g_str_equal, ++ NULL, object_property_free); ++ object_init_with_type(obj, type); ++ object_post_init_with_type(obj, type); ++} ++ ++void object_initialize(void *data, size_t size, const char *typename) ++{ ++ TypeImpl *type = type_get_by_name(typename); ++ ++#ifdef CONFIG_MODULES ++ if (!type) { ++ int rv = module_load_qom(typename, &error_fatal); ++ if (rv > 0) { ++ type = type_get_by_name(typename); ++ } else { ++ error_report("missing object type '%s'", typename); ++ exit(1); ++ } ++ } ++#endif ++ if (!type) { ++ error_report("missing object type '%s'", typename); ++ abort(); ++ } ++ ++ object_initialize_with_type(data, size, type); ++} ++ ++bool object_initialize_child_with_props(Object *parentobj, ++ const char *propname, ++ void *childobj, size_t size, ++ const char *type, ++ Error **errp, ...) ++{ ++ va_list vargs; ++ bool ok; ++ ++ va_start(vargs, errp); ++ ok = object_initialize_child_with_propsv(parentobj, propname, ++ childobj, size, type, errp, ++ vargs); ++ va_end(vargs); ++ return ok; ++} ++ ++bool object_initialize_child_with_propsv(Object *parentobj, ++ const char *propname, ++ void *childobj, size_t size, ++ const char *type, ++ Error **errp, va_list vargs) ++{ ++ bool ok = false; ++ Object *obj; ++ UserCreatable *uc; ++ ++ object_initialize(childobj, size, type); ++ obj = OBJECT(childobj); ++ ++ if (!object_set_propv(obj, errp, vargs)) { ++ goto out; ++ } ++ ++ object_property_add_child(parentobj, propname, obj); ++ ++ uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); ++ if (uc) { ++ if (!user_creatable_complete(uc, errp)) { ++ object_unparent(obj); ++ goto out; ++ } ++ } ++ ++ ok = true; ++ ++out: ++ /* ++ * We want @obj's reference to be 1 on success, 0 on failure. ++ * On success, it's 2: one taken by object_initialize(), and one ++ * by object_property_add_child(). ++ * On failure in object_initialize() or earlier, it's 1. ++ * On failure afterwards, it's also 1: object_unparent() releases ++ * the reference taken by object_property_add_child(). ++ */ ++ object_unref(obj); ++ return ok; ++} ++ ++void object_initialize_child_internal(Object *parent, ++ const char *propname, ++ void *child, size_t size, ++ const char *type) ++{ ++ object_initialize_child_with_props(parent, propname, child, size, type, ++ &error_abort, NULL); ++} ++ ++static inline bool object_property_is_child(ObjectProperty *prop) ++{ ++ return strstart(prop->type, "child<", NULL); ++} ++ ++static void object_property_del_all(Object *obj) ++{ ++ g_autoptr(GHashTable) done = g_hash_table_new(NULL, NULL); ++ ObjectProperty *prop; ++ ObjectPropertyIterator iter; ++ bool released; ++ ++ do { ++ released = false; ++ object_property_iter_init(&iter, obj); ++ while ((prop = object_property_iter_next(&iter)) != NULL) { ++ if (g_hash_table_add(done, prop)) { ++ if (prop->release) { ++ prop->release(obj, prop->name, prop->opaque); ++ released = true; ++ break; ++ } ++ } ++ } ++ } while (released); ++ ++ g_hash_table_unref(obj->properties); ++} ++ ++static void object_property_del_child(Object *obj, Object *child) ++{ ++ ObjectProperty *prop; ++ GHashTableIter iter; ++ gpointer key, value; ++ ++ g_hash_table_iter_init(&iter, obj->properties); ++ while (g_hash_table_iter_next(&iter, &key, &value)) { ++ prop = value; ++ if (object_property_is_child(prop) && prop->opaque == child) { ++ if (prop->release) { ++ prop->release(obj, prop->name, prop->opaque); ++ prop->release = NULL; ++ } ++ break; ++ } ++ } ++ g_hash_table_iter_init(&iter, obj->properties); ++ while (g_hash_table_iter_next(&iter, &key, &value)) { ++ prop = value; ++ if (object_property_is_child(prop) && prop->opaque == child) { ++ g_hash_table_iter_remove(&iter); ++ break; ++ } ++ } ++} ++ ++void object_unparent(Object *obj) ++{ ++ if (obj->parent) { ++ object_property_del_child(obj->parent, obj); ++ } ++} ++ ++static void object_deinit(Object *obj, TypeImpl *type) ++{ ++ if (type->instance_finalize) { ++ type->instance_finalize(obj); ++ } ++ ++ if (type_has_parent(type)) { ++ object_deinit(obj, type_get_parent(type)); ++ } ++} ++ ++static void object_finalize(void *data) ++{ ++ Object *obj = data; ++ TypeImpl *ti = obj->class->type; ++ ++ object_property_del_all(obj); ++ object_deinit(obj, ti); ++ ++ g_assert(obj->ref == 0); ++ g_assert(obj->parent == NULL); ++ if (obj->free) { ++ obj->free(obj); ++ } ++} ++ ++/* Find the minimum alignment guaranteed by the system malloc. */ ++#if __STDC_VERSION__ >= 201112L ++typedef max_align_t qemu_max_align_t; ++#else ++typedef union { ++ long l; ++ void *p; ++ double d; ++ long double ld; ++} qemu_max_align_t; ++#endif ++ ++static Object *object_new_with_type(Type type) ++{ ++ Object *obj; ++ size_t size, align; ++ void (*obj_free)(void *); ++ ++ g_assert(type != NULL); ++ type_initialize(type); ++ ++ size = type->instance_size; ++ align = type->instance_align; ++ ++ /* ++ * Do not use qemu_memalign unless required. Depending on the ++ * implementation, extra alignment implies extra overhead. ++ */ ++ if (likely(align <= __alignof__(qemu_max_align_t))) { ++ obj = g_malloc(size); ++ obj_free = g_free; ++ } else { ++ obj = qemu_memalign(align, size); ++ obj_free = qemu_vfree; ++ } ++ ++ object_initialize_with_type(obj, size, type); ++ obj->free = obj_free; ++ ++ return obj; ++} ++ ++Object *object_new_with_class(ObjectClass *klass) ++{ ++ return object_new_with_type(klass->type); ++} ++ ++Object *object_new(const char *typename) ++{ ++ TypeImpl *ti = type_get_by_name(typename); ++ ++ return object_new_with_type(ti); ++} ++ ++ ++Object *object_new_with_props(const char *typename, ++ Object *parent, ++ const char *id, ++ Error **errp, ++ ...) ++{ ++ va_list vargs; ++ Object *obj; ++ ++ va_start(vargs, errp); ++ obj = object_new_with_propv(typename, parent, id, errp, vargs); ++ va_end(vargs); ++ ++ return obj; ++} ++ ++ ++Object *object_new_with_propv(const char *typename, ++ Object *parent, ++ const char *id, ++ Error **errp, ++ va_list vargs) ++{ ++ Object *obj; ++ ObjectClass *klass; ++ UserCreatable *uc; ++ ++ klass = object_class_by_name(typename); ++ if (!klass) { ++ error_setg(errp, "invalid object type: %s", typename); ++ return NULL; ++ } ++ ++ if (object_class_is_abstract(klass)) { ++ error_setg(errp, "object type '%s' is abstract", typename); ++ return NULL; ++ } ++ obj = object_new_with_type(klass->type); ++ ++ if (!object_set_propv(obj, errp, vargs)) { ++ goto error; ++ } ++ ++ if (id != NULL) { ++ object_property_add_child(parent, id, obj); ++ } ++ ++ uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); ++ if (uc) { ++ if (!user_creatable_complete(uc, errp)) { ++ if (id != NULL) { ++ object_unparent(obj); ++ } ++ goto error; ++ } ++ } ++ ++ object_unref(obj); ++ return obj; ++ ++ error: ++ object_unref(obj); ++ return NULL; ++} ++ ++ ++bool object_set_props(Object *obj, ++ Error **errp, ++ ...) ++{ ++ va_list vargs; ++ bool ret; ++ ++ va_start(vargs, errp); ++ ret = object_set_propv(obj, errp, vargs); ++ va_end(vargs); ++ ++ return ret; ++} ++ ++ ++bool object_set_propv(Object *obj, ++ Error **errp, ++ va_list vargs) ++{ ++ const char *propname; ++ ++ propname = va_arg(vargs, char *); ++ while (propname != NULL) { ++ const char *value = va_arg(vargs, char *); ++ ++ g_assert(value != NULL); ++ if (!object_property_parse(obj, propname, value, errp)) { ++ return false; ++ } ++ propname = va_arg(vargs, char *); ++ } ++ ++ return true; ++} ++ ++ ++Object *object_dynamic_cast(Object *obj, const char *typename) ++{ ++ if (obj && object_class_dynamic_cast(object_get_class(obj), typename)) { ++ return obj; ++ } ++ ++ return NULL; ++} ++ ++Object *object_dynamic_cast_assert(Object *obj, const char *typename, ++ const char *file, int line, const char *func) ++{ ++ trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)", ++ typename, file, line, func); ++ ++#ifdef CONFIG_QOM_CAST_DEBUG ++ int i; ++ Object *inst; ++ ++ for (i = 0; obj && i < OBJECT_CLASS_CAST_CACHE; i++) { ++ if (qatomic_read(&obj->class->object_cast_cache[i]) == typename) { ++ goto out; ++ } ++ } ++ ++ inst = object_dynamic_cast(obj, typename); ++ ++ if (!inst && obj) { ++ fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n", ++ file, line, func, obj, typename); ++ abort(); ++ } ++ ++ assert(obj == inst); ++ ++ if (obj && obj == inst) { ++ for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) { ++ qatomic_set(&obj->class->object_cast_cache[i - 1], ++ qatomic_read(&obj->class->object_cast_cache[i])); ++ } ++ qatomic_set(&obj->class->object_cast_cache[i - 1], typename); ++ } ++ ++out: ++#endif ++ return obj; ++} ++ ++ObjectClass *object_class_dynamic_cast(ObjectClass *class, ++ const char *typename) ++{ ++ ObjectClass *ret = NULL; ++ TypeImpl *target_type; ++ TypeImpl *type; ++ ++ if (!class) { ++ return NULL; ++ } ++ ++ /* A simple fast path that can trigger a lot for leaf classes. */ ++ type = class->type; ++ if (type->name == typename) { ++ return class; ++ } ++ ++ target_type = type_get_by_name(typename); ++ if (!target_type) { ++ /* target class type unknown, so fail the cast */ ++ return NULL; ++ } ++ ++ if (type->class->interfaces && ++ type_is_ancestor(target_type, type_interface)) { ++ int found = 0; ++ GSList *i; ++ ++ for (i = class->interfaces; i; i = i->next) { ++ ObjectClass *target_class = i->data; ++ ++ if (type_is_ancestor(target_class->type, target_type)) { ++ ret = target_class; ++ found++; ++ } ++ } ++ ++ /* The match was ambiguous, don't allow a cast */ ++ if (found > 1) { ++ ret = NULL; ++ } ++ } else if (type_is_ancestor(type, target_type)) { ++ ret = class; ++ } ++ ++ return ret; ++} ++ ++ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class, ++ const char *typename, ++ const char *file, int line, ++ const char *func) ++{ ++ ObjectClass *ret; ++ ++ trace_object_class_dynamic_cast_assert(class ? class->type->name : "(null)", ++ typename, file, line, func); ++ ++#ifdef CONFIG_QOM_CAST_DEBUG ++ int i; ++ ++ for (i = 0; class && i < OBJECT_CLASS_CAST_CACHE; i++) { ++ if (qatomic_read(&class->class_cast_cache[i]) == typename) { ++ ret = class; ++ goto out; ++ } ++ } ++#else ++ if (!class || !class->interfaces) { ++ return class; ++ } ++#endif ++ ++ ret = object_class_dynamic_cast(class, typename); ++ if (!ret && class) { ++ fprintf(stderr, "%s:%d:%s: Object %p is not an instance of type %s\n", ++ file, line, func, class, typename); ++ abort(); ++ } ++ ++#ifdef CONFIG_QOM_CAST_DEBUG ++ if (class && ret == class) { ++ for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) { ++ qatomic_set(&class->class_cast_cache[i - 1], ++ qatomic_read(&class->class_cast_cache[i])); ++ } ++ qatomic_set(&class->class_cast_cache[i - 1], typename); ++ } ++out: ++#endif ++ return ret; ++} ++ ++const char *object_get_typename(const Object *obj) ++{ ++ return obj->class->type->name; ++} ++ ++ObjectClass *object_get_class(Object *obj) ++{ ++ return obj->class; ++} ++ ++bool object_class_is_abstract(ObjectClass *klass) ++{ ++ return klass->type->abstract; ++} ++ ++const char *object_class_get_name(ObjectClass *klass) ++{ ++ return klass->type->name; ++} ++ ++ObjectClass *object_class_by_name(const char *typename) ++{ ++ TypeImpl *type = type_get_by_name(typename); ++ ++ if (!type) { ++ return NULL; ++ } ++ ++ type_initialize(type); ++ ++ return type->class; ++} ++ ++ObjectClass *module_object_class_by_name(const char *typename) ++{ ++ ObjectClass *oc; ++ ++ oc = object_class_by_name(typename); ++#ifdef CONFIG_MODULES ++ if (!oc) { ++ Error *local_err = NULL; ++ int rv = module_load_qom(typename, &local_err); ++ if (rv > 0) { ++ oc = object_class_by_name(typename); ++ } else if (rv < 0) { ++ error_report_err(local_err); ++ } ++ } ++#endif ++ return oc; ++} ++ ++ObjectClass *object_class_get_parent(ObjectClass *class) ++{ ++ TypeImpl *type = type_get_parent(class->type); ++ ++ if (!type) { ++ return NULL; ++ } ++ ++ type_initialize(type); ++ ++ return type->class; ++} ++ ++typedef struct OCFData ++{ ++ void (*fn)(ObjectClass *klass, void *opaque); ++ const char *implements_type; ++ bool include_abstract; ++ void *opaque; ++} OCFData; ++ ++static void object_class_foreach_tramp(gpointer key, gpointer value, ++ gpointer opaque) ++{ ++ OCFData *data = opaque; ++ TypeImpl *type = value; ++ ObjectClass *k; ++ ++ type_initialize(type); ++ k = type->class; ++ ++ if (!data->include_abstract && type->abstract) { ++ return; ++ } ++ ++ if (data->implements_type && ++ !object_class_dynamic_cast(k, data->implements_type)) { ++ return; ++ } ++ ++ data->fn(k, data->opaque); ++} ++ ++void object_class_foreach(void (*fn)(ObjectClass *klass, void *opaque), ++ const char *implements_type, bool include_abstract, ++ void *opaque) ++{ ++ OCFData data = { fn, implements_type, include_abstract, opaque }; ++ ++ enumerating_types = true; ++ g_hash_table_foreach(type_table_get(), object_class_foreach_tramp, &data); ++ enumerating_types = false; ++} ++ ++static int do_object_child_foreach(Object *obj, ++ int (*fn)(Object *child, void *opaque), ++ void *opaque, bool recurse) ++{ ++ GHashTableIter iter; ++ ObjectProperty *prop; ++ int ret = 0; ++ ++ g_hash_table_iter_init(&iter, obj->properties); ++ while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { ++ if (object_property_is_child(prop)) { ++ Object *child = prop->opaque; ++ ++ ret = fn(child, opaque); ++ if (ret != 0) { ++ break; ++ } ++ if (recurse) { ++ ret = do_object_child_foreach(child, fn, opaque, true); ++ if (ret != 0) { ++ break; ++ } ++ } ++ } ++ } ++ return ret; ++} ++ ++int object_child_foreach(Object *obj, int (*fn)(Object *child, void *opaque), ++ void *opaque) ++{ ++ return do_object_child_foreach(obj, fn, opaque, false); ++} ++ ++int object_child_foreach_recursive(Object *obj, ++ int (*fn)(Object *child, void *opaque), ++ void *opaque) ++{ ++ return do_object_child_foreach(obj, fn, opaque, true); ++} ++ ++static void object_class_get_list_tramp(ObjectClass *klass, void *opaque) ++{ ++ GSList **list = opaque; ++ ++ *list = g_slist_prepend(*list, klass); ++} ++ ++GSList *object_class_get_list(const char *implements_type, ++ bool include_abstract) ++{ ++ GSList *list = NULL; ++ ++ object_class_foreach(object_class_get_list_tramp, ++ implements_type, include_abstract, &list); ++ return list; ++} ++ ++static gint object_class_cmp(gconstpointer a, gconstpointer b) ++{ ++ return strcasecmp(object_class_get_name((ObjectClass *)a), ++ object_class_get_name((ObjectClass *)b)); ++} ++ ++GSList *object_class_get_list_sorted(const char *implements_type, ++ bool include_abstract) ++{ ++ return g_slist_sort(object_class_get_list(implements_type, include_abstract), ++ object_class_cmp); ++} ++ ++Object *object_ref(void *objptr) ++{ ++ Object *obj = OBJECT(objptr); ++ uint32_t ref; ++ ++ if (!obj) { ++ return NULL; ++ } ++ ref = qatomic_fetch_inc(&obj->ref); ++ /* Assert waaay before the integer overflows */ ++ g_assert(ref < INT_MAX); ++ return obj; ++} ++ ++void object_unref(void *objptr) ++{ ++ Object *obj = OBJECT(objptr); ++ if (!obj) { ++ return; ++ } ++ g_assert(obj->ref > 0); ++ ++ /* parent always holds a reference to its children */ ++ if (qatomic_fetch_dec(&obj->ref) == 1) { ++ object_finalize(obj); ++ } ++} ++ ++ObjectProperty * ++object_property_try_add(Object *obj, const char *name, const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque, Error **errp) ++{ ++ ObjectProperty *prop; ++ size_t name_len = strlen(name); ++ ++ if (name_len >= 3 && !memcmp(name + name_len - 3, "[*]", 4)) { ++ int i; ++ ObjectProperty *ret = NULL; ++ char *name_no_array = g_strdup(name); ++ ++ name_no_array[name_len - 3] = '\0'; ++ for (i = 0; i < INT16_MAX; ++i) { ++ char *full_name = g_strdup_printf("%s[%d]", name_no_array, i); ++ ++ ret = object_property_try_add(obj, full_name, type, get, set, ++ release, opaque, NULL); ++ g_free(full_name); ++ if (ret) { ++ break; ++ } ++ } ++ g_free(name_no_array); ++ assert(ret); ++ return ret; ++ } ++ ++ if (object_property_find(obj, name) != NULL) { ++ error_setg(errp, "attempt to add duplicate property '%s' to object (type '%s')", ++ name, object_get_typename(obj)); ++ return NULL; ++ } ++ ++ prop = g_malloc0(sizeof(*prop)); ++ ++ prop->name = g_strdup(name); ++ prop->type = g_strdup(type); ++ ++ prop->get = get; ++ prop->set = set; ++ prop->release = release; ++ prop->opaque = opaque; ++ ++ g_hash_table_insert(obj->properties, prop->name, prop); ++ return prop; ++} ++ ++ObjectProperty * ++object_property_add(Object *obj, const char *name, const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque) ++{ ++ return object_property_try_add(obj, name, type, get, set, release, ++ opaque, &error_abort); ++} ++ ++ObjectProperty * ++object_class_property_add(ObjectClass *klass, ++ const char *name, ++ const char *type, ++ ObjectPropertyAccessor *get, ++ ObjectPropertyAccessor *set, ++ ObjectPropertyRelease *release, ++ void *opaque) ++{ ++ ObjectProperty *prop; ++ ++ assert(!object_class_property_find(klass, name)); ++ ++ prop = g_malloc0(sizeof(*prop)); ++ ++ prop->name = g_strdup(name); ++ prop->type = g_strdup(type); ++ ++ prop->get = get; ++ prop->set = set; ++ prop->release = release; ++ prop->opaque = opaque; ++ ++ g_hash_table_insert(klass->properties, prop->name, prop); ++ ++ return prop; ++} ++ ++ObjectProperty *object_property_find(Object *obj, const char *name) ++{ ++ ObjectProperty *prop; ++ ObjectClass *klass = object_get_class(obj); ++ ++ prop = object_class_property_find(klass, name); ++ if (prop) { ++ return prop; ++ } ++ ++ return g_hash_table_lookup(obj->properties, name); ++} ++ ++ObjectProperty *object_property_find_err(Object *obj, const char *name, ++ Error **errp) ++{ ++ ObjectProperty *prop = object_property_find(obj, name); ++ if (!prop) { ++ error_setg(errp, "Property '%s.%s' not found", ++ object_get_typename(obj), name); ++ } ++ return prop; ++} ++ ++void object_property_iter_init(ObjectPropertyIterator *iter, ++ Object *obj) ++{ ++ g_hash_table_iter_init(&iter->iter, obj->properties); ++ iter->nextclass = object_get_class(obj); ++} ++ ++ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter) ++{ ++ gpointer key, val; ++ while (!g_hash_table_iter_next(&iter->iter, &key, &val)) { ++ if (!iter->nextclass) { ++ return NULL; ++ } ++ g_hash_table_iter_init(&iter->iter, iter->nextclass->properties); ++ iter->nextclass = object_class_get_parent(iter->nextclass); ++ } ++ return val; ++} ++ ++void object_class_property_iter_init(ObjectPropertyIterator *iter, ++ ObjectClass *klass) ++{ ++ g_hash_table_iter_init(&iter->iter, klass->properties); ++ iter->nextclass = object_class_get_parent(klass); ++} ++ ++ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name) ++{ ++ ObjectClass *parent_klass; ++ ++ parent_klass = object_class_get_parent(klass); ++ if (parent_klass) { ++ ObjectProperty *prop = ++ object_class_property_find(parent_klass, name); ++ if (prop) { ++ return prop; ++ } ++ } ++ ++ return g_hash_table_lookup(klass->properties, name); ++} ++ ++ObjectProperty *object_class_property_find_err(ObjectClass *klass, ++ const char *name, ++ Error **errp) ++{ ++ ObjectProperty *prop = object_class_property_find(klass, name); ++ if (!prop) { ++ error_setg(errp, "Property '.%s' not found", name); ++ } ++ return prop; ++} ++ ++ ++void object_property_del(Object *obj, const char *name) ++{ ++ ObjectProperty *prop = g_hash_table_lookup(obj->properties, name); ++ ++ if (prop->release) { ++ prop->release(obj, name, prop->opaque); ++ } ++ g_hash_table_remove(obj->properties, name); ++} ++ ++bool object_property_get(Object *obj, const char *name, Visitor *v, ++ Error **errp) ++{ ++ Error *err = NULL; ++ ObjectProperty *prop = object_property_find_err(obj, name, errp); ++ ++ if (prop == NULL) { ++ return false; ++ } ++ ++ if (!prop->get) { ++ error_setg(errp, "Property '%s.%s' is not readable", ++ object_get_typename(obj), name); ++ return false; ++ } ++ prop->get(obj, v, name, prop->opaque, &err); ++ error_propagate(errp, err); ++ return !err; ++} ++ ++bool object_property_set(Object *obj, const char *name, Visitor *v, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ ObjectProperty *prop = object_property_find_err(obj, name, errp); ++ ++ if (prop == NULL) { ++ return false; ++ } ++ ++ if (!prop->set) { ++ error_setg(errp, "Property '%s.%s' is not writable", ++ object_get_typename(obj), name); ++ return false; ++ } ++ prop->set(obj, v, name, prop->opaque, errp); ++ return !*errp; ++} ++ ++bool object_property_set_str(Object *obj, const char *name, ++ const char *value, Error **errp) ++{ ++ QString *qstr = qstring_from_str(value); ++ bool ok = object_property_set_qobject(obj, name, QOBJECT(qstr), errp); ++ ++ qobject_unref(qstr); ++ return ok; ++} ++ ++char *object_property_get_str(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = object_property_get_qobject(obj, name, errp); ++ QString *qstring; ++ char *retval; ++ ++ if (!ret) { ++ return NULL; ++ } ++ qstring = qobject_to(QString, ret); ++ if (!qstring) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: string", ++ name); ++ retval = NULL; ++ } else { ++ retval = g_strdup(qstring_get_str(qstring)); ++ } ++ ++ qobject_unref(ret); ++ return retval; ++} ++ ++bool object_property_set_link(Object *obj, const char *name, ++ Object *value, Error **errp) ++{ ++ g_autofree char *path = NULL; ++ ++ if (value) { ++ path = object_get_canonical_path(value); ++ } ++ return object_property_set_str(obj, name, path ?: "", errp); ++} ++ ++Object *object_property_get_link(Object *obj, const char *name, ++ Error **errp) ++{ ++ char *str = object_property_get_str(obj, name, errp); ++ Object *target = NULL; ++ ++ if (str && *str) { ++ target = object_resolve_path(str, NULL); ++ if (!target) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", str); ++ } ++ } ++ ++ g_free(str); ++ return target; ++} ++ ++bool object_property_set_bool(Object *obj, const char *name, ++ bool value, Error **errp) ++{ ++ QBool *qbool = qbool_from_bool(value); ++ bool ok = object_property_set_qobject(obj, name, QOBJECT(qbool), errp); ++ ++ qobject_unref(qbool); ++ return ok; ++} ++ ++bool object_property_get_bool(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = object_property_get_qobject(obj, name, errp); ++ QBool *qbool; ++ bool retval; ++ ++ if (!ret) { ++ return false; ++ } ++ qbool = qobject_to(QBool, ret); ++ if (!qbool) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: boolean", ++ name); ++ retval = false; ++ } else { ++ retval = qbool_get_bool(qbool); ++ } ++ ++ qobject_unref(ret); ++ return retval; ++} ++ ++bool object_property_set_int(Object *obj, const char *name, ++ int64_t value, Error **errp) ++{ ++ QNum *qnum = qnum_from_int(value); ++ bool ok = object_property_set_qobject(obj, name, QOBJECT(qnum), errp); ++ ++ qobject_unref(qnum); ++ return ok; ++} ++ ++int64_t object_property_get_int(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = object_property_get_qobject(obj, name, errp); ++ QNum *qnum; ++ int64_t retval; ++ ++ if (!ret) { ++ return -1; ++ } ++ ++ qnum = qobject_to(QNum, ret); ++ if (!qnum || !qnum_get_try_int(qnum, &retval)) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: int", ++ name); ++ retval = -1; ++ } ++ ++ qobject_unref(ret); ++ return retval; ++} ++ ++static void object_property_init_defval(Object *obj, ObjectProperty *prop) ++{ ++ Visitor *v = qobject_input_visitor_new(prop->defval); ++ ++ assert(prop->set != NULL); ++ prop->set(obj, v, prop->name, prop->opaque, &error_abort); ++ ++ visit_free(v); ++} ++ ++static void object_property_set_default(ObjectProperty *prop, QObject *defval) ++{ ++ assert(!prop->defval); ++ assert(!prop->init); ++ ++ prop->defval = defval; ++ prop->init = object_property_init_defval; ++} ++ ++void object_property_set_default_bool(ObjectProperty *prop, bool value) ++{ ++ object_property_set_default(prop, QOBJECT(qbool_from_bool(value))); ++} ++ ++void object_property_set_default_str(ObjectProperty *prop, const char *value) ++{ ++ object_property_set_default(prop, QOBJECT(qstring_from_str(value))); ++} ++ ++void object_property_set_default_list(ObjectProperty *prop) ++{ ++ object_property_set_default(prop, QOBJECT(qlist_new())); ++} ++ ++void object_property_set_default_int(ObjectProperty *prop, int64_t value) ++{ ++ object_property_set_default(prop, QOBJECT(qnum_from_int(value))); ++} ++ ++void object_property_set_default_uint(ObjectProperty *prop, uint64_t value) ++{ ++ object_property_set_default(prop, QOBJECT(qnum_from_uint(value))); ++} ++ ++bool object_property_set_uint(Object *obj, const char *name, ++ uint64_t value, Error **errp) ++{ ++ QNum *qnum = qnum_from_uint(value); ++ bool ok = object_property_set_qobject(obj, name, QOBJECT(qnum), errp); ++ ++ qobject_unref(qnum); ++ return ok; ++} ++ ++uint64_t object_property_get_uint(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = object_property_get_qobject(obj, name, errp); ++ QNum *qnum; ++ uint64_t retval; ++ ++ if (!ret) { ++ return 0; ++ } ++ qnum = qobject_to(QNum, ret); ++ if (!qnum || !qnum_get_try_uint(qnum, &retval)) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: uint", ++ name); ++ retval = 0; ++ } ++ ++ qobject_unref(ret); ++ return retval; ++} ++ ++typedef struct EnumProperty { ++ const QEnumLookup *lookup; ++ int (*get)(Object *, Error **); ++ void (*set)(Object *, int, Error **); ++} EnumProperty; ++ ++int object_property_get_enum(Object *obj, const char *name, ++ const char *typename, Error **errp) ++{ ++ char *str; ++ int ret; ++ ObjectProperty *prop = object_property_find_err(obj, name, errp); ++ EnumProperty *enumprop; ++ ++ if (prop == NULL) { ++ return -1; ++ } ++ ++ if (!g_str_equal(prop->type, typename)) { ++ error_setg(errp, "Property %s on %s is not '%s' enum type", ++ name, object_class_get_name( ++ object_get_class(obj)), typename); ++ return -1; ++ } ++ ++ enumprop = prop->opaque; ++ ++ str = object_property_get_str(obj, name, errp); ++ if (!str) { ++ return -1; ++ } ++ ++ ret = qapi_enum_parse(enumprop->lookup, str, -1, errp); ++ g_free(str); ++ ++ return ret; ++} ++ ++bool object_property_parse(Object *obj, const char *name, ++ const char *string, Error **errp) ++{ ++ Visitor *v = string_input_visitor_new(string); ++ bool ok = object_property_set(obj, name, v, errp); ++ ++ visit_free(v); ++ return ok; ++} ++ ++char *object_property_print(Object *obj, const char *name, bool human, ++ Error **errp) ++{ ++ Visitor *v; ++ char *string = NULL; ++ ++ v = string_output_visitor_new(human, &string); ++ if (!object_property_get(obj, name, v, errp)) { ++ goto out; ++ } ++ ++ visit_complete(v, &string); ++ ++out: ++ visit_free(v); ++ return string; ++} ++ ++const char *object_property_get_type(Object *obj, const char *name, Error **errp) ++{ ++ ObjectProperty *prop = object_property_find_err(obj, name, errp); ++ if (prop == NULL) { ++ return NULL; ++ } ++ ++ return prop->type; ++} ++ ++Object *object_get_root(void) ++{ ++ static Object *root; ++ ++ if (!root) { ++ root = object_new("container"); ++ } ++ ++ return root; ++} ++ ++Object *object_get_objects_root(void) ++{ ++ return container_get(object_get_root(), "/objects"); ++} ++ ++Object *object_get_internal_root(void) ++{ ++ static Object *internal_root; ++ ++ if (!internal_root) { ++ internal_root = object_new("container"); ++ } ++ ++ return internal_root; ++} ++ ++static void object_get_child_property(Object *obj, Visitor *v, ++ const char *name, void *opaque, ++ Error **errp) ++{ ++ Object *child = opaque; ++ char *path; ++ ++ path = object_get_canonical_path(child); ++ visit_type_str(v, name, &path, errp); ++ g_free(path); ++} ++ ++static Object *object_resolve_child_property(Object *parent, void *opaque, ++ const char *part) ++{ ++ return opaque; ++} ++ ++static void object_finalize_child_property(Object *obj, const char *name, ++ void *opaque) ++{ ++ Object *child = opaque; ++ ++ if (child->class->unparent) { ++ (child->class->unparent)(child); ++ } ++ child->parent = NULL; ++ object_unref(child); ++} ++ ++ObjectProperty * ++object_property_try_add_child(Object *obj, const char *name, ++ Object *child, Error **errp) ++{ ++ g_autofree char *type = NULL; ++ ObjectProperty *op; ++ ++ assert(!child->parent); ++ ++ type = g_strdup_printf("child<%s>", object_get_typename(child)); ++ ++ op = object_property_try_add(obj, name, type, object_get_child_property, ++ NULL, object_finalize_child_property, ++ child, errp); ++ if (!op) { ++ return NULL; ++ } ++ op->resolve = object_resolve_child_property; ++ object_ref(child); ++ child->parent = obj; ++ return op; ++} ++ ++ObjectProperty * ++object_property_add_child(Object *obj, const char *name, ++ Object *child) ++{ ++ return object_property_try_add_child(obj, name, child, &error_abort); ++} ++ ++void object_property_allow_set_link(const Object *obj, const char *name, ++ Object *val, Error **errp) ++{ ++ /* Allow the link to be set, always */ ++} ++ ++typedef struct { ++ union { ++ Object **targetp; ++ Object *target; /* if OBJ_PROP_LINK_DIRECT, when holding the pointer */ ++ ptrdiff_t offset; /* if OBJ_PROP_LINK_CLASS */ ++ }; ++ void (*check)(const Object *, const char *, Object *, Error **); ++ ObjectPropertyLinkFlags flags; ++} LinkProperty; ++ ++static Object ** ++object_link_get_targetp(Object *obj, LinkProperty *lprop) ++{ ++ if (lprop->flags & OBJ_PROP_LINK_DIRECT) { ++ return &lprop->target; ++ } else if (lprop->flags & OBJ_PROP_LINK_CLASS) { ++ return (void *)obj + lprop->offset; ++ } else { ++ return lprop->targetp; ++ } ++} ++ ++static void object_get_link_property(Object *obj, Visitor *v, ++ const char *name, void *opaque, ++ Error **errp) ++{ ++ LinkProperty *lprop = opaque; ++ Object **targetp = object_link_get_targetp(obj, lprop); ++ char *path; ++ ++ if (*targetp) { ++ path = object_get_canonical_path(*targetp); ++ visit_type_str(v, name, &path, errp); ++ g_free(path); ++ } else { ++ path = (char *)""; ++ visit_type_str(v, name, &path, errp); ++ } ++} ++ ++/* ++ * object_resolve_link: ++ * ++ * Lookup an object and ensure its type matches the link property type. This ++ * is similar to object_resolve_path() except type verification against the ++ * link property is performed. ++ * ++ * Returns: The matched object or NULL on path lookup failures. ++ */ ++static Object *object_resolve_link(Object *obj, const char *name, ++ const char *path, Error **errp) ++{ ++ const char *type; ++ char *target_type; ++ bool ambiguous = false; ++ Object *target; ++ ++ /* Go from link to FOO. */ ++ type = object_property_get_type(obj, name, NULL); ++ target_type = g_strndup(&type[5], strlen(type) - 6); ++ target = object_resolve_path_type(path, target_type, &ambiguous); ++ ++ if (ambiguous) { ++ error_setg(errp, "Path '%s' does not uniquely identify an object", ++ path); ++ } else if (!target) { ++ target = object_resolve_path(path, &ambiguous); ++ if (target || ambiguous) { ++ error_setg(errp, "Invalid parameter type for '%s', expected: %s", ++ name, target_type); ++ } else { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, ++ "Device '%s' not found", path); ++ } ++ target = NULL; ++ } ++ g_free(target_type); ++ ++ return target; ++} ++ ++static void object_set_link_property(Object *obj, Visitor *v, ++ const char *name, void *opaque, ++ Error **errp) ++{ ++ Error *local_err = NULL; ++ LinkProperty *prop = opaque; ++ Object **targetp = object_link_get_targetp(obj, prop); ++ Object *old_target = *targetp; ++ Object *new_target; ++ char *path = NULL; ++ ++ if (!visit_type_str(v, name, &path, errp)) { ++ return; ++ } ++ ++ if (*path) { ++ new_target = object_resolve_link(obj, name, path, errp); ++ if (!new_target) { ++ g_free(path); ++ return; ++ } ++ } else { ++ new_target = NULL; ++ } ++ ++ g_free(path); ++ ++ prop->check(obj, name, new_target, &local_err); ++ if (local_err) { ++ error_propagate(errp, local_err); ++ return; ++ } ++ ++ *targetp = new_target; ++ if (prop->flags & OBJ_PROP_LINK_STRONG) { ++ object_ref(new_target); ++ object_unref(old_target); ++ } ++} ++ ++static Object *object_resolve_link_property(Object *parent, void *opaque, ++ const char *part) ++{ ++ LinkProperty *lprop = opaque; ++ ++ return *object_link_get_targetp(parent, lprop); ++} ++ ++static void object_release_link_property(Object *obj, const char *name, ++ void *opaque) ++{ ++ LinkProperty *prop = opaque; ++ Object **targetp = object_link_get_targetp(obj, prop); ++ ++ if ((prop->flags & OBJ_PROP_LINK_STRONG) && *targetp) { ++ object_unref(*targetp); ++ } ++ if (!(prop->flags & OBJ_PROP_LINK_CLASS)) { ++ g_free(prop); ++ } ++} ++ ++static ObjectProperty * ++object_add_link_prop(Object *obj, const char *name, ++ const char *type, void *ptr, ++ void (*check)(const Object *, const char *, ++ Object *, Error **), ++ ObjectPropertyLinkFlags flags) ++{ ++ LinkProperty *prop = g_malloc(sizeof(*prop)); ++ g_autofree char *full_type = NULL; ++ ObjectProperty *op; ++ ++ if (flags & OBJ_PROP_LINK_DIRECT) { ++ prop->target = ptr; ++ } else { ++ prop->targetp = ptr; ++ } ++ prop->check = check; ++ prop->flags = flags; ++ ++ full_type = g_strdup_printf("link<%s>", type); ++ ++ op = object_property_add(obj, name, full_type, ++ object_get_link_property, ++ check ? object_set_link_property : NULL, ++ object_release_link_property, ++ prop); ++ op->resolve = object_resolve_link_property; ++ return op; ++} ++ ++ObjectProperty * ++object_property_add_link(Object *obj, const char *name, ++ const char *type, Object **targetp, ++ void (*check)(const Object *, const char *, ++ Object *, Error **), ++ ObjectPropertyLinkFlags flags) ++{ ++ return object_add_link_prop(obj, name, type, targetp, check, flags); ++} ++ ++ObjectProperty * ++object_class_property_add_link(ObjectClass *oc, ++ const char *name, ++ const char *type, ptrdiff_t offset, ++ void (*check)(const Object *obj, const char *name, ++ Object *val, Error **errp), ++ ObjectPropertyLinkFlags flags) ++{ ++ LinkProperty *prop = g_new0(LinkProperty, 1); ++ char *full_type; ++ ObjectProperty *op; ++ ++ prop->offset = offset; ++ prop->check = check; ++ prop->flags = flags | OBJ_PROP_LINK_CLASS; ++ ++ full_type = g_strdup_printf("link<%s>", type); ++ ++ op = object_class_property_add(oc, name, full_type, ++ object_get_link_property, ++ check ? object_set_link_property : NULL, ++ object_release_link_property, ++ prop); ++ ++ op->resolve = object_resolve_link_property; ++ ++ g_free(full_type); ++ return op; ++} ++ ++ObjectProperty * ++object_property_add_const_link(Object *obj, const char *name, ++ Object *target) ++{ ++ return object_add_link_prop(obj, name, ++ object_get_typename(target), target, ++ NULL, OBJ_PROP_LINK_DIRECT); ++} ++ ++const char *object_get_canonical_path_component(const Object *obj) ++{ ++ ObjectProperty *prop = NULL; ++ GHashTableIter iter; ++ ++ if (obj->parent == NULL) { ++ return NULL; ++ } ++ ++ g_hash_table_iter_init(&iter, obj->parent->properties); ++ while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { ++ if (!object_property_is_child(prop)) { ++ continue; ++ } ++ ++ if (prop->opaque == obj) { ++ return prop->name; ++ } ++ } ++ ++ /* obj had a parent but was not a child, should never happen */ ++ g_assert_not_reached(); ++ return NULL; ++} ++ ++char *object_get_canonical_path(const Object *obj) ++{ ++ Object *root = object_get_root(); ++ char *newpath, *path = NULL; ++ ++ if (obj == root) { ++ return g_strdup("/"); ++ } ++ ++ do { ++ const char *component = object_get_canonical_path_component(obj); ++ ++ if (!component) { ++ /* A canonical path must be complete, so discard what was ++ * collected so far. ++ */ ++ g_free(path); ++ return NULL; ++ } ++ ++ newpath = g_strdup_printf("/%s%s", component, path ? path : ""); ++ g_free(path); ++ path = newpath; ++ obj = obj->parent; ++ } while (obj != root); ++ ++ return path; ++} ++ ++Object *object_resolve_path_component(Object *parent, const char *part) ++{ ++ ObjectProperty *prop = object_property_find(parent, part); ++ if (prop == NULL) { ++ return NULL; ++ } ++ ++ if (prop->resolve) { ++ return prop->resolve(parent, prop->opaque, part); ++ } else { ++ return NULL; ++ } ++} ++ ++static Object *object_resolve_abs_path(Object *parent, ++ char **parts, ++ const char *typename) ++{ ++ Object *child; ++ ++ if (*parts == NULL) { ++ return object_dynamic_cast(parent, typename); ++ } ++ ++ if (strcmp(*parts, "") == 0) { ++ return object_resolve_abs_path(parent, parts + 1, typename); ++ } ++ ++ child = object_resolve_path_component(parent, *parts); ++ if (!child) { ++ return NULL; ++ } ++ ++ return object_resolve_abs_path(child, parts + 1, typename); ++} ++ ++static Object *object_resolve_partial_path(Object *parent, ++ char **parts, ++ const char *typename, ++ bool *ambiguous) ++{ ++ Object *obj; ++ GHashTableIter iter; ++ ObjectProperty *prop; ++ ++ obj = object_resolve_abs_path(parent, parts, typename); ++ ++ g_hash_table_iter_init(&iter, parent->properties); ++ while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { ++ Object *found; ++ ++ if (!object_property_is_child(prop)) { ++ continue; ++ } ++ ++ found = object_resolve_partial_path(prop->opaque, parts, ++ typename, ambiguous); ++ if (found) { ++ if (obj) { ++ *ambiguous = true; ++ return NULL; ++ } ++ obj = found; ++ } ++ ++ if (*ambiguous) { ++ return NULL; ++ } ++ } ++ ++ return obj; ++} ++ ++Object *object_resolve_path_type(const char *path, const char *typename, ++ bool *ambiguousp) ++{ ++ Object *obj; ++ char **parts; ++ ++ parts = g_strsplit(path, "/", 0); ++ assert(parts); ++ ++ if (parts[0] == NULL || strcmp(parts[0], "") != 0) { ++ bool ambiguous = false; ++ obj = object_resolve_partial_path(object_get_root(), parts, ++ typename, &ambiguous); ++ if (ambiguousp) { ++ *ambiguousp = ambiguous; ++ } ++ } else { ++ obj = object_resolve_abs_path(object_get_root(), parts + 1, typename); ++ } ++ ++ g_strfreev(parts); ++ ++ return obj; ++} ++ ++Object *object_resolve_path(const char *path, bool *ambiguous) ++{ ++ return object_resolve_path_type(path, TYPE_OBJECT, ambiguous); ++} ++ ++Object *object_resolve_path_at(Object *parent, const char *path) ++{ ++ g_auto(GStrv) parts = g_strsplit(path, "/", 0); ++ ++ if (*path == '/') { ++ return object_resolve_abs_path(object_get_root(), parts + 1, ++ TYPE_OBJECT); ++ } ++ return object_resolve_abs_path(parent, parts, TYPE_OBJECT); ++} ++ ++Object *object_resolve_type_unambiguous(const char *typename, Error **errp) ++{ ++ bool ambig; ++ Object *o = object_resolve_path_type("", typename, &ambig); ++ ++ if (ambig) { ++ error_setg(errp, "More than one object of type %s", typename); ++ return NULL; ++ } ++ if (!o) { ++ error_setg(errp, "No object found of type %s", typename); ++ return NULL; ++ } ++ return o; ++} ++ ++typedef struct StringProperty ++{ ++ char *(*get)(Object *, Error **); ++ void (*set)(Object *, const char *, Error **); ++} StringProperty; ++ ++static void property_get_str(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ StringProperty *prop = opaque; ++ char *value; ++ Error *err = NULL; ++ ++ value = prop->get(obj, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ visit_type_str(v, name, &value, errp); ++ g_free(value); ++} ++ ++static void property_set_str(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ StringProperty *prop = opaque; ++ char *value; ++ ++ if (!visit_type_str(v, name, &value, errp)) { ++ return; ++ } ++ ++ prop->set(obj, value, errp); ++ g_free(value); ++} ++ ++static void property_release_data(Object *obj, const char *name, ++ void *opaque) ++{ ++ g_free(opaque); ++} ++ ++ObjectProperty * ++object_property_add_str(Object *obj, const char *name, ++ char *(*get)(Object *, Error **), ++ void (*set)(Object *, const char *, Error **)) ++{ ++ StringProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ prop->set = set; ++ ++ return object_property_add(obj, name, "string", ++ get ? property_get_str : NULL, ++ set ? property_set_str : NULL, ++ property_release_data, ++ prop); ++} ++ ++ObjectProperty * ++object_class_property_add_str(ObjectClass *klass, const char *name, ++ char *(*get)(Object *, Error **), ++ void (*set)(Object *, const char *, ++ Error **)) ++{ ++ StringProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ prop->set = set; ++ ++ return object_class_property_add(klass, name, "string", ++ get ? property_get_str : NULL, ++ set ? property_set_str : NULL, ++ NULL, ++ prop); ++} ++ ++typedef struct BoolProperty ++{ ++ bool (*get)(Object *, Error **); ++ void (*set)(Object *, bool, Error **); ++} BoolProperty; ++ ++static void property_get_bool(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ BoolProperty *prop = opaque; ++ bool value; ++ Error *err = NULL; ++ ++ value = prop->get(obj, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ visit_type_bool(v, name, &value, errp); ++} ++ ++static void property_set_bool(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ BoolProperty *prop = opaque; ++ bool value; ++ ++ if (!visit_type_bool(v, name, &value, errp)) { ++ return; ++ } ++ ++ prop->set(obj, value, errp); ++} ++ ++ObjectProperty * ++object_property_add_bool(Object *obj, const char *name, ++ bool (*get)(Object *, Error **), ++ void (*set)(Object *, bool, Error **)) ++{ ++ BoolProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ prop->set = set; ++ ++ return object_property_add(obj, name, "bool", ++ get ? property_get_bool : NULL, ++ set ? property_set_bool : NULL, ++ property_release_data, ++ prop); ++} ++ ++ObjectProperty * ++object_class_property_add_bool(ObjectClass *klass, const char *name, ++ bool (*get)(Object *, Error **), ++ void (*set)(Object *, bool, Error **)) ++{ ++ BoolProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ prop->set = set; ++ ++ return object_class_property_add(klass, name, "bool", ++ get ? property_get_bool : NULL, ++ set ? property_set_bool : NULL, ++ NULL, ++ prop); ++} ++ ++static void property_get_enum(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ EnumProperty *prop = opaque; ++ int value; ++ Error *err = NULL; ++ ++ value = prop->get(obj, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ visit_type_enum(v, name, &value, prop->lookup, errp); ++} ++ ++static void property_set_enum(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ EnumProperty *prop = opaque; ++ int value; ++ ++ if (!visit_type_enum(v, name, &value, prop->lookup, errp)) { ++ return; ++ } ++ prop->set(obj, value, errp); ++} ++ ++ObjectProperty * ++object_property_add_enum(Object *obj, const char *name, ++ const char *typename, ++ const QEnumLookup *lookup, ++ int (*get)(Object *, Error **), ++ void (*set)(Object *, int, Error **)) ++{ ++ EnumProperty *prop = g_malloc(sizeof(*prop)); ++ ++ prop->lookup = lookup; ++ prop->get = get; ++ prop->set = set; ++ ++ return object_property_add(obj, name, typename, ++ get ? property_get_enum : NULL, ++ set ? property_set_enum : NULL, ++ property_release_data, ++ prop); ++} ++ ++ObjectProperty * ++object_class_property_add_enum(ObjectClass *klass, const char *name, ++ const char *typename, ++ const QEnumLookup *lookup, ++ int (*get)(Object *, Error **), ++ void (*set)(Object *, int, Error **)) ++{ ++ EnumProperty *prop = g_malloc(sizeof(*prop)); ++ ++ prop->lookup = lookup; ++ prop->get = get; ++ prop->set = set; ++ ++ return object_class_property_add(klass, name, typename, ++ get ? property_get_enum : NULL, ++ set ? property_set_enum : NULL, ++ NULL, ++ prop); ++} ++ ++typedef struct TMProperty { ++ void (*get)(Object *, struct tm *, Error **); ++} TMProperty; ++ ++static void property_get_tm(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ TMProperty *prop = opaque; ++ Error *err = NULL; ++ struct tm value; ++ ++ prop->get(obj, &value, &err); ++ if (err) { ++ error_propagate(errp, err); ++ return; ++ } ++ ++ if (!visit_start_struct(v, name, NULL, 0, errp)) { ++ return; ++ } ++ if (!visit_type_int32(v, "tm_year", &value.tm_year, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_mon", &value.tm_mon, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_mday", &value.tm_mday, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_hour", &value.tm_hour, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_min", &value.tm_min, errp)) { ++ goto out_end; ++ } ++ if (!visit_type_int32(v, "tm_sec", &value.tm_sec, errp)) { ++ goto out_end; ++ } ++ visit_check_struct(v, errp); ++out_end: ++ visit_end_struct(v, NULL); ++} ++ ++ObjectProperty * ++object_property_add_tm(Object *obj, const char *name, ++ void (*get)(Object *, struct tm *, Error **)) ++{ ++ TMProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ ++ return object_property_add(obj, name, "struct tm", ++ get ? property_get_tm : NULL, NULL, ++ property_release_data, ++ prop); ++} ++ ++ObjectProperty * ++object_class_property_add_tm(ObjectClass *klass, const char *name, ++ void (*get)(Object *, struct tm *, Error **)) ++{ ++ TMProperty *prop = g_malloc0(sizeof(*prop)); ++ ++ prop->get = get; ++ ++ return object_class_property_add(klass, name, "struct tm", ++ get ? property_get_tm : NULL, ++ NULL, NULL, prop); ++} ++ ++static char *object_get_type(Object *obj, Error **errp) ++{ ++ return g_strdup(object_get_typename(obj)); ++} ++ ++static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint8_t value = *(uint8_t *)opaque; ++ visit_type_uint8(v, name, &value, errp); ++} ++ ++static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint8_t *field = opaque; ++ uint8_t value; ++ ++ if (!visit_type_uint8(v, name, &value, errp)) { ++ return; ++ } ++ ++ *field = value; ++} ++ ++static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint16_t value = *(uint16_t *)opaque; ++ visit_type_uint16(v, name, &value, errp); ++} ++ ++static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint16_t *field = opaque; ++ uint16_t value; ++ ++ if (!visit_type_uint16(v, name, &value, errp)) { ++ return; ++ } ++ ++ *field = value; ++} ++ ++static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint32_t value = *(uint32_t *)opaque; ++ visit_type_uint32(v, name, &value, errp); ++} ++ ++static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint32_t *field = opaque; ++ uint32_t value; ++ ++ if (!visit_type_uint32(v, name, &value, errp)) { ++ return; ++ } ++ ++ *field = value; ++} ++ ++static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint64_t value = *(uint64_t *)opaque; ++ visit_type_uint64(v, name, &value, errp); ++} ++ ++static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ uint64_t *field = opaque; ++ uint64_t value; ++ ++ if (!visit_type_uint64(v, name, &value, errp)) { ++ return; ++ } ++ ++ *field = value; ++} ++ ++ObjectProperty * ++object_property_add_uint8_ptr(Object *obj, const char *name, ++ const uint8_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint8_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint8_ptr; ++ } ++ ++ return object_property_add(obj, name, "uint8", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, ++ const uint8_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint8_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint8_ptr; ++ } ++ ++ return object_class_property_add(klass, name, "uint8", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_property_add_uint16_ptr(Object *obj, const char *name, ++ const uint16_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint16_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint16_ptr; ++ } ++ ++ return object_property_add(obj, name, "uint16", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, ++ const uint16_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint16_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint16_ptr; ++ } ++ ++ return object_class_property_add(klass, name, "uint16", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_property_add_uint32_ptr(Object *obj, const char *name, ++ const uint32_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint32_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint32_ptr; ++ } ++ ++ return object_property_add(obj, name, "uint32", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, ++ const uint32_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint32_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint32_ptr; ++ } ++ ++ return object_class_property_add(klass, name, "uint32", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_property_add_uint64_ptr(Object *obj, const char *name, ++ const uint64_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint64_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint64_ptr; ++ } ++ ++ return object_property_add(obj, name, "uint64", ++ getter, setter, NULL, (void *)v); ++} ++ ++ObjectProperty * ++object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, ++ const uint64_t *v, ++ ObjectPropertyFlags flags) ++{ ++ ObjectPropertyAccessor *getter = NULL; ++ ObjectPropertyAccessor *setter = NULL; ++ ++ if ((flags & OBJ_PROP_FLAG_READ) == OBJ_PROP_FLAG_READ) { ++ getter = property_get_uint64_ptr; ++ } ++ ++ if ((flags & OBJ_PROP_FLAG_WRITE) == OBJ_PROP_FLAG_WRITE) { ++ setter = property_set_uint64_ptr; ++ } ++ ++ return object_class_property_add(klass, name, "uint64", ++ getter, setter, NULL, (void *)v); ++} ++ ++typedef struct { ++ Object *target_obj; ++ char *target_name; ++} AliasProperty; ++ ++static void property_get_alias(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ AliasProperty *prop = opaque; ++ Visitor *alias_v = visitor_forward_field(v, prop->target_name, name); ++ ++ object_property_get(prop->target_obj, prop->target_name, alias_v, errp); ++ visit_free(alias_v); ++} ++ ++static void property_set_alias(Object *obj, Visitor *v, const char *name, ++ void *opaque, Error **errp) ++{ ++ AliasProperty *prop = opaque; ++ Visitor *alias_v = visitor_forward_field(v, prop->target_name, name); ++ ++ object_property_set(prop->target_obj, prop->target_name, alias_v, errp); ++ visit_free(alias_v); ++} ++ ++static Object *property_resolve_alias(Object *obj, void *opaque, ++ const char *part) ++{ ++ AliasProperty *prop = opaque; ++ ++ return object_resolve_path_component(prop->target_obj, prop->target_name); ++} ++ ++static void property_release_alias(Object *obj, const char *name, void *opaque) ++{ ++ AliasProperty *prop = opaque; ++ ++ g_free(prop->target_name); ++ g_free(prop); ++} ++ ++ObjectProperty * ++object_property_add_alias(Object *obj, const char *name, ++ Object *target_obj, const char *target_name) ++{ ++ AliasProperty *prop; ++ ObjectProperty *op; ++ ObjectProperty *target_prop; ++ g_autofree char *prop_type = NULL; ++ ++ target_prop = object_property_find_err(target_obj, target_name, ++ &error_abort); ++ ++ if (object_property_is_child(target_prop)) { ++ prop_type = g_strdup_printf("link%s", ++ target_prop->type + strlen("child")); ++ } else { ++ prop_type = g_strdup(target_prop->type); ++ } ++ ++ prop = g_malloc(sizeof(*prop)); ++ prop->target_obj = target_obj; ++ prop->target_name = g_strdup(target_name); ++ ++ op = object_property_add(obj, name, prop_type, ++ property_get_alias, ++ property_set_alias, ++ property_release_alias, ++ prop); ++ op->resolve = property_resolve_alias; ++ if (target_prop->defval) { ++ op->defval = qobject_ref(target_prop->defval); ++ } ++ ++ object_property_set_description(obj, op->name, ++ target_prop->description); ++ return op; ++} ++ ++void object_property_set_description(Object *obj, const char *name, ++ const char *description) ++{ ++ ObjectProperty *op; ++ ++ op = object_property_find_err(obj, name, &error_abort); ++ g_free(op->description); ++ op->description = g_strdup(description); ++} ++ ++void object_class_property_set_description(ObjectClass *klass, ++ const char *name, ++ const char *description) ++{ ++ ObjectProperty *op; ++ ++ op = g_hash_table_lookup(klass->properties, name); ++ g_free(op->description); ++ op->description = g_strdup(description); ++} ++ ++static void object_class_init(ObjectClass *klass, void *data) ++{ ++ object_class_property_add_str(klass, "type", object_get_type, ++ NULL); ++} ++ ++static void register_types(void) ++{ ++ static const TypeInfo interface_info = { ++ .name = TYPE_INTERFACE, ++ .class_size = sizeof(InterfaceClass), ++ .abstract = true, ++ }; ++ ++ static const TypeInfo object_info = { ++ .name = TYPE_OBJECT, ++ .instance_size = sizeof(Object), ++ .class_init = object_class_init, ++ .abstract = true, ++ }; ++ ++ type_interface = type_register_internal(&interface_info); ++ type_register_internal(&object_info); ++} ++ ++type_init(register_types) +diff --git a/qcow2/lib/qom/qom-qobject.c b/qcow2/lib/qom/qom-qobject.c +new file mode 100644 +index 00000000..21ce22de +--- /dev/null ++++ b/qcow2/lib/qom/qom-qobject.c +@@ -0,0 +1,45 @@ ++/* ++ * QEMU Object Model - QObject wrappers ++ * ++ * Copyright (C) 2012 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qom/object.h" ++#include "qom/qom-qobject.h" ++#include "qapi/visitor.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/qobject-output-visitor.h" ++ ++bool object_property_set_qobject(Object *obj, ++ const char *name, QObject *value, ++ Error **errp) ++{ ++ Visitor *v; ++ bool ok; ++ ++ v = qobject_input_visitor_new(value); ++ ok = object_property_set(obj, name, v, errp); ++ visit_free(v); ++ return ok; ++} ++ ++QObject *object_property_get_qobject(Object *obj, const char *name, ++ Error **errp) ++{ ++ QObject *ret = NULL; ++ Visitor *v; ++ ++ v = qobject_output_visitor_new(&ret); ++ if (object_property_get(obj, name, v, errp)) { ++ visit_complete(v, &ret); ++ } ++ visit_free(v); ++ return ret; ++} +diff --git a/qcow2/lib/system/.dirstamp b/qcow2/lib/system/.dirstamp +new file mode 100644 +index 00000000..e69de29b +diff --git a/qcow2/lib/system/cpus.c b/qcow2/lib/system/cpus.c +new file mode 100644 +index 00000000..1c818ff6 +--- /dev/null ++++ b/qcow2/lib/system/cpus.c +@@ -0,0 +1,878 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "monitor/monitor.h" ++#include "qemu/coroutine-tls.h" ++#include "qapi/error.h" ++#include "qapi/qapi-commands-machine.h" ++#include "qapi/qapi-commands-misc.h" ++#include "qapi/qapi-events-run-state.h" ++#include "qapi/qmp/qerror.h" ++#include "exec/gdbstub.h" ++#include "sysemu/hw_accel.h" ++#include "exec/cpu-common.h" ++#include "qemu/thread.h" ++#include "qemu/main-loop.h" ++#include "qemu/plugin.h" ++#include "sysemu/cpus.h" ++#include "qemu/guest-random.h" ++#include "hw/nmi.h" ++#include "sysemu/replay.h" ++#include "sysemu/runstate.h" ++#include "sysemu/cpu-timers.h" ++#include "sysemu/whpx.h" ++#include "hw/boards.h" ++#include "hw/hw.h" ++#include "trace.h" ++ ++#ifdef CONFIG_LINUX ++ ++#include ++ ++#ifndef PR_MCE_KILL ++#define PR_MCE_KILL 33 ++#endif ++ ++#ifndef PR_MCE_KILL_SET ++#define PR_MCE_KILL_SET 1 ++#endif ++ ++#ifndef PR_MCE_KILL_EARLY ++#define PR_MCE_KILL_EARLY 1 ++#endif ++ ++#endif /* CONFIG_LINUX */ ++ ++/* The Big QEMU Lock (BQL) */ ++static QemuMutex bql; ++ ++/* ++ * The chosen accelerator is supposed to register this. ++ */ ++static const AccelOpsClass *cpus_accel; ++ ++bool cpu_is_stopped(CPUState *cpu) ++{ ++ return cpu->stopped || !runstate_is_running(); ++} ++ ++bool cpu_work_list_empty(CPUState *cpu) ++{ ++ return QSIMPLEQ_EMPTY_ATOMIC(&cpu->work_list); ++} ++ ++bool cpu_thread_is_idle(CPUState *cpu) ++{ ++ if (cpu->stop || !cpu_work_list_empty(cpu)) { ++ return false; ++ } ++ if (cpu_is_stopped(cpu)) { ++ return true; ++ } ++ if (!cpu->halted || cpu_has_work(cpu)) { ++ return false; ++ } ++ if (cpus_accel->cpu_thread_is_idle) { ++ return cpus_accel->cpu_thread_is_idle(cpu); ++ } ++ return true; ++} ++ ++bool all_cpu_threads_idle(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ if (!cpu_thread_is_idle(cpu)) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++/***********************************************************/ ++void hw_error(const char *fmt, ...) ++{ ++ va_list ap; ++ CPUState *cpu; ++ ++ va_start(ap, fmt); ++ fprintf(stderr, "qemu: hardware error: "); ++ vfprintf(stderr, fmt, ap); ++ fprintf(stderr, "\n"); ++ CPU_FOREACH(cpu) { ++ fprintf(stderr, "CPU #%d:\n", cpu->cpu_index); ++ cpu_dump_state(cpu, stderr, CPU_DUMP_FPU); ++ } ++ va_end(ap); ++ abort(); ++} ++ ++void cpu_synchronize_all_states(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ cpu_synchronize_state(cpu); ++ } ++} ++ ++void cpu_synchronize_all_post_reset(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ cpu_synchronize_post_reset(cpu); ++ } ++} ++ ++void cpu_synchronize_all_post_init(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ cpu_synchronize_post_init(cpu); ++ } ++} ++ ++void cpu_synchronize_all_pre_loadvm(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ cpu_synchronize_pre_loadvm(cpu); ++ } ++} ++ ++void cpu_synchronize_state(CPUState *cpu) ++{ ++ if (cpus_accel->synchronize_state) { ++ cpus_accel->synchronize_state(cpu); ++ } ++} ++ ++void cpu_synchronize_post_reset(CPUState *cpu) ++{ ++ if (cpus_accel->synchronize_post_reset) { ++ cpus_accel->synchronize_post_reset(cpu); ++ } ++} ++ ++void cpu_synchronize_post_init(CPUState *cpu) ++{ ++ if (cpus_accel->synchronize_post_init) { ++ cpus_accel->synchronize_post_init(cpu); ++ } ++} ++ ++void cpu_synchronize_pre_loadvm(CPUState *cpu) ++{ ++ if (cpus_accel->synchronize_pre_loadvm) { ++ cpus_accel->synchronize_pre_loadvm(cpu); ++ } ++} ++ ++bool cpus_are_resettable(void) ++{ ++ if (cpus_accel->cpus_are_resettable) { ++ return cpus_accel->cpus_are_resettable(); ++ } ++ return true; ++} ++ ++void cpu_exec_reset_hold(CPUState *cpu) ++{ ++ if (cpus_accel->cpu_reset_hold) { ++ cpus_accel->cpu_reset_hold(cpu); ++ } ++} ++ ++int64_t cpus_get_virtual_clock(void) ++{ ++ /* ++ * XXX ++ * ++ * need to check that cpus_accel is not NULL, because qcow2 calls ++ * qemu_get_clock_ns(CLOCK_VIRTUAL) without any accel initialized and ++ * with ticks disabled in some io-tests: ++ * 030 040 041 060 099 120 127 140 156 161 172 181 191 192 195 203 229 249 256 267 ++ * ++ * is this expected? ++ * ++ * XXX ++ */ ++ if (cpus_accel && cpus_accel->get_virtual_clock) { ++ return cpus_accel->get_virtual_clock(); ++ } ++ return cpu_get_clock(); ++} ++ ++/* ++ * Signal the new virtual time to the accelerator. This is only needed ++ * by accelerators that need to track the changes as we warp time. ++ */ ++void cpus_set_virtual_clock(int64_t new_time) ++{ ++ if (cpus_accel && cpus_accel->set_virtual_clock) { ++ cpus_accel->set_virtual_clock(new_time); ++ } ++} ++ ++/* ++ * return the time elapsed in VM between vm_start and vm_stop. Unless ++ * icount is active, cpus_get_elapsed_ticks() uses units of the host CPU cycle ++ * counter. ++ */ ++int64_t cpus_get_elapsed_ticks(void) ++{ ++ if (cpus_accel->get_elapsed_ticks) { ++ return cpus_accel->get_elapsed_ticks(); ++ } ++ return cpu_get_ticks(); ++} ++ ++static void generic_handle_interrupt(CPUState *cpu, int mask) ++{ ++ cpu->interrupt_request |= mask; ++ ++ if (!qemu_cpu_is_self(cpu)) { ++ qemu_cpu_kick(cpu); ++ } ++} ++ ++void cpu_interrupt(CPUState *cpu, int mask) ++{ ++ if (cpus_accel->handle_interrupt) { ++ cpus_accel->handle_interrupt(cpu, mask); ++ } else { ++ generic_handle_interrupt(cpu, mask); ++ } ++} ++ ++/* ++ * True if the vm was previously suspended, and has not been woken or reset. ++ */ ++static int vm_was_suspended; ++ ++void vm_set_suspended(bool suspended) ++{ ++ vm_was_suspended = suspended; ++} ++ ++bool vm_get_suspended(void) ++{ ++ return vm_was_suspended; ++} ++ ++static int do_vm_stop(RunState state, bool send_stop) ++{ ++ int ret = 0; ++ RunState oldstate = runstate_get(); ++ ++ if (runstate_is_live(oldstate)) { ++ vm_was_suspended = (oldstate == RUN_STATE_SUSPENDED); ++ runstate_set(state); ++ cpu_disable_ticks(); ++ if (oldstate == RUN_STATE_RUNNING) { ++ pause_all_vcpus(); ++ } ++ vm_state_notify(0, state); ++ if (send_stop) { ++ qapi_event_send_stop(); ++ } ++ } ++ ++ bdrv_drain_all(); ++ ret = bdrv_flush_all(); ++ trace_vm_stop_flush_all(ret); ++ ++ return ret; ++} ++ ++/* Special vm_stop() variant for terminating the process. Historically clients ++ * did not expect a QMP STOP event and so we need to retain compatibility. ++ */ ++int vm_shutdown(void) ++{ ++ return do_vm_stop(RUN_STATE_SHUTDOWN, false); ++} ++ ++bool cpu_can_run(CPUState *cpu) ++{ ++ if (cpu->stop) { ++ return false; ++ } ++ if (cpu_is_stopped(cpu)) { ++ return false; ++ } ++ return true; ++} ++ ++void cpu_handle_guest_debug(CPUState *cpu) ++{ ++ if (replay_running_debug()) { ++ if (!cpu->singlestep_enabled) { ++ /* ++ * Report about the breakpoint and ++ * make a single step to skip it ++ */ ++ replay_breakpoint(); ++ cpu_single_step(cpu, SSTEP_ENABLE); ++ } else { ++ cpu_single_step(cpu, 0); ++ } ++ } else { ++ gdb_set_stop_cpu(cpu); ++ qemu_system_debug_request(); ++ cpu->stopped = true; ++ } ++} ++ ++#ifdef CONFIG_LINUX ++static void sigbus_reraise(void) ++{ ++ sigset_t set; ++ struct sigaction action; ++ ++ memset(&action, 0, sizeof(action)); ++ action.sa_handler = SIG_DFL; ++ if (!sigaction(SIGBUS, &action, NULL)) { ++ raise(SIGBUS); ++ sigemptyset(&set); ++ sigaddset(&set, SIGBUS); ++ pthread_sigmask(SIG_UNBLOCK, &set, NULL); ++ } ++ perror("Failed to re-raise SIGBUS!"); ++ abort(); ++} ++ ++static void sigbus_handler(int n, siginfo_t *siginfo, void *ctx) ++{ ++ if (siginfo->si_code != BUS_MCEERR_AO && siginfo->si_code != BUS_MCEERR_AR) { ++ sigbus_reraise(); ++ } ++ ++ if (current_cpu) { ++ /* Called asynchronously in VCPU thread. */ ++ if (kvm_on_sigbus_vcpu(current_cpu, siginfo->si_code, siginfo->si_addr)) { ++ sigbus_reraise(); ++ } ++ } else { ++ /* Called synchronously (via signalfd) in main thread. */ ++ if (kvm_on_sigbus(siginfo->si_code, siginfo->si_addr)) { ++ sigbus_reraise(); ++ } ++ } ++} ++ ++static void qemu_init_sigbus(void) ++{ ++ struct sigaction action; ++ ++ /* ++ * ALERT: when modifying this, take care that SIGBUS forwarding in ++ * qemu_prealloc_mem() will continue working as expected. ++ */ ++ memset(&action, 0, sizeof(action)); ++ action.sa_flags = SA_SIGINFO; ++ action.sa_sigaction = sigbus_handler; ++ sigaction(SIGBUS, &action, NULL); ++ ++ prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0); ++} ++#else /* !CONFIG_LINUX */ ++static void qemu_init_sigbus(void) ++{ ++} ++#endif /* !CONFIG_LINUX */ ++ ++static QemuThread io_thread; ++ ++/* cpu creation */ ++static QemuCond qemu_cpu_cond; ++/* system init */ ++static QemuCond qemu_pause_cond; ++ ++void qemu_init_cpu_loop(void) ++{ ++ qemu_init_sigbus(); ++ qemu_cond_init(&qemu_cpu_cond); ++ qemu_cond_init(&qemu_pause_cond); ++ qemu_mutex_init(&bql); ++ ++ qemu_thread_get_self(&io_thread); ++} ++ ++void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) ++{ ++ do_run_on_cpu(cpu, func, data, &bql); ++} ++ ++static void qemu_cpu_stop(CPUState *cpu, bool exit) ++{ ++ g_assert(qemu_cpu_is_self(cpu)); ++ cpu->stop = false; ++ cpu->stopped = true; ++ if (exit) { ++ cpu_exit(cpu); ++ } ++ qemu_cond_broadcast(&qemu_pause_cond); ++} ++ ++void qemu_wait_io_event_common(CPUState *cpu) ++{ ++ qatomic_set_mb(&cpu->thread_kicked, false); ++ if (cpu->stop) { ++ qemu_cpu_stop(cpu, false); ++ } ++ process_queued_cpu_work(cpu); ++} ++ ++void qemu_wait_io_event(CPUState *cpu) ++{ ++ bool slept = false; ++ ++ while (cpu_thread_is_idle(cpu)) { ++ if (!slept) { ++ slept = true; ++ qemu_plugin_vcpu_idle_cb(cpu); ++ } ++ qemu_cond_wait(cpu->halt_cond, &bql); ++ } ++ if (slept) { ++ qemu_plugin_vcpu_resume_cb(cpu); ++ } ++ ++ qemu_wait_io_event_common(cpu); ++} ++ ++void cpus_kick_thread(CPUState *cpu) ++{ ++ if (cpu->thread_kicked) { ++ return; ++ } ++ cpu->thread_kicked = true; ++ ++#ifndef _WIN32 ++ int err = pthread_kill(cpu->thread->thread, SIG_IPI); ++ if (err && err != ESRCH) { ++ fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); ++ exit(1); ++ } ++#else ++ qemu_sem_post(&cpu->sem); ++#endif ++} ++ ++void qemu_cpu_kick(CPUState *cpu) ++{ ++ qemu_cond_broadcast(cpu->halt_cond); ++ if (cpus_accel->kick_vcpu_thread) { ++ cpus_accel->kick_vcpu_thread(cpu); ++ } else { /* default */ ++ cpus_kick_thread(cpu); ++ } ++} ++ ++void qemu_cpu_kick_self(void) ++{ ++ assert(current_cpu); ++ cpus_kick_thread(current_cpu); ++} ++ ++bool qemu_cpu_is_self(CPUState *cpu) ++{ ++ return qemu_thread_is_self(cpu->thread); ++} ++ ++bool qemu_in_vcpu_thread(void) ++{ ++ return current_cpu && qemu_cpu_is_self(current_cpu); ++} ++ ++QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked) ++ ++bool bql_locked(void) ++{ ++ return get_bql_locked(); ++} ++ ++bool qemu_in_main_thread(void) ++{ ++ return bql_locked(); ++} ++ ++/* ++ * The BQL is taken from so many places that it is worth profiling the ++ * callers directly, instead of funneling them all through a single function. ++ */ ++void bql_lock_impl(const char *file, int line) ++{ ++ QemuMutexLockFunc bql_lock_fn = qatomic_read(&bql_mutex_lock_func); ++ ++ g_assert(!bql_locked()); ++ bql_lock_fn(&bql, file, line); ++ set_bql_locked(true); ++} ++ ++void bql_unlock(void) ++{ ++ g_assert(bql_locked()); ++ set_bql_locked(false); ++ qemu_mutex_unlock(&bql); ++} ++ ++void qemu_cond_wait_bql(QemuCond *cond) ++{ ++ qemu_cond_wait(cond, &bql); ++} ++ ++void qemu_cond_timedwait_bql(QemuCond *cond, int ms) ++{ ++ qemu_cond_timedwait(cond, &bql, ms); ++} ++ ++/* signal CPU creation */ ++void cpu_thread_signal_created(CPUState *cpu) ++{ ++ cpu->created = true; ++ qemu_cond_signal(&qemu_cpu_cond); ++} ++ ++/* signal CPU destruction */ ++void cpu_thread_signal_destroyed(CPUState *cpu) ++{ ++ cpu->created = false; ++ qemu_cond_signal(&qemu_cpu_cond); ++} ++ ++void cpu_pause(CPUState *cpu) ++{ ++ if (qemu_cpu_is_self(cpu)) { ++ qemu_cpu_stop(cpu, true); ++ } else { ++ cpu->stop = true; ++ qemu_cpu_kick(cpu); ++ } ++} ++ ++void cpu_resume(CPUState *cpu) ++{ ++ cpu->stop = false; ++ cpu->stopped = false; ++ qemu_cpu_kick(cpu); ++} ++ ++static bool all_vcpus_paused(void) ++{ ++ CPUState *cpu; ++ ++ CPU_FOREACH(cpu) { ++ if (!cpu->stopped) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++void pause_all_vcpus(void) ++{ ++ CPUState *cpu; ++ ++ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false); ++ CPU_FOREACH(cpu) { ++ cpu_pause(cpu); ++ } ++ ++ /* We need to drop the replay_lock so any vCPU threads woken up ++ * can finish their replay tasks ++ */ ++ replay_mutex_unlock(); ++ ++ while (!all_vcpus_paused()) { ++ qemu_cond_wait(&qemu_pause_cond, &bql); ++ CPU_FOREACH(cpu) { ++ qemu_cpu_kick(cpu); ++ } ++ } ++ ++ bql_unlock(); ++ replay_mutex_lock(); ++ bql_lock(); ++} ++ ++void resume_all_vcpus(void) ++{ ++ CPUState *cpu; ++ ++ if (!runstate_is_running()) { ++ return; ++ } ++ ++ qemu_clock_enable(QEMU_CLOCK_VIRTUAL, true); ++ CPU_FOREACH(cpu) { ++ cpu_resume(cpu); ++ } ++} ++ ++void cpu_remove_sync(CPUState *cpu) ++{ ++ cpu->stop = true; ++ cpu->unplug = true; ++ qemu_cpu_kick(cpu); ++ bql_unlock(); ++ qemu_thread_join(cpu->thread); ++ bql_lock(); ++} ++ ++void cpus_register_accel(const AccelOpsClass *ops) ++{ ++ assert(ops != NULL); ++ assert(ops->create_vcpu_thread != NULL); /* mandatory */ ++ cpus_accel = ops; ++} ++ ++const AccelOpsClass *cpus_get_accel(void) ++{ ++ /* broken if we call this early */ ++ assert(cpus_accel); ++ return cpus_accel; ++} ++ ++void qemu_init_vcpu(CPUState *cpu) ++{ ++ MachineState *ms = MACHINE(qdev_get_machine()); ++ ++ cpu->nr_cores = machine_topo_get_cores_per_socket(ms); ++ cpu->nr_threads = ms->smp.threads; ++ cpu->stopped = true; ++ cpu->random_seed = qemu_guest_random_seed_thread_part1(); ++ ++ if (!cpu->as) { ++ /* If the target cpu hasn't set up any address spaces itself, ++ * give it the default one. ++ */ ++ cpu->num_ases = 1; ++ cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory); ++ } ++ ++ /* accelerators all implement the AccelOpsClass */ ++ g_assert(cpus_accel != NULL && cpus_accel->create_vcpu_thread != NULL); ++ cpus_accel->create_vcpu_thread(cpu); ++ ++ while (!cpu->created) { ++ qemu_cond_wait(&qemu_cpu_cond, &bql); ++ } ++} ++ ++void cpu_stop_current(void) ++{ ++ if (current_cpu) { ++ current_cpu->stop = true; ++ cpu_exit(current_cpu); ++ } ++} ++ ++int vm_stop(RunState state) ++{ ++ if (qemu_in_vcpu_thread()) { ++ qemu_system_vmstop_request_prepare(); ++ qemu_system_vmstop_request(state); ++ /* ++ * FIXME: should not return to device code in case ++ * vm_stop() has been requested. ++ */ ++ cpu_stop_current(); ++ return 0; ++ } ++ ++ return do_vm_stop(state, true); ++} ++ ++/** ++ * Prepare for (re)starting the VM. ++ * Returns 0 if the vCPUs should be restarted, -1 on an error condition, ++ * and 1 otherwise. ++ */ ++int vm_prepare_start(bool step_pending) ++{ ++ int ret = vm_was_suspended ? 1 : 0; ++ RunState state = vm_was_suspended ? RUN_STATE_SUSPENDED : RUN_STATE_RUNNING; ++ RunState requested; ++ ++ qemu_vmstop_requested(&requested); ++ if (runstate_is_running() && requested == RUN_STATE__MAX) { ++ return -1; ++ } ++ ++ /* Ensure that a STOP/RESUME pair of events is emitted if a ++ * vmstop request was pending. The BLOCK_IO_ERROR event, for ++ * example, according to documentation is always followed by ++ * the STOP event. ++ */ ++ if (runstate_is_running()) { ++ qapi_event_send_stop(); ++ qapi_event_send_resume(); ++ return -1; ++ } ++ ++ /* ++ * WHPX accelerator needs to know whether we are going to step ++ * any CPUs, before starting the first one. ++ */ ++ if (cpus_accel->synchronize_pre_resume) { ++ cpus_accel->synchronize_pre_resume(step_pending); ++ } ++ ++ /* We are sending this now, but the CPUs will be resumed shortly later */ ++ qapi_event_send_resume(); ++ ++ cpu_enable_ticks(); ++ runstate_set(state); ++ vm_state_notify(1, state); ++ vm_was_suspended = false; ++ return ret; ++} ++ ++void vm_start(void) ++{ ++ if (!vm_prepare_start(false)) { ++ resume_all_vcpus(); ++ } ++} ++ ++void vm_resume(RunState state) ++{ ++ if (runstate_is_live(state)) { ++ vm_start(); ++ } else { ++ runstate_set(state); ++ } ++} ++ ++/* does a state transition even if the VM is already stopped, ++ current state is forgotten forever */ ++int vm_stop_force_state(RunState state) ++{ ++ if (runstate_is_live(runstate_get())) { ++ return vm_stop(state); ++ } else { ++ int ret; ++ runstate_set(state); ++ ++ bdrv_drain_all(); ++ /* Make sure to return an error if the flush in a previous vm_stop() ++ * failed. */ ++ ret = bdrv_flush_all(); ++ trace_vm_stop_flush_all(ret); ++ return ret; ++ } ++} ++ ++void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, ++ bool has_cpu, int64_t cpu_index, Error **errp) ++{ ++ FILE *f; ++ uint64_t l; ++ CPUState *cpu; ++ uint8_t buf[1024]; ++ uint64_t orig_addr = addr, orig_size = size; ++ ++ if (!has_cpu) { ++ cpu_index = 0; ++ } ++ ++ cpu = qemu_get_cpu(cpu_index); ++ if (cpu == NULL) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", ++ "a CPU number"); ++ return; ++ } ++ ++ f = fopen(filename, "wb"); ++ if (!f) { ++ error_setg_file_open(errp, errno, filename); ++ return; ++ } ++ ++ while (size != 0) { ++ l = sizeof(buf); ++ if (l > size) ++ l = size; ++ if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { ++ error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 ++ " specified", orig_addr, orig_size); ++ goto exit; ++ } ++ if (fwrite(buf, 1, l, f) != l) { ++ error_setg(errp, "writing memory to '%s' failed", ++ filename); ++ goto exit; ++ } ++ addr += l; ++ size -= l; ++ } ++ ++exit: ++ fclose(f); ++} ++ ++void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, ++ Error **errp) ++{ ++ FILE *f; ++ uint64_t l; ++ uint8_t buf[1024]; ++ ++ f = fopen(filename, "wb"); ++ if (!f) { ++ error_setg_file_open(errp, errno, filename); ++ return; ++ } ++ ++ while (size != 0) { ++ l = sizeof(buf); ++ if (l > size) ++ l = size; ++ cpu_physical_memory_read(addr, buf, l); ++ if (fwrite(buf, 1, l, f) != l) { ++ error_setg(errp, "writing memory to '%s' failed", ++ filename); ++ goto exit; ++ } ++ addr += l; ++ size -= l; ++ } ++ ++exit: ++ fclose(f); ++} ++ ++void qmp_inject_nmi(Error **errp) ++{ ++ nmi_monitor_handle(monitor_get_cpu_index(monitor_cur()), errp); ++} ++ +diff --git a/qcow2/lib/util/aio-posix.c b/qcow2/lib/util/aio-posix.c +new file mode 100644 +index 00000000..266c9dd3 +--- /dev/null ++++ b/qcow2/lib/util/aio-posix.c +@@ -0,0 +1,789 @@ ++/* ++ * QEMU aio implementation ++ * ++ * Copyright IBM, Corp. 2008 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/block.h" ++#include "block/thread-pool.h" ++#include "qemu/main-loop.h" ++#include "qemu/rcu.h" ++#include "qemu/rcu_queue.h" ++#include "qemu/sockets.h" ++#include "qemu/cutils.h" ++#include "trace.h" ++#include "aio-posix.h" ++ ++/* Stop userspace polling on a handler if it isn't active for some time */ ++#define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND) ++ ++bool aio_poll_disabled(AioContext *ctx) ++{ ++ return qatomic_read(&ctx->poll_disable_cnt); ++} ++ ++void aio_add_ready_handler(AioHandlerList *ready_list, ++ AioHandler *node, ++ int revents) ++{ ++ QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */ ++ node->pfd.revents = revents; ++ QLIST_INSERT_HEAD(ready_list, node, node_ready); ++} ++ ++static void aio_add_poll_ready_handler(AioHandlerList *ready_list, ++ AioHandler *node) ++{ ++ QLIST_SAFE_REMOVE(node, node_ready); /* remove from nested parent's list */ ++ node->poll_ready = true; ++ QLIST_INSERT_HEAD(ready_list, node, node_ready); ++} ++ ++static AioHandler *find_aio_handler(AioContext *ctx, int fd) ++{ ++ AioHandler *node; ++ ++ QLIST_FOREACH(node, &ctx->aio_handlers, node) { ++ if (node->pfd.fd == fd) { ++ if (!QLIST_IS_INSERTED(node, node_deleted)) { ++ return node; ++ } ++ } ++ } ++ ++ return NULL; ++} ++ ++static bool aio_remove_fd_handler(AioContext *ctx, AioHandler *node) ++{ ++ /* If the GSource is in the process of being destroyed then ++ * g_source_remove_poll() causes an assertion failure. Skip ++ * removal in that case, because glib cleans up its state during ++ * destruction anyway. ++ */ ++ if (!g_source_is_destroyed(&ctx->source)) { ++ g_source_remove_poll(&ctx->source, &node->pfd); ++ } ++ ++ node->pfd.revents = 0; ++ node->poll_ready = false; ++ ++ /* If the fd monitor has already marked it deleted, leave it alone */ ++ if (QLIST_IS_INSERTED(node, node_deleted)) { ++ return false; ++ } ++ ++ /* If a read is in progress, just mark the node as deleted */ ++ if (qemu_lockcnt_count(&ctx->list_lock)) { ++ QLIST_INSERT_HEAD_RCU(&ctx->deleted_aio_handlers, node, node_deleted); ++ return false; ++ } ++ /* Otherwise, delete it for real. We can't just mark it as ++ * deleted because deleted nodes are only cleaned up while ++ * no one is walking the handlers list. ++ */ ++ QLIST_SAFE_REMOVE(node, node_poll); ++ QLIST_REMOVE(node, node); ++ return true; ++} ++ ++void aio_set_fd_handler(AioContext *ctx, ++ int fd, ++ IOHandler *io_read, ++ IOHandler *io_write, ++ AioPollFn *io_poll, ++ IOHandler *io_poll_ready, ++ void *opaque) ++{ ++ AioHandler *node; ++ AioHandler *new_node = NULL; ++ bool is_new = false; ++ bool deleted = false; ++ int poll_disable_change; ++ ++ if (io_poll && !io_poll_ready) { ++ io_poll = NULL; /* polling only makes sense if there is a handler */ ++ } ++ ++ qemu_lockcnt_lock(&ctx->list_lock); ++ ++ node = find_aio_handler(ctx, fd); ++ ++ /* Are we deleting the fd handler? */ ++ if (!io_read && !io_write && !io_poll) { ++ if (node == NULL) { ++ qemu_lockcnt_unlock(&ctx->list_lock); ++ return; ++ } ++ /* Clean events in order to unregister fd from the ctx epoll. */ ++ node->pfd.events = 0; ++ ++ poll_disable_change = -!node->io_poll; ++ } else { ++ poll_disable_change = !io_poll - (node && !node->io_poll); ++ if (node == NULL) { ++ is_new = true; ++ } ++ /* Alloc and insert if it's not already there */ ++ new_node = g_new0(AioHandler, 1); ++ ++ /* Update handler with latest information */ ++ new_node->io_read = io_read; ++ new_node->io_write = io_write; ++ new_node->io_poll = io_poll; ++ new_node->io_poll_ready = io_poll_ready; ++ new_node->opaque = opaque; ++ ++ if (is_new) { ++ new_node->pfd.fd = fd; ++ } else { ++ new_node->pfd = node->pfd; ++ } ++ g_source_add_poll(&ctx->source, &new_node->pfd); ++ ++ new_node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0); ++ new_node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0); ++ ++ QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, new_node, node); ++ } ++ ++ /* No need to order poll_disable_cnt writes against other updates; ++ * the counter is only used to avoid wasting time and latency on ++ * iterated polling when the system call will be ultimately necessary. ++ * Changing handlers is a rare event, and a little wasted polling until ++ * the aio_notify below is not an issue. ++ */ ++ qatomic_set(&ctx->poll_disable_cnt, ++ qatomic_read(&ctx->poll_disable_cnt) + poll_disable_change); ++ ++ ctx->fdmon_ops->update(ctx, node, new_node); ++ if (node) { ++ deleted = aio_remove_fd_handler(ctx, node); ++ } ++ qemu_lockcnt_unlock(&ctx->list_lock); ++ aio_notify(ctx); ++ ++ if (deleted) { ++ g_free(node); ++ } ++} ++ ++static void aio_set_fd_poll(AioContext *ctx, int fd, ++ IOHandler *io_poll_begin, ++ IOHandler *io_poll_end) ++{ ++ AioHandler *node = find_aio_handler(ctx, fd); ++ ++ if (!node) { ++ return; ++ } ++ ++ node->io_poll_begin = io_poll_begin; ++ node->io_poll_end = io_poll_end; ++} ++ ++void aio_set_event_notifier(AioContext *ctx, ++ EventNotifier *notifier, ++ EventNotifierHandler *io_read, ++ AioPollFn *io_poll, ++ EventNotifierHandler *io_poll_ready) ++{ ++ aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), ++ (IOHandler *)io_read, NULL, io_poll, ++ (IOHandler *)io_poll_ready, notifier); ++} ++ ++void aio_set_event_notifier_poll(AioContext *ctx, ++ EventNotifier *notifier, ++ EventNotifierHandler *io_poll_begin, ++ EventNotifierHandler *io_poll_end) ++{ ++ aio_set_fd_poll(ctx, event_notifier_get_fd(notifier), ++ (IOHandler *)io_poll_begin, ++ (IOHandler *)io_poll_end); ++} ++ ++static bool poll_set_started(AioContext *ctx, AioHandlerList *ready_list, ++ bool started) ++{ ++ AioHandler *node; ++ bool progress = false; ++ ++ if (started == ctx->poll_started) { ++ return false; ++ } ++ ++ ctx->poll_started = started; ++ ++ qemu_lockcnt_inc(&ctx->list_lock); ++ QLIST_FOREACH(node, &ctx->poll_aio_handlers, node_poll) { ++ IOHandler *fn; ++ ++ if (QLIST_IS_INSERTED(node, node_deleted)) { ++ continue; ++ } ++ ++ if (started) { ++ fn = node->io_poll_begin; ++ } else { ++ fn = node->io_poll_end; ++ } ++ ++ if (fn) { ++ fn(node->opaque); ++ } ++ ++ /* Poll one last time in case ->io_poll_end() raced with the event */ ++ if (!started && node->io_poll(node->opaque)) { ++ aio_add_poll_ready_handler(ready_list, node); ++ progress = true; ++ } ++ } ++ qemu_lockcnt_dec(&ctx->list_lock); ++ ++ return progress; ++} ++ ++ ++bool aio_prepare(AioContext *ctx) ++{ ++ AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list); ++ ++ /* Poll mode cannot be used with glib's event loop, disable it. */ ++ poll_set_started(ctx, &ready_list, false); ++ /* TODO what to do with this list? */ ++ ++ return false; ++} ++ ++bool aio_pending(AioContext *ctx) ++{ ++ AioHandler *node; ++ bool result = false; ++ ++ /* ++ * We have to walk very carefully in case aio_set_fd_handler is ++ * called while we're walking. ++ */ ++ qemu_lockcnt_inc(&ctx->list_lock); ++ ++ QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { ++ int revents; ++ ++ /* TODO should this check poll ready? */ ++ revents = node->pfd.revents & node->pfd.events; ++ if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR) && node->io_read) { ++ result = true; ++ break; ++ } ++ if (revents & (G_IO_OUT | G_IO_ERR) && node->io_write) { ++ result = true; ++ break; ++ } ++ } ++ qemu_lockcnt_dec(&ctx->list_lock); ++ ++ return result; ++} ++ ++static void aio_free_deleted_handlers(AioContext *ctx) ++{ ++ AioHandler *node; ++ ++ if (QLIST_EMPTY_RCU(&ctx->deleted_aio_handlers)) { ++ return; ++ } ++ if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) { ++ return; /* we are nested, let the parent do the freeing */ ++ } ++ ++ while ((node = QLIST_FIRST_RCU(&ctx->deleted_aio_handlers))) { ++ QLIST_REMOVE(node, node); ++ QLIST_REMOVE(node, node_deleted); ++ QLIST_SAFE_REMOVE(node, node_poll); ++ g_free(node); ++ } ++ ++ qemu_lockcnt_inc_and_unlock(&ctx->list_lock); ++} ++ ++static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) ++{ ++ bool progress = false; ++ bool poll_ready; ++ int revents; ++ ++ revents = node->pfd.revents & node->pfd.events; ++ node->pfd.revents = 0; ++ ++ poll_ready = node->poll_ready; ++ node->poll_ready = false; ++ ++ /* ++ * Start polling AioHandlers when they become ready because activity is ++ * likely to continue. Note that starvation is theoretically possible when ++ * fdmon_supports_polling(), but only until the fd fires for the first ++ * time. ++ */ ++ if (!QLIST_IS_INSERTED(node, node_deleted) && ++ !QLIST_IS_INSERTED(node, node_poll) && ++ node->io_poll) { ++ trace_poll_add(ctx, node, node->pfd.fd, revents); ++ if (ctx->poll_started && node->io_poll_begin) { ++ node->io_poll_begin(node->opaque); ++ } ++ QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); ++ } ++ if (!QLIST_IS_INSERTED(node, node_deleted) && ++ poll_ready && revents == 0 && node->io_poll_ready) { ++ /* ++ * Remove temporarily to avoid infinite loops when ->io_poll_ready() ++ * calls aio_poll() before clearing the condition that made the poll ++ * handler become ready. ++ */ ++ QLIST_SAFE_REMOVE(node, node_poll); ++ ++ node->io_poll_ready(node->opaque); ++ ++ if (!QLIST_IS_INSERTED(node, node_poll)) { ++ QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll); ++ } ++ ++ /* ++ * Return early since revents was zero. aio_notify() does not count as ++ * progress. ++ */ ++ return node->opaque != &ctx->notifier; ++ } ++ ++ if (!QLIST_IS_INSERTED(node, node_deleted) && ++ (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) && ++ node->io_read) { ++ node->io_read(node->opaque); ++ ++ /* aio_notify() does not count as progress */ ++ if (node->opaque != &ctx->notifier) { ++ progress = true; ++ } ++ } ++ if (!QLIST_IS_INSERTED(node, node_deleted) && ++ (revents & (G_IO_OUT | G_IO_ERR)) && ++ node->io_write) { ++ node->io_write(node->opaque); ++ progress = true; ++ } ++ ++ return progress; ++} ++ ++/* ++ * If we have a list of ready handlers then this is more efficient than ++ * scanning all handlers with aio_dispatch_handlers(). ++ */ ++static bool aio_dispatch_ready_handlers(AioContext *ctx, ++ AioHandlerList *ready_list) ++{ ++ bool progress = false; ++ AioHandler *node; ++ ++ while ((node = QLIST_FIRST(ready_list))) { ++ QLIST_REMOVE(node, node_ready); ++ progress = aio_dispatch_handler(ctx, node) || progress; ++ } ++ ++ return progress; ++} ++ ++/* Slower than aio_dispatch_ready_handlers() but only used via glib */ ++static bool aio_dispatch_handlers(AioContext *ctx) ++{ ++ AioHandler *node, *tmp; ++ bool progress = false; ++ ++ QLIST_FOREACH_SAFE_RCU(node, &ctx->aio_handlers, node, tmp) { ++ progress = aio_dispatch_handler(ctx, node) || progress; ++ } ++ ++ return progress; ++} ++ ++void aio_dispatch(AioContext *ctx) ++{ ++ qemu_lockcnt_inc(&ctx->list_lock); ++ aio_bh_poll(ctx); ++ aio_dispatch_handlers(ctx); ++ aio_free_deleted_handlers(ctx); ++ qemu_lockcnt_dec(&ctx->list_lock); ++ ++ timerlistgroup_run_timers(&ctx->tlg); ++} ++ ++static bool run_poll_handlers_once(AioContext *ctx, ++ AioHandlerList *ready_list, ++ int64_t now, ++ int64_t *timeout) ++{ ++ bool progress = false; ++ AioHandler *node; ++ AioHandler *tmp; ++ ++ QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { ++ if (node->io_poll(node->opaque)) { ++ aio_add_poll_ready_handler(ready_list, node); ++ ++ node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; ++ ++ /* ++ * Polling was successful, exit try_poll_mode immediately ++ * to adjust the next polling time. ++ */ ++ *timeout = 0; ++ if (node->opaque != &ctx->notifier) { ++ progress = true; ++ } ++ } ++ ++ /* Caller handles freeing deleted nodes. Don't do it here. */ ++ } ++ ++ return progress; ++} ++ ++static bool fdmon_supports_polling(AioContext *ctx) ++{ ++ return ctx->fdmon_ops->need_wait != aio_poll_disabled; ++} ++ ++static bool remove_idle_poll_handlers(AioContext *ctx, ++ AioHandlerList *ready_list, ++ int64_t now) ++{ ++ AioHandler *node; ++ AioHandler *tmp; ++ bool progress = false; ++ ++ /* ++ * File descriptor monitoring implementations without userspace polling ++ * support suffer from starvation when a subset of handlers is polled ++ * because fds will not be processed in a timely fashion. Don't remove ++ * idle poll handlers. ++ */ ++ if (!fdmon_supports_polling(ctx)) { ++ return false; ++ } ++ ++ QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) { ++ if (node->poll_idle_timeout == 0LL) { ++ node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; ++ } else if (now >= node->poll_idle_timeout) { ++ trace_poll_remove(ctx, node, node->pfd.fd); ++ node->poll_idle_timeout = 0LL; ++ QLIST_SAFE_REMOVE(node, node_poll); ++ if (ctx->poll_started && node->io_poll_end) { ++ node->io_poll_end(node->opaque); ++ ++ /* ++ * Final poll in case ->io_poll_end() races with an event. ++ * Nevermind about re-adding the handler in the rare case where ++ * this causes progress. ++ */ ++ if (node->io_poll(node->opaque)) { ++ aio_add_poll_ready_handler(ready_list, node); ++ progress = true; ++ } ++ } ++ } ++ } ++ ++ return progress; ++} ++ ++/* run_poll_handlers: ++ * @ctx: the AioContext ++ * @ready_list: the list to place ready handlers on ++ * @max_ns: maximum time to poll for, in nanoseconds ++ * ++ * Polls for a given time. ++ * ++ * Note that the caller must have incremented ctx->list_lock. ++ * ++ * Returns: true if progress was made, false otherwise ++ */ ++static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list, ++ int64_t max_ns, int64_t *timeout) ++{ ++ bool progress; ++ int64_t start_time, elapsed_time; ++ ++ assert(qemu_lockcnt_count(&ctx->list_lock) > 0); ++ ++ trace_run_poll_handlers_begin(ctx, max_ns, *timeout); ++ ++ /* ++ * Optimization: ->io_poll() handlers often contain RCU read critical ++ * sections and we therefore see many rcu_read_lock() -> rcu_read_unlock() ++ * -> rcu_read_lock() -> ... sequences with expensive memory ++ * synchronization primitives. Make the entire polling loop an RCU ++ * critical section because nested rcu_read_lock()/rcu_read_unlock() calls ++ * are cheap. ++ */ ++ RCU_READ_LOCK_GUARD(); ++ ++ start_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ do { ++ progress = run_poll_handlers_once(ctx, ready_list, ++ start_time, timeout); ++ elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time; ++ max_ns = qemu_soonest_timeout(*timeout, max_ns); ++ assert(!(max_ns && progress)); ++ } while (elapsed_time < max_ns && !ctx->fdmon_ops->need_wait(ctx)); ++ ++ if (remove_idle_poll_handlers(ctx, ready_list, ++ start_time + elapsed_time)) { ++ *timeout = 0; ++ progress = true; ++ } ++ ++ /* If time has passed with no successful polling, adjust *timeout to ++ * keep the same ending time. ++ */ ++ if (*timeout != -1) { ++ *timeout -= MIN(*timeout, elapsed_time); ++ } ++ ++ trace_run_poll_handlers_end(ctx, progress, *timeout); ++ return progress; ++} ++ ++/* try_poll_mode: ++ * @ctx: the AioContext ++ * @ready_list: list to add handlers that need to be run ++ * @timeout: timeout for blocking wait, computed by the caller and updated if ++ * polling succeeds. ++ * ++ * Note that the caller must have incremented ctx->list_lock. ++ * ++ * Returns: true if progress was made, false otherwise ++ */ ++static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list, ++ int64_t *timeout) ++{ ++ int64_t max_ns; ++ ++ if (QLIST_EMPTY_RCU(&ctx->poll_aio_handlers)) { ++ return false; ++ } ++ ++ max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns); ++ if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) { ++ /* ++ * Enable poll mode. It pairs with the poll_set_started() in ++ * aio_poll() which disables poll mode. ++ */ ++ poll_set_started(ctx, ready_list, true); ++ ++ if (run_poll_handlers(ctx, ready_list, max_ns, timeout)) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++bool aio_poll(AioContext *ctx, bool blocking) ++{ ++ AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list); ++ bool progress; ++ bool use_notify_me; ++ int64_t timeout; ++ int64_t start = 0; ++ ++ /* ++ * There cannot be two concurrent aio_poll calls for the same AioContext (or ++ * an aio_poll concurrent with a GSource prepare/check/dispatch callback). ++ * We rely on this below to avoid slow locked accesses to ctx->notify_me. ++ * ++ * aio_poll() may only be called in the AioContext's thread. iohandler_ctx ++ * is special in that it runs in the main thread, but that thread's context ++ * is qemu_aio_context. ++ */ ++ assert(in_aio_context_home_thread(ctx == iohandler_get_aio_context() ? ++ qemu_get_aio_context() : ctx)); ++ ++ qemu_lockcnt_inc(&ctx->list_lock); ++ ++ if (ctx->poll_max_ns) { ++ start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); ++ } ++ ++ timeout = blocking ? aio_compute_timeout(ctx) : 0; ++ progress = try_poll_mode(ctx, &ready_list, &timeout); ++ assert(!(timeout && progress)); ++ ++ /* ++ * aio_notify can avoid the expensive event_notifier_set if ++ * everything (file descriptors, bottom halves, timers) will ++ * be re-evaluated before the next blocking poll(). This is ++ * already true when aio_poll is called with blocking == false; ++ * if blocking == true, it is only true after poll() returns, ++ * so disable the optimization now. ++ */ ++ use_notify_me = timeout != 0; ++ if (use_notify_me) { ++ qatomic_set(&ctx->notify_me, qatomic_read(&ctx->notify_me) + 2); ++ /* ++ * Write ctx->notify_me before reading ctx->notified. Pairs with ++ * smp_mb in aio_notify(). ++ */ ++ smp_mb(); ++ ++ /* Don't block if aio_notify() was called */ ++ if (qatomic_read(&ctx->notified)) { ++ timeout = 0; ++ } ++ } ++ ++ /* If polling is allowed, non-blocking aio_poll does not need the ++ * system call---a single round of run_poll_handlers_once suffices. ++ */ ++ if (timeout || ctx->fdmon_ops->need_wait(ctx)) { ++ /* ++ * Disable poll mode. poll mode should be disabled before the call ++ * of ctx->fdmon_ops->wait() so that guest's notification can wake ++ * up IO threads when some work becomes pending. It is essential to ++ * avoid hangs or unnecessary latency. ++ */ ++ if (poll_set_started(ctx, &ready_list, false)) { ++ timeout = 0; ++ progress = true; ++ } ++ ++ ctx->fdmon_ops->wait(ctx, &ready_list, timeout); ++ } ++ ++ if (use_notify_me) { ++ /* Finish the poll before clearing the flag. */ ++ qatomic_store_release(&ctx->notify_me, ++ qatomic_read(&ctx->notify_me) - 2); ++ } ++ ++ aio_notify_accept(ctx); ++ ++ /* Adjust polling time */ ++ if (ctx->poll_max_ns) { ++ int64_t block_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start; ++ ++ if (block_ns <= ctx->poll_ns) { ++ /* This is the sweet spot, no adjustment needed */ ++ } else if (block_ns > ctx->poll_max_ns) { ++ /* We'd have to poll for too long, poll less */ ++ int64_t old = ctx->poll_ns; ++ ++ if (ctx->poll_shrink) { ++ ctx->poll_ns /= ctx->poll_shrink; ++ } else { ++ ctx->poll_ns = 0; ++ } ++ ++ trace_poll_shrink(ctx, old, ctx->poll_ns); ++ } else if (ctx->poll_ns < ctx->poll_max_ns && ++ block_ns < ctx->poll_max_ns) { ++ /* There is room to grow, poll longer */ ++ int64_t old = ctx->poll_ns; ++ int64_t grow = ctx->poll_grow; ++ ++ if (grow == 0) { ++ grow = 2; ++ } ++ ++ if (ctx->poll_ns) { ++ ctx->poll_ns *= grow; ++ } else { ++ ctx->poll_ns = 4000; /* start polling at 4 microseconds */ ++ } ++ ++ if (ctx->poll_ns > ctx->poll_max_ns) { ++ ctx->poll_ns = ctx->poll_max_ns; ++ } ++ ++ trace_poll_grow(ctx, old, ctx->poll_ns); ++ } ++ } ++ ++ progress |= aio_bh_poll(ctx); ++ progress |= aio_dispatch_ready_handlers(ctx, &ready_list); ++ ++ aio_free_deleted_handlers(ctx); ++ ++ qemu_lockcnt_dec(&ctx->list_lock); ++ ++ progress |= timerlistgroup_run_timers(&ctx->tlg); ++ ++ return progress; ++} ++ ++void aio_context_setup(AioContext *ctx) ++{ ++ ctx->fdmon_ops = &fdmon_poll_ops; ++ ctx->epollfd = -1; ++ ++ /* Use the fastest fd monitoring implementation if available */ ++ if (fdmon_io_uring_setup(ctx)) { ++ return; ++ } ++ ++ fdmon_epoll_setup(ctx); ++} ++ ++void aio_context_destroy(AioContext *ctx) ++{ ++ fdmon_io_uring_destroy(ctx); ++ fdmon_epoll_disable(ctx); ++ aio_free_deleted_handlers(ctx); ++} ++ ++void aio_context_use_g_source(AioContext *ctx) ++{ ++ /* ++ * Disable io_uring when the glib main loop is used because it doesn't ++ * support mixed glib/aio_poll() usage. It relies on aio_poll() being ++ * called regularly so that changes to the monitored file descriptors are ++ * submitted, otherwise a list of pending fd handlers builds up. ++ */ ++ fdmon_io_uring_destroy(ctx); ++ aio_free_deleted_handlers(ctx); ++} ++ ++void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, ++ int64_t grow, int64_t shrink, Error **errp) ++{ ++ /* No thread synchronization here, it doesn't matter if an incorrect value ++ * is used once. ++ */ ++ ctx->poll_max_ns = max_ns; ++ ctx->poll_ns = 0; ++ ctx->poll_grow = grow; ++ ctx->poll_shrink = shrink; ++ ++ aio_notify(ctx); ++} ++ ++void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch) ++{ ++ /* ++ * No thread synchronization here, it doesn't matter if an incorrect value ++ * is used once. ++ */ ++ ctx->aio_max_batch = max_batch; ++ ++ aio_notify(ctx); ++} +diff --git a/qcow2/lib/util/aio-posix.h b/qcow2/lib/util/aio-posix.h +new file mode 100644 +index 00000000..4264c518 +--- /dev/null ++++ b/qcow2/lib/util/aio-posix.h +@@ -0,0 +1,82 @@ ++/* ++ * AioContext POSIX event loop implementation internal APIs ++ * ++ * Copyright IBM, Corp. 2008 ++ * Copyright Red Hat, Inc. 2020 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#ifndef AIO_POSIX_H ++#define AIO_POSIX_H ++ ++#include "block/aio.h" ++ ++struct AioHandler { ++ GPollFD pfd; ++ IOHandler *io_read; ++ IOHandler *io_write; ++ AioPollFn *io_poll; ++ IOHandler *io_poll_ready; ++ IOHandler *io_poll_begin; ++ IOHandler *io_poll_end; ++ void *opaque; ++ QLIST_ENTRY(AioHandler) node; ++ QLIST_ENTRY(AioHandler) node_ready; /* only used during aio_poll() */ ++ QLIST_ENTRY(AioHandler) node_deleted; ++ QLIST_ENTRY(AioHandler) node_poll; ++#ifdef CONFIG_LINUX_IO_URING ++ QSLIST_ENTRY(AioHandler) node_submitted; ++ unsigned flags; /* see fdmon-io_uring.c */ ++#endif ++ int64_t poll_idle_timeout; /* when to stop userspace polling */ ++ bool poll_ready; /* has polling detected an event? */ ++}; ++ ++/* Add a handler to a ready list */ ++void aio_add_ready_handler(AioHandlerList *ready_list, AioHandler *node, ++ int revents); ++ ++extern const FDMonOps fdmon_poll_ops; ++ ++#ifdef CONFIG_EPOLL_CREATE1 ++bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd); ++void fdmon_epoll_setup(AioContext *ctx); ++void fdmon_epoll_disable(AioContext *ctx); ++#else ++static inline bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) ++{ ++ return false; ++} ++ ++static inline void fdmon_epoll_setup(AioContext *ctx) ++{ ++} ++ ++static inline void fdmon_epoll_disable(AioContext *ctx) ++{ ++} ++#endif /* !CONFIG_EPOLL_CREATE1 */ ++ ++#ifdef CONFIG_LINUX_IO_URING ++bool fdmon_io_uring_setup(AioContext *ctx); ++void fdmon_io_uring_destroy(AioContext *ctx); ++#else ++static inline bool fdmon_io_uring_setup(AioContext *ctx) ++{ ++ return false; ++} ++ ++static inline void fdmon_io_uring_destroy(AioContext *ctx) ++{ ++} ++#endif /* !CONFIG_LINUX_IO_URING */ ++ ++#endif /* AIO_POSIX_H */ +diff --git a/qcow2/lib/util/aio-wait.c b/qcow2/lib/util/aio-wait.c +new file mode 100644 +index 00000000..b5336cf5 +--- /dev/null ++++ b/qcow2/lib/util/aio-wait.c +@@ -0,0 +1,86 @@ ++/* ++ * AioContext wait support ++ * ++ * Copyright (C) 2018 Red Hat, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/main-loop.h" ++#include "block/aio-wait.h" ++ ++AioWait global_aio_wait; ++ ++static void dummy_bh_cb(void *opaque) ++{ ++ /* The point is to make AIO_WAIT_WHILE()'s aio_poll() return */ ++} ++ ++void aio_wait_kick(void) ++{ ++ /* ++ * Paired with smp_mb in AIO_WAIT_WHILE. Here we have: ++ * write(condition); ++ * aio_wait_kick() { ++ * smp_mb(); ++ * read(num_waiters); ++ * } ++ * ++ * And in AIO_WAIT_WHILE: ++ * write(num_waiters); ++ * smp_mb(); ++ * read(condition); ++ */ ++ smp_mb(); ++ ++ if (qatomic_read(&global_aio_wait.num_waiters)) { ++ aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); ++ } ++} ++ ++typedef struct { ++ bool done; ++ QEMUBHFunc *cb; ++ void *opaque; ++} AioWaitBHData; ++ ++/* Context: BH in IOThread */ ++static void aio_wait_bh(void *opaque) ++{ ++ AioWaitBHData *data = opaque; ++ ++ data->cb(data->opaque); ++ ++ data->done = true; ++ aio_wait_kick(); ++} ++ ++void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) ++{ ++ AioWaitBHData data = { ++ .cb = cb, ++ .opaque = opaque, ++ }; ++ ++ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); ++ ++ aio_bh_schedule_oneshot(ctx, aio_wait_bh, &data); ++ AIO_WAIT_WHILE_UNLOCKED(NULL, !data.done); ++} +diff --git a/qcow2/lib/util/aiocb.c b/qcow2/lib/util/aiocb.c +new file mode 100644 +index 00000000..5aef3a06 +--- /dev/null ++++ b/qcow2/lib/util/aiocb.c +@@ -0,0 +1,55 @@ ++/* ++ * BlockAIOCB allocation ++ * ++ * Copyright (c) 2003-2017 Fabrice Bellard and other QEMU contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "block/aio.h" ++ ++void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ BlockAIOCB *acb; ++ ++ acb = g_malloc(aiocb_info->aiocb_size); ++ acb->aiocb_info = aiocb_info; ++ acb->bs = bs; ++ acb->cb = cb; ++ acb->opaque = opaque; ++ acb->refcnt = 1; ++ return acb; ++} ++ ++void qemu_aio_ref(void *p) ++{ ++ BlockAIOCB *acb = p; ++ acb->refcnt++; ++} ++ ++void qemu_aio_unref(void *p) ++{ ++ BlockAIOCB *acb = p; ++ assert(acb->refcnt > 0); ++ if (--acb->refcnt == 0) { ++ g_free(acb); ++ } ++} +diff --git a/qcow2/lib/util/async.c b/qcow2/lib/util/async.c +new file mode 100644 +index 00000000..3e3e4fc7 +--- /dev/null ++++ b/qcow2/lib/util/async.c +@@ -0,0 +1,760 @@ ++/* ++ * Data plane event loop ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2009-2017 QEMU contributors ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "block/aio.h" ++#include "block/thread-pool.h" ++#include "block/graph-lock.h" ++#include "qemu/main-loop.h" ++#include "qemu/atomic.h" ++#include "qemu/rcu_queue.h" ++#include "block/raw-aio.h" ++#include "qemu/coroutine_int.h" ++#include "qemu/coroutine-tls.h" ++#include "sysemu/cpu-timers.h" ++#include "trace.h" ++ ++/***********************************************************/ ++/* bottom halves (can be seen as timers which expire ASAP) */ ++ ++/* QEMUBH::flags values */ ++enum { ++ /* Already enqueued and waiting for aio_bh_poll() */ ++ BH_PENDING = (1 << 0), ++ ++ /* Invoke the callback */ ++ BH_SCHEDULED = (1 << 1), ++ ++ /* Delete without invoking callback */ ++ BH_DELETED = (1 << 2), ++ ++ /* Delete after invoking callback */ ++ BH_ONESHOT = (1 << 3), ++ ++ /* Schedule periodically when the event loop is idle */ ++ BH_IDLE = (1 << 4), ++}; ++ ++struct QEMUBH { ++ AioContext *ctx; ++ const char *name; ++ QEMUBHFunc *cb; ++ void *opaque; ++ QSLIST_ENTRY(QEMUBH) next; ++ unsigned flags; ++ MemReentrancyGuard *reentrancy_guard; ++}; ++ ++/* Called concurrently from any thread */ ++static void aio_bh_enqueue(QEMUBH *bh, unsigned new_flags) ++{ ++ AioContext *ctx = bh->ctx; ++ unsigned old_flags; ++ ++ /* ++ * Synchronizes with atomic_fetch_and() in aio_bh_dequeue(), ensuring that ++ * insertion starts after BH_PENDING is set. ++ */ ++ old_flags = qatomic_fetch_or(&bh->flags, BH_PENDING | new_flags); ++ ++ if (!(old_flags & BH_PENDING)) { ++ /* ++ * At this point the bottom half becomes visible to aio_bh_poll(). ++ * This insertion thus synchronizes with QSLIST_MOVE_ATOMIC in ++ * aio_bh_poll(), ensuring that: ++ * 1. any writes needed by the callback are visible from the callback ++ * after aio_bh_dequeue() returns bh. ++ * 2. ctx is loaded before the callback has a chance to execute and bh ++ * could be freed. ++ */ ++ QSLIST_INSERT_HEAD_ATOMIC(&ctx->bh_list, bh, next); ++ } ++ ++ aio_notify(ctx); ++ if (unlikely(icount_enabled())) { ++ /* ++ * Workaround for record/replay. ++ * vCPU execution should be suspended when new BH is set. ++ * This is needed to avoid guest timeouts caused ++ * by the long cycles of the execution. ++ */ ++ icount_notify_exit(); ++ } ++} ++ ++/* Only called from aio_bh_poll() and aio_ctx_finalize() */ ++static QEMUBH *aio_bh_dequeue(BHList *head, unsigned *flags) ++{ ++ QEMUBH *bh = QSLIST_FIRST_RCU(head); ++ ++ if (!bh) { ++ return NULL; ++ } ++ ++ QSLIST_REMOVE_HEAD(head, next); ++ ++ /* ++ * Synchronizes with qatomic_fetch_or() in aio_bh_enqueue(), ensuring that ++ * the removal finishes before BH_PENDING is reset. ++ */ ++ *flags = qatomic_fetch_and(&bh->flags, ++ ~(BH_PENDING | BH_SCHEDULED | BH_IDLE)); ++ return bh; ++} ++ ++void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, ++ void *opaque, const char *name) ++{ ++ QEMUBH *bh; ++ bh = g_new(QEMUBH, 1); ++ *bh = (QEMUBH){ ++ .ctx = ctx, ++ .cb = cb, ++ .opaque = opaque, ++ .name = name, ++ }; ++ aio_bh_enqueue(bh, BH_SCHEDULED | BH_ONESHOT); ++} ++ ++QEMUBH *aio_bh_new_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, ++ const char *name, MemReentrancyGuard *reentrancy_guard) ++{ ++ QEMUBH *bh; ++ bh = g_new(QEMUBH, 1); ++ *bh = (QEMUBH){ ++ .ctx = ctx, ++ .cb = cb, ++ .opaque = opaque, ++ .name = name, ++ .reentrancy_guard = reentrancy_guard, ++ }; ++ return bh; ++} ++ ++void aio_bh_call(QEMUBH *bh) ++{ ++ bool last_engaged_in_io = false; ++ ++ /* Make a copy of the guard-pointer as cb may free the bh */ ++ MemReentrancyGuard *reentrancy_guard = bh->reentrancy_guard; ++ if (reentrancy_guard) { ++ last_engaged_in_io = reentrancy_guard->engaged_in_io; ++ if (reentrancy_guard->engaged_in_io) { ++ trace_reentrant_aio(bh->ctx, bh->name); ++ } ++ reentrancy_guard->engaged_in_io = true; ++ } ++ ++ bh->cb(bh->opaque); ++ ++ if (reentrancy_guard) { ++ reentrancy_guard->engaged_in_io = last_engaged_in_io; ++ } ++} ++ ++/* Multiple occurrences of aio_bh_poll cannot be called concurrently. */ ++int aio_bh_poll(AioContext *ctx) ++{ ++ BHListSlice slice; ++ BHListSlice *s; ++ int ret = 0; ++ ++ /* Synchronizes with QSLIST_INSERT_HEAD_ATOMIC in aio_bh_enqueue(). */ ++ QSLIST_MOVE_ATOMIC(&slice.bh_list, &ctx->bh_list); ++ ++ /* ++ * GCC13 [-Werror=dangling-pointer=] complains that the local variable ++ * 'slice' is being stored in the global 'ctx->bh_slice_list' but the ++ * list is emptied before this function returns. ++ */ ++#if !defined(__clang__) ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wpragmas" ++#pragma GCC diagnostic ignored "-Wdangling-pointer=" ++#endif ++ QSIMPLEQ_INSERT_TAIL(&ctx->bh_slice_list, &slice, next); ++#if !defined(__clang__) ++#pragma GCC diagnostic pop ++#endif ++ ++ while ((s = QSIMPLEQ_FIRST(&ctx->bh_slice_list))) { ++ QEMUBH *bh; ++ unsigned flags; ++ ++ bh = aio_bh_dequeue(&s->bh_list, &flags); ++ if (!bh) { ++ QSIMPLEQ_REMOVE_HEAD(&ctx->bh_slice_list, next); ++ continue; ++ } ++ ++ if ((flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { ++ /* Idle BHs don't count as progress */ ++ if (!(flags & BH_IDLE)) { ++ ret = 1; ++ } ++ aio_bh_call(bh); ++ } ++ if (flags & (BH_DELETED | BH_ONESHOT)) { ++ g_free(bh); ++ } ++ } ++ ++ return ret; ++} ++ ++void qemu_bh_schedule_idle(QEMUBH *bh) ++{ ++ aio_bh_enqueue(bh, BH_SCHEDULED | BH_IDLE); ++} ++ ++void qemu_bh_schedule(QEMUBH *bh) ++{ ++ aio_bh_enqueue(bh, BH_SCHEDULED); ++} ++ ++/* This func is async. ++ */ ++void qemu_bh_cancel(QEMUBH *bh) ++{ ++ qatomic_and(&bh->flags, ~BH_SCHEDULED); ++} ++ ++/* This func is async.The bottom half will do the delete action at the finial ++ * end. ++ */ ++void qemu_bh_delete(QEMUBH *bh) ++{ ++ aio_bh_enqueue(bh, BH_DELETED); ++} ++ ++static int64_t aio_compute_bh_timeout(BHList *head, int timeout) ++{ ++ QEMUBH *bh; ++ ++ QSLIST_FOREACH_RCU(bh, head, next) { ++ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { ++ if (bh->flags & BH_IDLE) { ++ /* idle bottom halves will be polled at least ++ * every 10ms */ ++ timeout = 10000000; ++ } else { ++ /* non-idle bottom halves will be executed ++ * immediately */ ++ return 0; ++ } ++ } ++ } ++ ++ return timeout; ++} ++ ++int64_t ++aio_compute_timeout(AioContext *ctx) ++{ ++ BHListSlice *s; ++ int64_t deadline; ++ int timeout = -1; ++ ++ timeout = aio_compute_bh_timeout(&ctx->bh_list, timeout); ++ if (timeout == 0) { ++ return 0; ++ } ++ ++ QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) { ++ timeout = aio_compute_bh_timeout(&s->bh_list, timeout); ++ if (timeout == 0) { ++ return 0; ++ } ++ } ++ ++ deadline = timerlistgroup_deadline_ns(&ctx->tlg); ++ if (deadline == 0) { ++ return 0; ++ } else { ++ return qemu_soonest_timeout(timeout, deadline); ++ } ++} ++ ++static gboolean ++aio_ctx_prepare(GSource *source, gint *timeout) ++{ ++ AioContext *ctx = (AioContext *) source; ++ ++ qatomic_set(&ctx->notify_me, qatomic_read(&ctx->notify_me) | 1); ++ ++ /* ++ * Write ctx->notify_me before computing the timeout ++ * (reading bottom half flags, etc.). Pairs with ++ * smp_mb in aio_notify(). ++ */ ++ smp_mb(); ++ ++ /* We assume there is no timeout already supplied */ ++ *timeout = qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)); ++ ++ if (aio_prepare(ctx)) { ++ *timeout = 0; ++ } ++ ++ return *timeout == 0; ++} ++ ++static gboolean ++aio_ctx_check(GSource *source) ++{ ++ AioContext *ctx = (AioContext *) source; ++ QEMUBH *bh; ++ BHListSlice *s; ++ ++ /* Finish computing the timeout before clearing the flag. */ ++ qatomic_store_release(&ctx->notify_me, qatomic_read(&ctx->notify_me) & ~1); ++ aio_notify_accept(ctx); ++ ++ QSLIST_FOREACH_RCU(bh, &ctx->bh_list, next) { ++ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { ++ return true; ++ } ++ } ++ ++ QSIMPLEQ_FOREACH(s, &ctx->bh_slice_list, next) { ++ QSLIST_FOREACH_RCU(bh, &s->bh_list, next) { ++ if ((bh->flags & (BH_SCHEDULED | BH_DELETED)) == BH_SCHEDULED) { ++ return true; ++ } ++ } ++ } ++ return aio_pending(ctx) || (timerlistgroup_deadline_ns(&ctx->tlg) == 0); ++} ++ ++static gboolean ++aio_ctx_dispatch(GSource *source, ++ GSourceFunc callback, ++ gpointer user_data) ++{ ++ AioContext *ctx = (AioContext *) source; ++ ++ assert(callback == NULL); ++ aio_dispatch(ctx); ++ return true; ++} ++ ++static void ++aio_ctx_finalize(GSource *source) ++{ ++ AioContext *ctx = (AioContext *) source; ++ QEMUBH *bh; ++ unsigned flags; ++ ++ thread_pool_free(ctx->thread_pool); ++ ++#ifdef CONFIG_LINUX_AIO ++ if (ctx->linux_aio) { ++ laio_detach_aio_context(ctx->linux_aio, ctx); ++ laio_cleanup(ctx->linux_aio); ++ ctx->linux_aio = NULL; ++ } ++#endif ++ ++#ifdef CONFIG_LINUX_IO_URING ++ if (ctx->linux_io_uring) { ++ luring_detach_aio_context(ctx->linux_io_uring, ctx); ++ luring_cleanup(ctx->linux_io_uring); ++ ctx->linux_io_uring = NULL; ++ } ++#endif ++ ++ assert(QSLIST_EMPTY(&ctx->scheduled_coroutines)); ++ qemu_bh_delete(ctx->co_schedule_bh); ++ ++ /* There must be no aio_bh_poll() calls going on */ ++ assert(QSIMPLEQ_EMPTY(&ctx->bh_slice_list)); ++ ++ while ((bh = aio_bh_dequeue(&ctx->bh_list, &flags))) { ++ /* ++ * qemu_bh_delete() must have been called on BHs in this AioContext. In ++ * many cases memory leaks, hangs, or inconsistent state occur when a ++ * BH is leaked because something still expects it to run. ++ * ++ * If you hit this, fix the lifecycle of the BH so that ++ * qemu_bh_delete() and any associated cleanup is called before the ++ * AioContext is finalized. ++ */ ++ if (unlikely(!(flags & BH_DELETED))) { ++ fprintf(stderr, "%s: BH '%s' leaked, aborting...\n", ++ __func__, bh->name); ++ abort(); ++ } ++ ++ g_free(bh); ++ } ++ ++ aio_set_event_notifier(ctx, &ctx->notifier, NULL, NULL, NULL); ++ event_notifier_cleanup(&ctx->notifier); ++ qemu_rec_mutex_destroy(&ctx->lock); ++ qemu_lockcnt_destroy(&ctx->list_lock); ++ timerlistgroup_deinit(&ctx->tlg); ++ unregister_aiocontext(ctx); ++ aio_context_destroy(ctx); ++} ++ ++static GSourceFuncs aio_source_funcs = { ++ aio_ctx_prepare, ++ aio_ctx_check, ++ aio_ctx_dispatch, ++ aio_ctx_finalize ++}; ++ ++GSource *aio_get_g_source(AioContext *ctx) ++{ ++ aio_context_use_g_source(ctx); ++ g_source_ref(&ctx->source); ++ return &ctx->source; ++} ++ ++ThreadPool *aio_get_thread_pool(AioContext *ctx) ++{ ++ if (!ctx->thread_pool) { ++ ctx->thread_pool = thread_pool_new(ctx); ++ } ++ return ctx->thread_pool; ++} ++ ++#ifdef CONFIG_LINUX_AIO ++LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp) ++{ ++ if (!ctx->linux_aio) { ++ ctx->linux_aio = laio_init(errp); ++ if (ctx->linux_aio) { ++ laio_attach_aio_context(ctx->linux_aio, ctx); ++ } ++ } ++ return ctx->linux_aio; ++} ++ ++LinuxAioState *aio_get_linux_aio(AioContext *ctx) ++{ ++ assert(ctx->linux_aio); ++ return ctx->linux_aio; ++} ++#endif ++ ++#ifdef CONFIG_LINUX_IO_URING ++LuringState *aio_setup_linux_io_uring(AioContext *ctx, Error **errp) ++{ ++ if (ctx->linux_io_uring) { ++ return ctx->linux_io_uring; ++ } ++ ++ ctx->linux_io_uring = luring_init(errp); ++ if (!ctx->linux_io_uring) { ++ return NULL; ++ } ++ ++ luring_attach_aio_context(ctx->linux_io_uring, ctx); ++ return ctx->linux_io_uring; ++} ++ ++LuringState *aio_get_linux_io_uring(AioContext *ctx) ++{ ++ assert(ctx->linux_io_uring); ++ return ctx->linux_io_uring; ++} ++#endif ++ ++void aio_notify(AioContext *ctx) ++{ ++ /* ++ * Write e.g. ctx->bh_list before writing ctx->notified. Pairs with ++ * smp_mb() in aio_notify_accept(). ++ */ ++ smp_wmb(); ++ qatomic_set(&ctx->notified, true); ++ ++ /* ++ * Write ctx->notified (and also ctx->bh_list) before reading ctx->notify_me. ++ * Pairs with smp_mb() in aio_ctx_prepare or aio_poll. ++ */ ++ smp_mb(); ++ if (qatomic_read(&ctx->notify_me)) { ++ event_notifier_set(&ctx->notifier); ++ } ++} ++ ++void aio_notify_accept(AioContext *ctx) ++{ ++ qatomic_set(&ctx->notified, false); ++ ++ /* ++ * Order reads of ctx->notified (in aio_context_notifier_poll()) and the ++ * above clearing of ctx->notified before reads of e.g. bh->flags. Pairs ++ * with smp_wmb() in aio_notify. ++ */ ++ smp_mb(); ++} ++ ++static void aio_timerlist_notify(void *opaque, QEMUClockType type) ++{ ++ aio_notify(opaque); ++} ++ ++static void aio_context_notifier_cb(EventNotifier *e) ++{ ++ AioContext *ctx = container_of(e, AioContext, notifier); ++ ++ event_notifier_test_and_clear(&ctx->notifier); ++} ++ ++/* Returns true if aio_notify() was called (e.g. a BH was scheduled) */ ++static bool aio_context_notifier_poll(void *opaque) ++{ ++ EventNotifier *e = opaque; ++ AioContext *ctx = container_of(e, AioContext, notifier); ++ ++ /* ++ * No need for load-acquire because we just want to kick the ++ * event loop. aio_notify_accept() takes care of synchronizing ++ * the event loop with the producers. ++ */ ++ return qatomic_read(&ctx->notified); ++} ++ ++static void aio_context_notifier_poll_ready(EventNotifier *e) ++{ ++ /* Do nothing, we just wanted to kick the event loop */ ++} ++ ++static void co_schedule_bh_cb(void *opaque) ++{ ++ AioContext *ctx = opaque; ++ QSLIST_HEAD(, Coroutine) straight, reversed; ++ ++ QSLIST_MOVE_ATOMIC(&reversed, &ctx->scheduled_coroutines); ++ QSLIST_INIT(&straight); ++ ++ while (!QSLIST_EMPTY(&reversed)) { ++ Coroutine *co = QSLIST_FIRST(&reversed); ++ QSLIST_REMOVE_HEAD(&reversed, co_scheduled_next); ++ QSLIST_INSERT_HEAD(&straight, co, co_scheduled_next); ++ } ++ ++ while (!QSLIST_EMPTY(&straight)) { ++ Coroutine *co = QSLIST_FIRST(&straight); ++ QSLIST_REMOVE_HEAD(&straight, co_scheduled_next); ++ trace_aio_co_schedule_bh_cb(ctx, co); ++ ++ /* Protected by write barrier in qemu_aio_coroutine_enter */ ++ qatomic_set(&co->scheduled, NULL); ++ qemu_aio_coroutine_enter(ctx, co); ++ } ++} ++ ++AioContext *aio_context_new(Error **errp) ++{ ++ int ret; ++ AioContext *ctx; ++ ++ ctx = (AioContext *) g_source_new(&aio_source_funcs, sizeof(AioContext)); ++ QSLIST_INIT(&ctx->bh_list); ++ QSIMPLEQ_INIT(&ctx->bh_slice_list); ++ aio_context_setup(ctx); ++ ++ ret = event_notifier_init(&ctx->notifier, false); ++ if (ret < 0) { ++ error_setg_errno(errp, -ret, "Failed to initialize event notifier"); ++ goto fail; ++ } ++ g_source_set_can_recurse(&ctx->source, true); ++ qemu_lockcnt_init(&ctx->list_lock); ++ ++ ctx->co_schedule_bh = aio_bh_new(ctx, co_schedule_bh_cb, ctx); ++ QSLIST_INIT(&ctx->scheduled_coroutines); ++ ++ aio_set_event_notifier(ctx, &ctx->notifier, ++ aio_context_notifier_cb, ++ aio_context_notifier_poll, ++ aio_context_notifier_poll_ready); ++#ifdef CONFIG_LINUX_AIO ++ ctx->linux_aio = NULL; ++#endif ++ ++#ifdef CONFIG_LINUX_IO_URING ++ ctx->linux_io_uring = NULL; ++#endif ++ ++ ctx->thread_pool = NULL; ++ qemu_rec_mutex_init(&ctx->lock); ++ timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); ++ ++ ctx->poll_ns = 0; ++ ctx->poll_max_ns = 0; ++ ctx->poll_grow = 0; ++ ctx->poll_shrink = 0; ++ ++ ctx->aio_max_batch = 0; ++ ++ ctx->thread_pool_min = 0; ++ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT; ++ ++ register_aiocontext(ctx); ++ ++ return ctx; ++fail: ++ g_source_destroy(&ctx->source); ++ return NULL; ++} ++ ++void aio_co_schedule(AioContext *ctx, Coroutine *co) ++{ ++ trace_aio_co_schedule(ctx, co); ++ const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, ++ __func__); ++ ++ if (scheduled) { ++ fprintf(stderr, ++ "%s: Co-routine was already scheduled in '%s'\n", ++ __func__, scheduled); ++ abort(); ++ } ++ ++ /* The coroutine might run and release the last ctx reference before we ++ * invoke qemu_bh_schedule(). Take a reference to keep ctx alive until ++ * we're done. ++ */ ++ aio_context_ref(ctx); ++ ++ QSLIST_INSERT_HEAD_ATOMIC(&ctx->scheduled_coroutines, ++ co, co_scheduled_next); ++ qemu_bh_schedule(ctx->co_schedule_bh); ++ ++ aio_context_unref(ctx); ++} ++ ++typedef struct AioCoRescheduleSelf { ++ Coroutine *co; ++ AioContext *new_ctx; ++} AioCoRescheduleSelf; ++ ++static void aio_co_reschedule_self_bh(void *opaque) ++{ ++ AioCoRescheduleSelf *data = opaque; ++ aio_co_schedule(data->new_ctx, data->co); ++} ++ ++void coroutine_fn aio_co_reschedule_self(AioContext *new_ctx) ++{ ++ AioContext *old_ctx = qemu_get_current_aio_context(); ++ ++ if (old_ctx != new_ctx) { ++ AioCoRescheduleSelf data = { ++ .co = qemu_coroutine_self(), ++ .new_ctx = new_ctx, ++ }; ++ /* ++ * We can't directly schedule the coroutine in the target context ++ * because this would be racy: The other thread could try to enter the ++ * coroutine before it has yielded in this one. ++ */ ++ aio_bh_schedule_oneshot(old_ctx, aio_co_reschedule_self_bh, &data); ++ qemu_coroutine_yield(); ++ } ++} ++ ++void aio_co_wake(Coroutine *co) ++{ ++ AioContext *ctx; ++ ++ /* Read coroutine before co->ctx. Matches smp_wmb in ++ * qemu_coroutine_enter. ++ */ ++ smp_read_barrier_depends(); ++ ctx = qatomic_read(&co->ctx); ++ ++ aio_co_enter(ctx, co); ++} ++ ++void aio_co_enter(AioContext *ctx, Coroutine *co) ++{ ++ if (ctx != qemu_get_current_aio_context()) { ++ aio_co_schedule(ctx, co); ++ return; ++ } ++ ++ if (qemu_in_coroutine()) { ++ Coroutine *self = qemu_coroutine_self(); ++ assert(self != co); ++ QSIMPLEQ_INSERT_TAIL(&self->co_queue_wakeup, co, co_queue_next); ++ } else { ++ qemu_aio_coroutine_enter(ctx, co); ++ } ++} ++ ++void aio_context_ref(AioContext *ctx) ++{ ++ g_source_ref(&ctx->source); ++} ++ ++void aio_context_unref(AioContext *ctx) ++{ ++ g_source_unref(&ctx->source); ++} ++ ++QEMU_DEFINE_STATIC_CO_TLS(AioContext *, my_aiocontext) ++ ++AioContext *qemu_get_current_aio_context(void) ++{ ++ AioContext *ctx = get_my_aiocontext(); ++ if (ctx) { ++ return ctx; ++ } ++ if (bql_locked()) { ++ /* Possibly in a vCPU thread. */ ++ return qemu_get_aio_context(); ++ } ++ return NULL; ++} ++ ++void qemu_set_current_aio_context(AioContext *ctx) ++{ ++ assert(!get_my_aiocontext()); ++ set_my_aiocontext(ctx); ++} ++ ++void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, ++ int64_t max, Error **errp) ++{ ++ ++ if (min > max || max <= 0 || min < 0 || min > INT_MAX || max > INT_MAX) { ++ error_setg(errp, "bad thread-pool-min/thread-pool-max values"); ++ return; ++ } ++ ++ ctx->thread_pool_min = min; ++ ctx->thread_pool_max = max; ++ ++ if (ctx->thread_pool) { ++ thread_pool_update_params(ctx->thread_pool, ctx); ++ } ++} +diff --git a/qcow2/lib/util/bitmap.c b/qcow2/lib/util/bitmap.c +new file mode 100644 +index 00000000..8d12e90a +--- /dev/null ++++ b/qcow2/lib/util/bitmap.c +@@ -0,0 +1,534 @@ ++/* ++ * Bitmap Module ++ * ++ * Stolen from linux/src/lib/bitmap.c ++ * ++ * Copyright (C) 2010 Corentin Chary ++ * ++ * This source code is licensed under the GNU General Public License, ++ * Version 2. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/bitops.h" ++#include "qemu/bitmap.h" ++#include "qemu/atomic.h" ++ ++/* ++ * bitmaps provide an array of bits, implemented using an ++ * array of unsigned longs. The number of valid bits in a ++ * given bitmap does _not_ need to be an exact multiple of ++ * BITS_PER_LONG. ++ * ++ * The possible unused bits in the last, partially used word ++ * of a bitmap are 'don't care'. The implementation makes ++ * no particular effort to keep them zero. It ensures that ++ * their value will not affect the results of any operation. ++ * The bitmap operations that return Boolean (bitmap_empty, ++ * for example) or scalar (bitmap_weight, for example) results ++ * carefully filter out these unused bits from impacting their ++ * results. ++ * ++ * These operations actually hold to a slightly stronger rule: ++ * if you don't input any bitmaps to these ops that have some ++ * unused bits set, then they won't output any set unused bits ++ * in output bitmaps. ++ * ++ * The byte ordering of bitmaps is more natural on little ++ * endian architectures. ++ */ ++ ++int slow_bitmap_empty(const unsigned long *bitmap, long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ if (bitmap[k]) { ++ return 0; ++ } ++ } ++ if (bits % BITS_PER_LONG) { ++ if (bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) { ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++int slow_bitmap_full(const unsigned long *bitmap, long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ if (~bitmap[k]) { ++ return 0; ++ } ++ } ++ ++ if (bits % BITS_PER_LONG) { ++ if (~bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) { ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++int slow_bitmap_equal(const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ if (bitmap1[k] != bitmap2[k]) { ++ return 0; ++ } ++ } ++ ++ if (bits % BITS_PER_LONG) { ++ if ((bitmap1[k] ^ bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) { ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ ++void slow_bitmap_complement(unsigned long *dst, const unsigned long *src, ++ long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ dst[k] = ~src[k]; ++ } ++ ++ if (bits % BITS_PER_LONG) { ++ dst[k] = ~src[k] & BITMAP_LAST_WORD_MASK(bits); ++ } ++} ++ ++int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k; ++ long nr = BITS_TO_LONGS(bits); ++ unsigned long result = 0; ++ ++ for (k = 0; k < nr; k++) { ++ result |= (dst[k] = bitmap1[k] & bitmap2[k]); ++ } ++ return result != 0; ++} ++ ++void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k; ++ long nr = BITS_TO_LONGS(bits); ++ ++ for (k = 0; k < nr; k++) { ++ dst[k] = bitmap1[k] | bitmap2[k]; ++ } ++} ++ ++void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k; ++ long nr = BITS_TO_LONGS(bits); ++ ++ for (k = 0; k < nr; k++) { ++ dst[k] = bitmap1[k] ^ bitmap2[k]; ++ } ++} ++ ++int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k; ++ long nr = BITS_TO_LONGS(bits); ++ unsigned long result = 0; ++ ++ for (k = 0; k < nr; k++) { ++ result |= (dst[k] = bitmap1[k] & ~bitmap2[k]); ++ } ++ return result != 0; ++} ++ ++void bitmap_set(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); ++ ++ assert(start >= 0 && nr >= 0); ++ ++ while (nr - bits_to_set >= 0) { ++ *p |= mask_to_set; ++ nr -= bits_to_set; ++ bits_to_set = BITS_PER_LONG; ++ mask_to_set = ~0UL; ++ p++; ++ } ++ if (nr) { ++ mask_to_set &= BITMAP_LAST_WORD_MASK(size); ++ *p |= mask_to_set; ++ } ++} ++ ++void bitmap_set_atomic(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); ++ ++ assert(start >= 0 && nr >= 0); ++ ++ /* First word */ ++ if (nr - bits_to_set > 0) { ++ qatomic_or(p, mask_to_set); ++ nr -= bits_to_set; ++ bits_to_set = BITS_PER_LONG; ++ mask_to_set = ~0UL; ++ p++; ++ } ++ ++ /* Full words */ ++ if (bits_to_set == BITS_PER_LONG) { ++ while (nr >= BITS_PER_LONG) { ++ *p = ~0UL; ++ nr -= BITS_PER_LONG; ++ p++; ++ } ++ } ++ ++ /* Last word */ ++ if (nr) { ++ mask_to_set &= BITMAP_LAST_WORD_MASK(size); ++ qatomic_or(p, mask_to_set); ++ } else { ++ /* If we avoided the full barrier in qatomic_or(), issue a ++ * barrier to account for the assignments in the while loop. ++ */ ++ smp_mb(); ++ } ++} ++ ++void bitmap_clear(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); ++ ++ assert(start >= 0 && nr >= 0); ++ ++ while (nr - bits_to_clear >= 0) { ++ *p &= ~mask_to_clear; ++ nr -= bits_to_clear; ++ bits_to_clear = BITS_PER_LONG; ++ mask_to_clear = ~0UL; ++ p++; ++ } ++ if (nr) { ++ mask_to_clear &= BITMAP_LAST_WORD_MASK(size); ++ *p &= ~mask_to_clear; ++ } ++} ++ ++bool bitmap_test_and_clear(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); ++ bool dirty = false; ++ ++ assert(start >= 0 && nr >= 0); ++ ++ /* First word */ ++ if (nr - bits_to_clear > 0) { ++ if ((*p) & mask_to_clear) { ++ dirty = true; ++ } ++ *p &= ~mask_to_clear; ++ nr -= bits_to_clear; ++ bits_to_clear = BITS_PER_LONG; ++ p++; ++ } ++ ++ /* Full words */ ++ if (bits_to_clear == BITS_PER_LONG) { ++ while (nr >= BITS_PER_LONG) { ++ if (*p) { ++ dirty = true; ++ *p = 0; ++ } ++ nr -= BITS_PER_LONG; ++ p++; ++ } ++ } ++ ++ /* Last word */ ++ if (nr) { ++ mask_to_clear &= BITMAP_LAST_WORD_MASK(size); ++ if ((*p) & mask_to_clear) { ++ dirty = true; ++ } ++ *p &= ~mask_to_clear; ++ } ++ ++ return dirty; ++} ++ ++bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr) ++{ ++ unsigned long *p = map + BIT_WORD(start); ++ const long size = start + nr; ++ int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); ++ unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); ++ unsigned long dirty = 0; ++ unsigned long old_bits; ++ ++ assert(start >= 0 && nr >= 0); ++ ++ /* First word */ ++ if (nr - bits_to_clear > 0) { ++ old_bits = qatomic_fetch_and(p, ~mask_to_clear); ++ dirty |= old_bits & mask_to_clear; ++ nr -= bits_to_clear; ++ bits_to_clear = BITS_PER_LONG; ++ mask_to_clear = ~0UL; ++ p++; ++ } ++ ++ /* Full words */ ++ if (bits_to_clear == BITS_PER_LONG) { ++ while (nr >= BITS_PER_LONG) { ++ if (*p) { ++ old_bits = qatomic_xchg(p, 0); ++ dirty |= old_bits; ++ } ++ nr -= BITS_PER_LONG; ++ p++; ++ } ++ } ++ ++ /* Last word */ ++ if (nr) { ++ mask_to_clear &= BITMAP_LAST_WORD_MASK(size); ++ old_bits = qatomic_fetch_and(p, ~mask_to_clear); ++ dirty |= old_bits & mask_to_clear; ++ } else { ++ if (!dirty) { ++ smp_mb(); ++ } ++ } ++ ++ return dirty != 0; ++} ++ ++void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, ++ long nr) ++{ ++ while (nr > 0) { ++ *dst = qatomic_xchg(src, 0); ++ dst++; ++ src++; ++ nr -= BITS_PER_LONG; ++ } ++} ++ ++#define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) ++ ++/** ++ * bitmap_find_next_zero_area - find a contiguous aligned zero area ++ * @map: The address to base the search on ++ * @size: The bitmap size in bits ++ * @start: The bitnumber to start searching at ++ * @nr: The number of zeroed bits we're looking for ++ * @align_mask: Alignment mask for zero area ++ * ++ * The @align_mask should be one less than a power of 2; the effect is that ++ * the bit offset of all zero areas this function finds is multiples of that ++ * power of 2. A @align_mask of 0 means no alignment is required. ++ */ ++unsigned long bitmap_find_next_zero_area(unsigned long *map, ++ unsigned long size, ++ unsigned long start, ++ unsigned long nr, ++ unsigned long align_mask) ++{ ++ unsigned long index, end, i; ++again: ++ index = find_next_zero_bit(map, size, start); ++ ++ /* Align allocation */ ++ index = ALIGN_MASK(index, align_mask); ++ ++ end = index + nr; ++ if (end > size) { ++ return end; ++ } ++ i = find_next_bit(map, end, index); ++ if (i < end) { ++ start = i + 1; ++ goto again; ++ } ++ return index; ++} ++ ++int slow_bitmap_intersects(const unsigned long *bitmap1, ++ const unsigned long *bitmap2, long bits) ++{ ++ long k, lim = bits/BITS_PER_LONG; ++ ++ for (k = 0; k < lim; ++k) { ++ if (bitmap1[k] & bitmap2[k]) { ++ return 1; ++ } ++ } ++ ++ if (bits % BITS_PER_LONG) { ++ if ((bitmap1[k] & bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) { ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++long slow_bitmap_count_one(const unsigned long *bitmap, long nbits) ++{ ++ long k, lim = nbits / BITS_PER_LONG, result = 0; ++ ++ for (k = 0; k < lim; k++) { ++ result += ctpopl(bitmap[k]); ++ } ++ ++ if (nbits % BITS_PER_LONG) { ++ result += ctpopl(bitmap[k] & BITMAP_LAST_WORD_MASK(nbits)); ++ } ++ ++ return result; ++} ++ ++static void bitmap_to_from_le(unsigned long *dst, ++ const unsigned long *src, long nbits) ++{ ++ long len = BITS_TO_LONGS(nbits); ++ ++#if HOST_BIG_ENDIAN ++ long index; ++ ++ for (index = 0; index < len; index++) { ++# if HOST_LONG_BITS == 64 ++ dst[index] = bswap64(src[index]); ++# else ++ dst[index] = bswap32(src[index]); ++# endif ++ } ++#else ++ memcpy(dst, src, len * sizeof(unsigned long)); ++#endif ++} ++ ++void bitmap_from_le(unsigned long *dst, const unsigned long *src, ++ long nbits) ++{ ++ bitmap_to_from_le(dst, src, nbits); ++} ++ ++void bitmap_to_le(unsigned long *dst, const unsigned long *src, ++ long nbits) ++{ ++ bitmap_to_from_le(dst, src, nbits); ++} ++ ++/* ++ * Copy "src" bitmap with a positive offset and put it into the "dst" ++ * bitmap. The caller needs to make sure the bitmap size of "src" ++ * is bigger than (shift + nbits). ++ */ ++void bitmap_copy_with_src_offset(unsigned long *dst, const unsigned long *src, ++ unsigned long shift, unsigned long nbits) ++{ ++ unsigned long left_mask, right_mask, last_mask; ++ ++ /* Proper shift src pointer to the first word to copy from */ ++ src += BIT_WORD(shift); ++ shift %= BITS_PER_LONG; ++ ++ if (!shift) { ++ /* Fast path */ ++ bitmap_copy(dst, src, nbits); ++ return; ++ } ++ ++ right_mask = (1ul << shift) - 1; ++ left_mask = ~right_mask; ++ ++ while (nbits >= BITS_PER_LONG) { ++ *dst = (*src & left_mask) >> shift; ++ *dst |= (src[1] & right_mask) << (BITS_PER_LONG - shift); ++ dst++; ++ src++; ++ nbits -= BITS_PER_LONG; ++ } ++ ++ if (nbits > BITS_PER_LONG - shift) { ++ *dst = (*src & left_mask) >> shift; ++ nbits -= BITS_PER_LONG - shift; ++ last_mask = (1ul << nbits) - 1; ++ *dst |= (src[1] & last_mask) << (BITS_PER_LONG - shift); ++ } else if (nbits) { ++ last_mask = (1ul << nbits) - 1; ++ *dst = (*src >> shift) & last_mask; ++ } ++} ++ ++/* ++ * Copy "src" bitmap into the "dst" bitmap with an offset in the ++ * "dst". The caller needs to make sure the bitmap size of "dst" is ++ * bigger than (shift + nbits). ++ */ ++void bitmap_copy_with_dst_offset(unsigned long *dst, const unsigned long *src, ++ unsigned long shift, unsigned long nbits) ++{ ++ unsigned long left_mask, right_mask, last_mask; ++ ++ /* Proper shift dst pointer to the first word to copy from */ ++ dst += BIT_WORD(shift); ++ shift %= BITS_PER_LONG; ++ ++ if (!shift) { ++ /* Fast path */ ++ bitmap_copy(dst, src, nbits); ++ return; ++ } ++ ++ right_mask = (1ul << (BITS_PER_LONG - shift)) - 1; ++ left_mask = ~right_mask; ++ ++ *dst &= (1ul << shift) - 1; ++ while (nbits >= BITS_PER_LONG) { ++ *dst |= (*src & right_mask) << shift; ++ dst[1] = (*src & left_mask) >> (BITS_PER_LONG - shift); ++ dst++; ++ src++; ++ nbits -= BITS_PER_LONG; ++ } ++ ++ if (nbits > BITS_PER_LONG - shift) { ++ *dst |= (*src & right_mask) << shift; ++ nbits -= BITS_PER_LONG - shift; ++ last_mask = ((1ul << nbits) - 1) << (BITS_PER_LONG - shift); ++ dst[1] = (*src & last_mask) >> (BITS_PER_LONG - shift); ++ } else if (nbits) { ++ last_mask = (1ul << nbits) - 1; ++ *dst |= (*src & last_mask) << shift; ++ } ++} +diff --git a/qcow2/lib/util/bitops.c b/qcow2/lib/util/bitops.c +new file mode 100644 +index 00000000..4b647b3e +--- /dev/null ++++ b/qcow2/lib/util/bitops.c +@@ -0,0 +1,157 @@ ++/* ++ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. ++ * Written by David Howells (dhowells@redhat.com) ++ * Copyright (C) 2008 IBM Corporation ++ * Written by Rusty Russell ++ * (Inspired by David Howell's find_next_bit implementation) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version ++ * 2 of the License, or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/bitops.h" ++ ++/* ++ * Find the next set bit in a memory region. ++ */ ++unsigned long find_next_bit(const unsigned long *addr, unsigned long size, ++ unsigned long offset) ++{ ++ const unsigned long *p = addr + BIT_WORD(offset); ++ unsigned long result = offset & ~(BITS_PER_LONG-1); ++ unsigned long tmp; ++ ++ if (offset >= size) { ++ return size; ++ } ++ size -= result; ++ offset %= BITS_PER_LONG; ++ if (offset) { ++ tmp = *(p++); ++ tmp &= (~0UL << offset); ++ if (size < BITS_PER_LONG) { ++ goto found_first; ++ } ++ if (tmp) { ++ goto found_middle; ++ } ++ size -= BITS_PER_LONG; ++ result += BITS_PER_LONG; ++ } ++ while (size >= 4*BITS_PER_LONG) { ++ unsigned long d1, d2, d3; ++ tmp = *p; ++ d1 = *(p+1); ++ d2 = *(p+2); ++ d3 = *(p+3); ++ if (tmp) { ++ goto found_middle; ++ } ++ if (d1 | d2 | d3) { ++ break; ++ } ++ p += 4; ++ result += 4*BITS_PER_LONG; ++ size -= 4*BITS_PER_LONG; ++ } ++ while (size >= BITS_PER_LONG) { ++ if ((tmp = *(p++))) { ++ goto found_middle; ++ } ++ result += BITS_PER_LONG; ++ size -= BITS_PER_LONG; ++ } ++ if (!size) { ++ return result; ++ } ++ tmp = *p; ++ ++found_first: ++ tmp &= (~0UL >> (BITS_PER_LONG - size)); ++ if (tmp == 0UL) { /* Are any bits set? */ ++ return result + size; /* Nope. */ ++ } ++found_middle: ++ return result + ctzl(tmp); ++} ++ ++/* ++ * This implementation of find_{first,next}_zero_bit was stolen from ++ * Linus' asm-alpha/bitops.h. ++ */ ++unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, ++ unsigned long offset) ++{ ++ const unsigned long *p = addr + BIT_WORD(offset); ++ unsigned long result = offset & ~(BITS_PER_LONG-1); ++ unsigned long tmp; ++ ++ if (offset >= size) { ++ return size; ++ } ++ size -= result; ++ offset %= BITS_PER_LONG; ++ if (offset) { ++ tmp = *(p++); ++ tmp |= ~0UL >> (BITS_PER_LONG - offset); ++ if (size < BITS_PER_LONG) { ++ goto found_first; ++ } ++ if (~tmp) { ++ goto found_middle; ++ } ++ size -= BITS_PER_LONG; ++ result += BITS_PER_LONG; ++ } ++ while (size & ~(BITS_PER_LONG-1)) { ++ if (~(tmp = *(p++))) { ++ goto found_middle; ++ } ++ result += BITS_PER_LONG; ++ size -= BITS_PER_LONG; ++ } ++ if (!size) { ++ return result; ++ } ++ tmp = *p; ++ ++found_first: ++ tmp |= ~0UL << size; ++ if (tmp == ~0UL) { /* Are any bits zero? */ ++ return result + size; /* Nope. */ ++ } ++found_middle: ++ return result + ctzl(~tmp); ++} ++ ++unsigned long find_last_bit(const unsigned long *addr, unsigned long size) ++{ ++ unsigned long words; ++ unsigned long tmp; ++ ++ /* Start at final word. */ ++ words = size / BITS_PER_LONG; ++ ++ /* Partial final word? */ ++ if (size & (BITS_PER_LONG-1)) { ++ tmp = (addr[words] & (~0UL >> (BITS_PER_LONG ++ - (size & (BITS_PER_LONG-1))))); ++ if (tmp) { ++ goto found; ++ } ++ } ++ ++ while (words) { ++ tmp = addr[--words]; ++ if (tmp) { ++ found: ++ return words * BITS_PER_LONG + BITS_PER_LONG - 1 - clzl(tmp); ++ } ++ } ++ ++ /* Not found */ ++ return size; ++} +diff --git a/qcow2/lib/util/bufferiszero.c b/qcow2/lib/util/bufferiszero.c +new file mode 100644 +index 00000000..522146da +--- /dev/null ++++ b/qcow2/lib/util/bufferiszero.c +@@ -0,0 +1,126 @@ ++/* ++ * Simple C functions to supplement the C library ++ * ++ * Copyright (c) 2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "qemu/bswap.h" ++#include "host/cpuinfo.h" ++ ++typedef bool (*biz_accel_fn)(const void *, size_t); ++ ++static bool buffer_is_zero_int_lt256(const void *buf, size_t len) ++{ ++ uint64_t t; ++ const uint64_t *p, *e; ++ ++ /* ++ * Use unaligned memory access functions to handle ++ * the beginning and end of the buffer. ++ */ ++ if (unlikely(len <= 8)) { ++ return (ldl_he_p(buf) | ldl_he_p(buf + len - 4)) == 0; ++ } ++ ++ t = ldq_he_p(buf) | ldq_he_p(buf + len - 8); ++ p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8); ++ e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8); ++ ++ /* Read 0 to 31 aligned words from the middle. */ ++ while (p < e) { ++ t |= *p++; ++ } ++ return t == 0; ++} ++ ++static bool buffer_is_zero_int_ge256(const void *buf, size_t len) ++{ ++ /* ++ * Use unaligned memory access functions to handle ++ * the beginning and end of the buffer. ++ */ ++ uint64_t t = ldq_he_p(buf) | ldq_he_p(buf + len - 8); ++ const uint64_t *p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8); ++ const uint64_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8); ++ ++ /* Collect a partial block at the tail end. */ ++ t |= e[-7] | e[-6] | e[-5] | e[-4] | e[-3] | e[-2] | e[-1]; ++ ++ /* ++ * Loop over 64 byte blocks. ++ * With the head and tail removed, e - p >= 30, ++ * so the loop must iterate at least 3 times. ++ */ ++ do { ++ if (t) { ++ return false; ++ } ++ t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7]; ++ p += 8; ++ } while (p < e - 7); ++ ++ return t == 0; ++} ++ ++#include "host/bufferiszero.c.inc" ++ ++static biz_accel_fn buffer_is_zero_accel; ++static unsigned accel_index; ++ ++bool buffer_is_zero_ool(const void *buf, size_t len) ++{ ++ if (unlikely(len == 0)) { ++ return true; ++ } ++ if (!buffer_is_zero_sample3(buf, len)) { ++ return false; ++ } ++ /* All bytes are covered for any len <= 3. */ ++ if (unlikely(len <= 3)) { ++ return true; ++ } ++ ++ if (likely(len >= 256)) { ++ return buffer_is_zero_accel(buf, len); ++ } ++ return buffer_is_zero_int_lt256(buf, len); ++} ++ ++bool buffer_is_zero_ge256(const void *buf, size_t len) ++{ ++ return buffer_is_zero_accel(buf, len); ++} ++ ++bool test_buffer_is_zero_next_accel(void) ++{ ++ if (accel_index != 0) { ++ buffer_is_zero_accel = accel_table[--accel_index]; ++ return true; ++ } ++ return false; ++} ++ ++static void __attribute__((constructor)) init_accel(void) ++{ ++ accel_index = best_accel(); ++ buffer_is_zero_accel = accel_table[accel_index]; ++} +diff --git a/qcow2/lib/util/compatfd.c b/qcow2/lib/util/compatfd.c +new file mode 100644 +index 00000000..147e39e2 +--- /dev/null ++++ b/qcow2/lib/util/compatfd.c +@@ -0,0 +1,89 @@ ++/* ++ * signalfd/eventfd compatibility ++ * ++ * Copyright IBM, Corp. 2008 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/thread.h" ++ ++#if defined(CONFIG_SIGNALFD) ++#include ++#endif ++ ++struct sigfd_compat_info { ++ sigset_t mask; ++ int fd; ++}; ++ ++static void *sigwait_compat(void *opaque) ++{ ++ struct sigfd_compat_info *info = opaque; ++ ++ while (1) { ++ int sig; ++ int err; ++ ++ err = sigwait(&info->mask, &sig); ++ if (err != 0) { ++ if (errno == EINTR) { ++ continue; ++ } else { ++ return NULL; ++ } ++ } else { ++ struct qemu_signalfd_siginfo buffer; ++ memset(&buffer, 0, sizeof(buffer)); ++ buffer.ssi_signo = sig; ++ ++ if (qemu_write_full(info->fd, &buffer, sizeof(buffer)) != sizeof(buffer)) { ++ return NULL; ++ } ++ } ++ } ++} ++ ++static int qemu_signalfd_compat(const sigset_t *mask) ++{ ++ struct sigfd_compat_info *info; ++ QemuThread thread; ++ int fds[2]; ++ ++ info = g_malloc(sizeof(*info)); ++ ++ if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { ++ g_free(info); ++ return -1; ++ } ++ ++ memcpy(&info->mask, mask, sizeof(*mask)); ++ info->fd = fds[1]; ++ ++ qemu_thread_create(&thread, "signalfd_compat", sigwait_compat, info, ++ QEMU_THREAD_DETACHED); ++ ++ return fds[0]; ++} ++ ++int qemu_signalfd(const sigset_t *mask) ++{ ++#if defined(CONFIG_SIGNALFD) ++ int ret; ++ ++ ret = signalfd(-1, mask, SFD_CLOEXEC); ++ if (ret != -1) { ++ return ret; ++ } ++#endif ++ ++ return qemu_signalfd_compat(mask); ++} +diff --git a/qcow2/lib/util/coroutine-ucontext.c b/qcow2/lib/util/coroutine-ucontext.c +new file mode 100644 +index 00000000..8ef603d0 +--- /dev/null ++++ b/qcow2/lib/util/coroutine-ucontext.c +@@ -0,0 +1,359 @@ ++/* ++ * ucontext coroutine initialization code ++ * ++ * Copyright (C) 2006 Anthony Liguori ++ * Copyright (C) 2011 Kevin Wolf ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.0 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, see . ++ */ ++ ++/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */ ++#undef _FORTIFY_SOURCE ++#define _FORTIFY_SOURCE 0 ++ ++#include "qemu/osdep.h" ++#include ++#include "qemu/coroutine_int.h" ++#include "qemu/coroutine-tls.h" ++ ++#ifdef CONFIG_VALGRIND_H ++#include ++#endif ++ ++#ifdef QEMU_SANITIZE_ADDRESS ++#ifdef CONFIG_ASAN_IFACE_FIBER ++#define CONFIG_ASAN 1 ++#include ++#endif ++#endif ++ ++#ifdef CONFIG_TSAN ++#include ++#endif ++ ++typedef struct { ++ Coroutine base; ++ void *stack; ++ size_t stack_size; ++#ifdef CONFIG_SAFESTACK ++ /* Need an unsafe stack for each coroutine */ ++ void *unsafe_stack; ++ size_t unsafe_stack_size; ++#endif ++ sigjmp_buf env; ++ ++#ifdef CONFIG_TSAN ++ void *tsan_co_fiber; ++ void *tsan_caller_fiber; ++#endif ++ ++#ifdef CONFIG_VALGRIND_H ++ unsigned int valgrind_stack_id; ++#endif ++ ++} CoroutineUContext; ++ ++/** ++ * Per-thread coroutine bookkeeping ++ */ ++QEMU_DEFINE_STATIC_CO_TLS(Coroutine *, current); ++QEMU_DEFINE_STATIC_CO_TLS(CoroutineUContext, leader); ++ ++/* ++ * va_args to makecontext() must be type 'int', so passing ++ * the pointer we need may require several int args. This ++ * union is a quick hack to let us do that ++ */ ++union cc_arg { ++ void *p; ++ int i[2]; ++}; ++ ++/* ++ * QEMU_ALWAYS_INLINE only does so if __OPTIMIZE__, so we cannot use it. ++ * always_inline is required to avoid TSan runtime fatal errors. ++ */ ++static inline __attribute__((always_inline)) ++void on_new_fiber(CoroutineUContext *co) ++{ ++#ifdef CONFIG_TSAN ++ co->tsan_co_fiber = __tsan_create_fiber(0); /* flags: sync on switch */ ++ co->tsan_caller_fiber = __tsan_get_current_fiber(); ++#endif ++} ++ ++/* always_inline is required to avoid TSan runtime fatal errors. */ ++static inline __attribute__((always_inline)) ++void finish_switch_fiber(void *fake_stack_save) ++{ ++#ifdef CONFIG_ASAN ++ CoroutineUContext *leaderp = get_ptr_leader(); ++ const void *bottom_old; ++ size_t size_old; ++ ++ __sanitizer_finish_switch_fiber(fake_stack_save, &bottom_old, &size_old); ++ ++ if (!leaderp->stack) { ++ leaderp->stack = (void *)bottom_old; ++ leaderp->stack_size = size_old; ++ } ++#endif ++#ifdef CONFIG_TSAN ++ if (fake_stack_save) { ++ __tsan_release(fake_stack_save); ++ __tsan_switch_to_fiber(fake_stack_save, 0); /* 0=synchronize */ ++ } ++#endif ++} ++ ++/* always_inline is required to avoid TSan runtime fatal errors. */ ++static inline __attribute__((always_inline)) ++void start_switch_fiber_asan(void **fake_stack_save, ++ const void *bottom, size_t size) ++{ ++#ifdef CONFIG_ASAN ++ __sanitizer_start_switch_fiber(fake_stack_save, bottom, size); ++#endif ++} ++ ++/* always_inline is required to avoid TSan runtime fatal errors. */ ++static inline __attribute__((always_inline)) ++void start_switch_fiber_tsan(void **fake_stack_save, ++ CoroutineUContext *co, ++ bool caller) ++{ ++#ifdef CONFIG_TSAN ++ void *new_fiber = caller ? ++ co->tsan_caller_fiber : ++ co->tsan_co_fiber; ++ void *curr_fiber = __tsan_get_current_fiber(); ++ __tsan_acquire(curr_fiber); ++ ++ *fake_stack_save = curr_fiber; ++ __tsan_switch_to_fiber(new_fiber, 0); /* 0=synchronize */ ++#endif ++} ++ ++static void coroutine_trampoline(int i0, int i1) ++{ ++ union cc_arg arg; ++ CoroutineUContext *self; ++ Coroutine *co; ++ void *fake_stack_save = NULL; ++ ++ finish_switch_fiber(NULL); ++ ++ arg.i[0] = i0; ++ arg.i[1] = i1; ++ self = arg.p; ++ co = &self->base; ++ ++ /* Initialize longjmp environment and switch back the caller */ ++ if (!sigsetjmp(self->env, 0)) { ++ CoroutineUContext *leaderp = get_ptr_leader(); ++ ++ start_switch_fiber_asan(&fake_stack_save, ++ leaderp->stack, leaderp->stack_size); ++ start_switch_fiber_tsan(&fake_stack_save, self, true); /* true=caller */ ++ siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); ++ } ++ ++ finish_switch_fiber(fake_stack_save); ++ ++ while (true) { ++ co->entry(co->entry_arg); ++ qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); ++ } ++} ++ ++Coroutine *qemu_coroutine_new(void) ++{ ++ CoroutineUContext *co; ++ ucontext_t old_uc, uc; ++ sigjmp_buf old_env; ++ union cc_arg arg = {0}; ++ void *fake_stack_save = NULL; ++ ++ /* The ucontext functions preserve signal masks which incurs a ++ * system call overhead. sigsetjmp(buf, 0)/siglongjmp() does not ++ * preserve signal masks but only works on the current stack. ++ * Since we need a way to create and switch to a new stack, use ++ * the ucontext functions for that but sigsetjmp()/siglongjmp() for ++ * everything else. ++ */ ++ ++ if (getcontext(&uc) == -1) { ++ abort(); ++ } ++ ++ co = g_malloc0(sizeof(*co)); ++ co->stack_size = COROUTINE_STACK_SIZE; ++ co->stack = qemu_alloc_stack(&co->stack_size); ++#ifdef CONFIG_SAFESTACK ++ co->unsafe_stack_size = COROUTINE_STACK_SIZE; ++ co->unsafe_stack = qemu_alloc_stack(&co->unsafe_stack_size); ++#endif ++ co->base.entry_arg = &old_env; /* stash away our jmp_buf */ ++ ++ uc.uc_link = &old_uc; ++ uc.uc_stack.ss_sp = co->stack; ++ uc.uc_stack.ss_size = co->stack_size; ++ uc.uc_stack.ss_flags = 0; ++ ++#ifdef CONFIG_VALGRIND_H ++ co->valgrind_stack_id = ++ VALGRIND_STACK_REGISTER(co->stack, co->stack + co->stack_size); ++#endif ++ ++ arg.p = co; ++ ++ on_new_fiber(co); ++ makecontext(&uc, (void (*)(void))coroutine_trampoline, ++ 2, arg.i[0], arg.i[1]); ++ ++ /* swapcontext() in, siglongjmp() back out */ ++ if (!sigsetjmp(old_env, 0)) { ++ start_switch_fiber_asan(&fake_stack_save, co->stack, co->stack_size); ++ start_switch_fiber_tsan(&fake_stack_save, ++ co, false); /* false=not caller */ ++ ++#ifdef CONFIG_SAFESTACK ++ /* ++ * Before we swap the context, set the new unsafe stack ++ * The unsafe stack grows just like the normal stack, so start from ++ * the last usable location of the memory area. ++ * NOTE: we don't have to re-set the usp afterwards because we are ++ * coming back to this context through a siglongjmp. ++ * The compiler already wrapped the corresponding sigsetjmp call with ++ * code that saves the usp on the (safe) stack before the call, and ++ * restores it right after (which is where we return with siglongjmp). ++ */ ++ void *usp = co->unsafe_stack + co->unsafe_stack_size; ++ __safestack_unsafe_stack_ptr = usp; ++#endif ++ ++ swapcontext(&old_uc, &uc); ++ } ++ ++ finish_switch_fiber(fake_stack_save); ++ ++ return &co->base; ++} ++ ++#ifdef CONFIG_VALGRIND_H ++/* Work around an unused variable in the valgrind.h macro... */ ++#if !defined(__clang__) ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wunused-but-set-variable" ++#endif ++static inline void valgrind_stack_deregister(CoroutineUContext *co) ++{ ++ VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); ++} ++#if !defined(__clang__) ++#pragma GCC diagnostic pop ++#endif ++#endif ++ ++#if defined(CONFIG_ASAN) && defined(CONFIG_COROUTINE_POOL) ++static void coroutine_fn terminate_asan(void *opaque) ++{ ++ CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, opaque); ++ ++ set_current(opaque); ++ start_switch_fiber_asan(NULL, to->stack, to->stack_size); ++ G_STATIC_ASSERT(!IS_ENABLED(CONFIG_TSAN)); ++ siglongjmp(to->env, COROUTINE_ENTER); ++} ++#endif ++ ++void qemu_coroutine_delete(Coroutine *co_) ++{ ++ CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); ++ ++#if defined(CONFIG_ASAN) && defined(CONFIG_COROUTINE_POOL) ++ co_->entry_arg = qemu_coroutine_self(); ++ co_->entry = terminate_asan; ++ qemu_coroutine_switch(co_->entry_arg, co_, COROUTINE_ENTER); ++#endif ++ ++#ifdef CONFIG_VALGRIND_H ++ valgrind_stack_deregister(co); ++#endif ++ ++ qemu_free_stack(co->stack, co->stack_size); ++#ifdef CONFIG_SAFESTACK ++ qemu_free_stack(co->unsafe_stack, co->unsafe_stack_size); ++#endif ++ g_free(co); ++} ++ ++/* This function is marked noinline to prevent GCC from inlining it ++ * into coroutine_trampoline(). If we allow it to do that then it ++ * hoists the code to get the address of the TLS variable "current" ++ * out of the while() loop. This is an invalid transformation because ++ * the sigsetjmp() call may be called when running thread A but ++ * return in thread B, and so we might be in a different thread ++ * context each time round the loop. ++ */ ++CoroutineAction __attribute__((noinline)) ++qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, ++ CoroutineAction action) ++{ ++ CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_); ++ CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_); ++ int ret; ++ void *fake_stack_save = NULL; ++ ++ set_current(to_); ++ ++ ret = sigsetjmp(from->env, 0); ++ if (ret == 0) { ++ start_switch_fiber_asan(IS_ENABLED(CONFIG_COROUTINE_POOL) || ++ action != COROUTINE_TERMINATE ? ++ &fake_stack_save : NULL, ++ to->stack, to->stack_size); ++ start_switch_fiber_tsan(&fake_stack_save, ++ to, false); /* false=not caller */ ++ siglongjmp(to->env, action); ++ } ++ ++ finish_switch_fiber(fake_stack_save); ++ ++ return ret; ++} ++ ++Coroutine *qemu_coroutine_self(void) ++{ ++ Coroutine *self = get_current(); ++ CoroutineUContext *leaderp = get_ptr_leader(); ++ ++ if (!self) { ++ self = &leaderp->base; ++ set_current(self); ++ } ++#ifdef CONFIG_TSAN ++ if (!leaderp->tsan_co_fiber) { ++ leaderp->tsan_co_fiber = __tsan_get_current_fiber(); ++ } ++#endif ++ return self; ++} ++ ++bool qemu_in_coroutine(void) ++{ ++ Coroutine *self = get_current(); ++ ++ return self && self->caller; ++} +diff --git a/qcow2/lib/util/cutils.c b/qcow2/lib/util/cutils.c +new file mode 100644 +index 00000000..42364039 +--- /dev/null ++++ b/qcow2/lib/util/cutils.c +@@ -0,0 +1,1218 @@ ++/* ++ * Simple C functions to supplement the C library ++ * ++ * Copyright (c) 2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/host-utils.h" ++#include ++ ++#ifdef __FreeBSD__ ++#include ++#include ++#endif ++ ++#ifdef __NetBSD__ ++#include ++#endif ++ ++#ifdef __HAIKU__ ++#include ++#endif ++ ++#ifdef __APPLE__ ++#include ++#endif ++ ++#ifdef G_OS_WIN32 ++#include ++#include ++#endif ++ ++#include "qemu/ctype.h" ++#include "qemu/cutils.h" ++#include "qemu/error-report.h" ++ ++void strpadcpy(char *buf, int buf_size, const char *str, char pad) ++{ ++ int len = qemu_strnlen(str, buf_size); ++ memcpy(buf, str, len); ++ memset(buf + len, pad, buf_size - len); ++} ++ ++void pstrcpy(char *buf, int buf_size, const char *str) ++{ ++ int c; ++ char *q = buf; ++ ++ if (buf_size <= 0) ++ return; ++ ++ for(;;) { ++ c = *str++; ++ if (c == 0 || q >= buf + buf_size - 1) ++ break; ++ *q++ = c; ++ } ++ *q = '\0'; ++} ++ ++/* strcat and truncate. */ ++char *pstrcat(char *buf, int buf_size, const char *s) ++{ ++ int len; ++ len = strlen(buf); ++ if (len < buf_size) ++ pstrcpy(buf + len, buf_size - len, s); ++ return buf; ++} ++ ++int strstart(const char *str, const char *val, const char **ptr) ++{ ++ const char *p, *q; ++ p = str; ++ q = val; ++ while (*q != '\0') { ++ if (*p != *q) ++ return 0; ++ p++; ++ q++; ++ } ++ if (ptr) ++ *ptr = p; ++ return 1; ++} ++ ++int stristart(const char *str, const char *val, const char **ptr) ++{ ++ const char *p, *q; ++ p = str; ++ q = val; ++ while (*q != '\0') { ++ if (qemu_toupper(*p) != qemu_toupper(*q)) ++ return 0; ++ p++; ++ q++; ++ } ++ if (ptr) ++ *ptr = p; ++ return 1; ++} ++ ++/* XXX: use host strnlen if available ? */ ++int qemu_strnlen(const char *s, int max_len) ++{ ++ int i; ++ ++ for(i = 0; i < max_len; i++) { ++ if (s[i] == '\0') { ++ break; ++ } ++ } ++ return i; ++} ++ ++char *qemu_strsep(char **input, const char *delim) ++{ ++ char *result = *input; ++ if (result != NULL) { ++ char *p; ++ ++ for (p = result; *p != '\0'; p++) { ++ if (strchr(delim, *p)) { ++ break; ++ } ++ } ++ if (*p == '\0') { ++ *input = NULL; ++ } else { ++ *p = '\0'; ++ *input = p + 1; ++ } ++ } ++ return result; ++} ++ ++time_t mktimegm(struct tm *tm) ++{ ++ time_t t; ++ int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday; ++ if (m < 3) { ++ m += 12; ++ y--; ++ } ++ t = 86400ULL * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + ++ y / 400 - 719469); ++ t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec; ++ return t; ++} ++ ++static int64_t suffix_mul(char suffix, int64_t unit) ++{ ++ switch (qemu_toupper(suffix)) { ++ case 'B': ++ return 1; ++ case 'K': ++ return unit; ++ case 'M': ++ return unit * unit; ++ case 'G': ++ return unit * unit * unit; ++ case 'T': ++ return unit * unit * unit * unit; ++ case 'P': ++ return unit * unit * unit * unit * unit; ++ case 'E': ++ return unit * unit * unit * unit * unit * unit; ++ } ++ return -1; ++} ++ ++/* ++ * Convert size string to bytes. ++ * ++ * The size parsing supports the following syntaxes ++ * - 12345 - decimal, scale determined by @default_suffix and @unit ++ * - 12345{bBkKmMgGtTpPeE} - decimal, scale determined by suffix and @unit ++ * - 12345.678{kKmMgGtTpPeE} - decimal, scale determined by suffix, and ++ * fractional portion is truncated to byte, either side of . may be empty ++ * - 0x7fEE - hexadecimal, unit determined by @default_suffix ++ * ++ * The following are intentionally not supported ++ * - hex with scaling suffix, such as 0x20M or 0x1p3 (both fail with ++ * -EINVAL), while 0x1b is 27 (not 1 with byte scale) ++ * - octal, such as 08 (parsed as decimal instead) ++ * - binary, such as 0b1000 (parsed as 0b with trailing garbage "1000") ++ * - fractional hex, such as 0x1.8 (parsed as 0 with trailing garbage "x1.8") ++ * - negative values, including -0 (fail with -ERANGE) ++ * - floating point exponents, such as 1e3 (parsed as 1e with trailing ++ * garbage "3") or 0x1p3 (rejected as hex with scaling suffix) ++ * - non-finite values, such as inf or NaN (fail with -EINVAL) ++ * ++ * The end pointer will be returned in *end, if not NULL. If there is ++ * no fraction, the input can be decimal or hexadecimal; if there is a ++ * non-zero fraction, then the input must be decimal and there must be ++ * a suffix (possibly by @default_suffix) larger than Byte, and the ++ * fractional portion may suffer from precision loss or rounding. The ++ * input must be positive. ++ * ++ * Return -ERANGE on overflow (with *@end advanced), and -EINVAL on ++ * other error (with *@end at @nptr). Unlike strtoull, *@result is ++ * set to 0 on all errors, as returning UINT64_MAX on overflow is less ++ * likely to be usable as a size. ++ */ ++static int do_strtosz(const char *nptr, const char **end, ++ const char default_suffix, int64_t unit, ++ uint64_t *result) ++{ ++ int retval; ++ const char *endptr; ++ unsigned char c; ++ uint64_t val = 0, valf = 0; ++ int64_t mul; ++ ++ /* Parse integral portion as decimal. */ ++ retval = parse_uint(nptr, &endptr, 10, &val); ++ if (retval == -ERANGE || !nptr) { ++ goto out; ++ } ++ if (retval == 0 && val == 0 && (*endptr == 'x' || *endptr == 'X')) { ++ /* Input looks like hex; reparse, and insist on no fraction or suffix. */ ++ retval = qemu_strtou64(nptr, &endptr, 16, &val); ++ if (retval) { ++ goto out; ++ } ++ if (*endptr == '.' || suffix_mul(*endptr, unit) > 0) { ++ endptr = nptr; ++ retval = -EINVAL; ++ goto out; ++ } ++ } else if (*endptr == '.' || (endptr == nptr && strchr(nptr, '.'))) { ++ /* ++ * Input looks like a fraction. Make sure even 1.k works ++ * without fractional digits. strtod tries to treat 'e' as an ++ * exponent, but we want to treat it as a scaling suffix; ++ * doing this requires modifying a copy of the fraction. ++ */ ++ double fraction = 0.0; ++ ++ if (retval == 0 && *endptr == '.' && !isdigit(endptr[1])) { ++ /* If we got here, we parsed at least one digit already. */ ++ endptr++; ++ } else { ++ char *e; ++ const char *tail; ++ g_autofree char *copy = g_strdup(endptr); ++ ++ e = strchr(copy, 'e'); ++ if (e) { ++ *e = '\0'; ++ } ++ e = strchr(copy, 'E'); ++ if (e) { ++ *e = '\0'; ++ } ++ /* ++ * If this is a floating point, we are guaranteed that '.' ++ * appears before any possible digits in copy. If it is ++ * not a floating point, strtod will fail. Either way, ++ * there is now no exponent in copy, so if it parses, we ++ * know 0.0 <= abs(result) <= 1.0 (after rounding), and ++ * ERANGE is only possible on underflow which is okay. ++ */ ++ retval = qemu_strtod_finite(copy, &tail, &fraction); ++ endptr += tail - copy; ++ if (signbit(fraction)) { ++ retval = -ERANGE; ++ goto out; ++ } ++ } ++ ++ /* Extract into a 64-bit fixed-point fraction. */ ++ if (fraction == 1.0) { ++ if (val == UINT64_MAX) { ++ retval = -ERANGE; ++ goto out; ++ } ++ val++; ++ } else if (retval == -ERANGE) { ++ /* See comments above about underflow */ ++ valf = 1; ++ retval = 0; ++ } else { ++ /* We want non-zero valf for any non-zero fraction */ ++ valf = (uint64_t)(fraction * 0x1p64); ++ if (valf == 0 && fraction > 0.0) { ++ valf = 1; ++ } ++ } ++ } ++ if (retval) { ++ goto out; ++ } ++ c = *endptr; ++ mul = suffix_mul(c, unit); ++ if (mul > 0) { ++ endptr++; ++ } else { ++ mul = suffix_mul(default_suffix, unit); ++ assert(mul > 0); ++ } ++ if (mul == 1) { ++ /* When a fraction is present, a scale is required. */ ++ if (valf != 0) { ++ endptr = nptr; ++ retval = -EINVAL; ++ goto out; ++ } ++ } else { ++ uint64_t valh, tmp; ++ ++ /* Compute exact result: 64.64 x 64.0 -> 128.64 fixed point */ ++ mulu64(&val, &valh, val, mul); ++ mulu64(&valf, &tmp, valf, mul); ++ val += tmp; ++ valh += val < tmp; ++ ++ /* Round 0.5 upward. */ ++ tmp = valf >> 63; ++ val += tmp; ++ valh += val < tmp; ++ ++ /* Report overflow. */ ++ if (valh != 0) { ++ retval = -ERANGE; ++ goto out; ++ } ++ } ++ ++ retval = 0; ++ ++out: ++ if (end) { ++ *end = endptr; ++ } else if (nptr && *endptr) { ++ retval = -EINVAL; ++ } ++ if (retval == 0) { ++ *result = val; ++ } else { ++ *result = 0; ++ if (end && retval == -EINVAL) { ++ *end = nptr; ++ } ++ } ++ ++ return retval; ++} ++ ++int qemu_strtosz(const char *nptr, const char **end, uint64_t *result) ++{ ++ return do_strtosz(nptr, end, 'B', 1024, result); ++} ++ ++int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result) ++{ ++ return do_strtosz(nptr, end, 'M', 1024, result); ++} ++ ++int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result) ++{ ++ return do_strtosz(nptr, end, 'B', 1000, result); ++} ++ ++/** ++ * Helper function for error checking after strtol() and the like ++ */ ++static int check_strtox_error(const char *nptr, char *ep, ++ const char **endptr, bool check_zero, ++ int libc_errno) ++{ ++ assert(ep >= nptr); ++ ++ /* Windows has a bug in that it fails to parse 0 from "0x" in base 16 */ ++ if (check_zero && ep == nptr && libc_errno == 0) { ++ char *tmp; ++ ++ errno = 0; ++ if (strtol(nptr, &tmp, 10) == 0 && errno == 0 && ++ (*tmp == 'x' || *tmp == 'X')) { ++ ep = tmp; ++ } ++ } ++ ++ if (endptr) { ++ *endptr = ep; ++ } ++ ++ /* Turn "no conversion" into an error */ ++ if (libc_errno == 0 && ep == nptr) { ++ return -EINVAL; ++ } ++ ++ /* Fail when we're expected to consume the string, but didn't */ ++ if (!endptr && *ep) { ++ return -EINVAL; ++ } ++ ++ return -libc_errno; ++} ++ ++/** ++ * Convert string @nptr to an integer, and store it in @result. ++ * ++ * This is a wrapper around strtol() that is harder to misuse. ++ * Semantics of @nptr, @endptr, @base match strtol() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, 0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows @result, store INT_MAX in @result, ++ * and return -ERANGE. ++ * ++ * If the conversion underflows @result, store INT_MIN in @result, ++ * and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ * ++ * This matches the behavior of strtol() on 32-bit platforms, even on ++ * platforms where long is 64-bits. ++ */ ++int qemu_strtoi(const char *nptr, const char **endptr, int base, ++ int *result) ++{ ++ char *ep; ++ long long lresult; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ lresult = strtoll(nptr, &ep, base); ++ if (lresult < INT_MIN) { ++ *result = INT_MIN; ++ errno = ERANGE; ++ } else if (lresult > INT_MAX) { ++ *result = INT_MAX; ++ errno = ERANGE; ++ } else { ++ *result = lresult; ++ } ++ return check_strtox_error(nptr, ep, endptr, lresult == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to an unsigned integer, and store it in @result. ++ * ++ * This is a wrapper around strtoul() that is harder to misuse. ++ * Semantics of @nptr, @endptr, @base match strtoul() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, 0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows @result, store UINT_MAX in @result, ++ * and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ * ++ * Note that a number with a leading minus sign gets converted without ++ * the minus sign, checked for overflow (see above), then negated (in ++ * @result's type). This matches the behavior of strtoul() on 32-bit ++ * platforms, even on platforms where long is 64-bits. ++ */ ++int qemu_strtoui(const char *nptr, const char **endptr, int base, ++ unsigned int *result) ++{ ++ char *ep; ++ unsigned long long lresult; ++ bool neg; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ lresult = strtoull(nptr, &ep, base); ++ ++ /* Windows returns 1 for negative out-of-range values. */ ++ if (errno == ERANGE) { ++ *result = -1; ++ } else { ++ /* ++ * Note that platforms with 32-bit strtoul only accept input ++ * in the range [-4294967295, 4294967295]; but we used 64-bit ++ * strtoull which wraps -18446744073709551615 to 1 instead of ++ * declaring overflow. So we must check if '-' was parsed, ++ * and if so, undo the negation before doing our bounds check. ++ */ ++ neg = memchr(nptr, '-', ep - nptr) != NULL; ++ if (neg) { ++ lresult = -lresult; ++ } ++ if (lresult > UINT_MAX) { ++ *result = UINT_MAX; ++ errno = ERANGE; ++ } else { ++ *result = neg ? -lresult : lresult; ++ } ++ } ++ return check_strtox_error(nptr, ep, endptr, lresult == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to a long integer, and store it in @result. ++ * ++ * This is a wrapper around strtol() that is harder to misuse. ++ * Semantics of @nptr, @endptr, @base match strtol() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, 0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows @result, store LONG_MAX in @result, ++ * and return -ERANGE. ++ * ++ * If the conversion underflows @result, store LONG_MIN in @result, ++ * and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ */ ++int qemu_strtol(const char *nptr, const char **endptr, int base, ++ long *result) ++{ ++ char *ep; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ *result = strtol(nptr, &ep, base); ++ return check_strtox_error(nptr, ep, endptr, *result == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to an unsigned long, and store it in @result. ++ * ++ * This is a wrapper around strtoul() that is harder to misuse. ++ * Semantics of @nptr, @endptr, @base match strtoul() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, 0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows @result, store ULONG_MAX in @result, ++ * and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ * ++ * Note that a number with a leading minus sign gets converted without ++ * the minus sign, checked for overflow (see above), then negated (in ++ * @result's type). This is exactly how strtoul() works. ++ */ ++int qemu_strtoul(const char *nptr, const char **endptr, int base, ++ unsigned long *result) ++{ ++ char *ep; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ *result = strtoul(nptr, &ep, base); ++ /* Windows returns 1 for negative out-of-range values. */ ++ if (errno == ERANGE) { ++ *result = -1; ++ } ++ return check_strtox_error(nptr, ep, endptr, *result == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to an int64_t. ++ * ++ * Works like qemu_strtol(), except it stores INT64_MAX on overflow, ++ * and INT64_MIN on underflow. ++ */ ++int qemu_strtoi64(const char *nptr, const char **endptr, int base, ++ int64_t *result) ++{ ++ char *ep; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ /* This assumes int64_t is long long TODO relax */ ++ QEMU_BUILD_BUG_ON(sizeof(int64_t) != sizeof(long long)); ++ errno = 0; ++ *result = strtoll(nptr, &ep, base); ++ return check_strtox_error(nptr, ep, endptr, *result == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to an uint64_t. ++ * ++ * Works like qemu_strtoul(), except it stores UINT64_MAX on overflow. ++ * (If you want to prohibit negative numbers that wrap around to ++ * positive, use parse_uint()). ++ */ ++int qemu_strtou64(const char *nptr, const char **endptr, int base, ++ uint64_t *result) ++{ ++ char *ep; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!nptr) { ++ *result = 0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ /* This assumes uint64_t is unsigned long long TODO relax */ ++ QEMU_BUILD_BUG_ON(sizeof(uint64_t) != sizeof(unsigned long long)); ++ errno = 0; ++ *result = strtoull(nptr, &ep, base); ++ /* Windows returns 1 for negative out-of-range values. */ ++ if (errno == ERANGE) { ++ *result = -1; ++ } ++ return check_strtox_error(nptr, ep, endptr, *result == 0, errno); ++} ++ ++/** ++ * Convert string @nptr to a double. ++ * ++ * This is a wrapper around strtod() that is harder to misuse. ++ * Semantics of @nptr and @endptr match strtod() with differences ++ * noted below. ++ * ++ * @nptr may be null, and no conversion is performed then. ++ * ++ * If no conversion is performed, store @nptr in *@endptr, +0.0 in ++ * @result, and return -EINVAL. ++ * ++ * If @endptr is null, and the string isn't fully converted, return ++ * -EINVAL with @result set to the parsed value. This is the case ++ * when the pointer that would be stored in a non-null @endptr points ++ * to a character other than '\0'. ++ * ++ * If the conversion overflows, store +/-HUGE_VAL in @result, depending ++ * on the sign, and return -ERANGE. ++ * ++ * If the conversion underflows, store +/-0.0 in @result, depending on the ++ * sign, and return -ERANGE. ++ * ++ * Else store the converted value in @result, and return zero. ++ */ ++int qemu_strtod(const char *nptr, const char **endptr, double *result) ++{ ++ char *ep; ++ ++ if (!nptr) { ++ *result = 0.0; ++ if (endptr) { ++ *endptr = nptr; ++ } ++ return -EINVAL; ++ } ++ ++ errno = 0; ++ *result = strtod(nptr, &ep); ++ return check_strtox_error(nptr, ep, endptr, false, errno); ++} ++ ++/** ++ * Convert string @nptr to a finite double. ++ * ++ * Works like qemu_strtod(), except that "NaN", "inf", and strings ++ * that cause ERANGE overflow errors are rejected with -EINVAL as if ++ * no conversion is performed, storing 0.0 into @result regardless of ++ * any sign. -ERANGE failures for underflow still preserve the parsed ++ * sign. ++ */ ++int qemu_strtod_finite(const char *nptr, const char **endptr, double *result) ++{ ++ const char *tmp; ++ int ret; ++ ++ ret = qemu_strtod(nptr, &tmp, result); ++ if (!isfinite(*result)) { ++ if (endptr) { ++ *endptr = nptr; ++ } ++ *result = 0.0; ++ ret = -EINVAL; ++ } else if (endptr) { ++ *endptr = tmp; ++ } else if (*tmp) { ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++/** ++ * Searches for the first occurrence of 'c' in 's', and returns a pointer ++ * to the trailing null byte if none was found. ++ */ ++#ifndef HAVE_STRCHRNUL ++const char *qemu_strchrnul(const char *s, int c) ++{ ++ const char *e = strchr(s, c); ++ if (!e) { ++ e = s + strlen(s); ++ } ++ return e; ++} ++#endif ++ ++/** ++ * parse_uint: ++ * ++ * @s: String to parse ++ * @endptr: Destination for pointer to first character not consumed ++ * @base: integer base, between 2 and 36 inclusive, or 0 ++ * @value: Destination for parsed integer value ++ * ++ * Parse unsigned integer ++ * ++ * Parsed syntax is like strtoull()'s: arbitrary whitespace, a single optional ++ * '+' or '-', an optional "0x" if @base is 0 or 16, one or more digits. ++ * ++ * If @s is null, or @s doesn't start with an integer in the syntax ++ * above, set *@value to 0, *@endptr to @s, and return -EINVAL. ++ * ++ * Set *@endptr to point right beyond the parsed integer (even if the integer ++ * overflows or is negative, all digits will be parsed and *@endptr will ++ * point right beyond them). If @endptr is %NULL, any trailing character ++ * instead causes a result of -EINVAL with *@value of 0. ++ * ++ * If the integer is negative, set *@value to 0, and return -ERANGE. ++ * (If you want to allow negative numbers that wrap around within ++ * bounds, use qemu_strtou64()). ++ * ++ * If the integer overflows unsigned long long, set *@value to ++ * ULLONG_MAX, and return -ERANGE. ++ * ++ * Else, set *@value to the parsed integer, and return 0. ++ */ ++int parse_uint(const char *s, const char **endptr, int base, uint64_t *value) ++{ ++ int r = 0; ++ char *endp = (char *)s; ++ unsigned long long val = 0; ++ ++ assert((unsigned) base <= 36 && base != 1); ++ if (!s) { ++ r = -EINVAL; ++ goto out; ++ } ++ ++ errno = 0; ++ val = strtoull(s, &endp, base); ++ if (errno) { ++ r = -errno; ++ goto out; ++ } ++ ++ if (endp == s) { ++ r = -EINVAL; ++ goto out; ++ } ++ ++ /* make sure we reject negative numbers: */ ++ while (qemu_isspace(*s)) { ++ s++; ++ } ++ if (*s == '-') { ++ val = 0; ++ r = -ERANGE; ++ goto out; ++ } ++ ++out: ++ *value = val; ++ if (endptr) { ++ *endptr = endp; ++ } else if (s && *endp) { ++ r = -EINVAL; ++ *value = 0; ++ } ++ return r; ++} ++ ++/** ++ * parse_uint_full: ++ * ++ * @s: String to parse ++ * @base: integer base, between 2 and 36 inclusive, or 0 ++ * @value: Destination for parsed integer value ++ * ++ * Parse unsigned integer from entire string, rejecting any trailing slop. ++ * ++ * Shorthand for parse_uint(s, NULL, base, value). ++ */ ++int parse_uint_full(const char *s, int base, uint64_t *value) ++{ ++ return parse_uint(s, NULL, base, value); ++} ++ ++int qemu_parse_fd(const char *param) ++{ ++ long fd; ++ char *endptr; ++ ++ errno = 0; ++ fd = strtol(param, &endptr, 10); ++ if (param == endptr /* no conversion performed */ || ++ errno != 0 /* not representable as long; possibly others */ || ++ *endptr != '\0' /* final string not empty */ || ++ fd < 0 /* invalid as file descriptor */ || ++ fd > INT_MAX /* not representable as int */) { ++ return -1; ++ } ++ return fd; ++} ++ ++/* ++ * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128) ++ * Input is limited to 14-bit numbers ++ */ ++int uleb128_encode_small(uint8_t *out, uint32_t n) ++{ ++ g_assert(n <= 0x3fff); ++ if (n < 0x80) { ++ *out = n; ++ return 1; ++ } else { ++ *out++ = (n & 0x7f) | 0x80; ++ *out = n >> 7; ++ return 2; ++ } ++} ++ ++int uleb128_decode_small(const uint8_t *in, uint32_t *n) ++{ ++ if (!(*in & 0x80)) { ++ *n = *in; ++ return 1; ++ } else { ++ *n = *in++ & 0x7f; ++ /* we exceed 14 bit number */ ++ if (*in & 0x80) { ++ return -1; ++ } ++ *n |= *in << 7; ++ return 2; ++ } ++} ++ ++/* ++ * helper to parse debug environment variables ++ */ ++int parse_debug_env(const char *name, int max, int initial) ++{ ++ char *debug_env = getenv(name); ++ char *inv = NULL; ++ long debug; ++ ++ if (!debug_env) { ++ return initial; ++ } ++ errno = 0; ++ debug = strtol(debug_env, &inv, 10); ++ if (inv == debug_env) { ++ return initial; ++ } ++ if (debug < 0 || debug > max || errno != 0) { ++ warn_report("%s not in [0, %d]", name, max); ++ return initial; ++ } ++ return debug; ++} ++ ++const char *si_prefix(unsigned int exp10) ++{ ++ static const char *prefixes[] = { ++ "a", "f", "p", "n", "u", "m", "", "K", "M", "G", "T", "P", "E" ++ }; ++ ++ exp10 += 18; ++ assert(exp10 % 3 == 0 && exp10 / 3 < ARRAY_SIZE(prefixes)); ++ return prefixes[exp10 / 3]; ++} ++ ++const char *iec_binary_prefix(unsigned int exp2) ++{ ++ static const char *prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; ++ ++ assert(exp2 % 10 == 0 && exp2 / 10 < ARRAY_SIZE(prefixes)); ++ return prefixes[exp2 / 10]; ++} ++ ++/* ++ * Return human readable string for size @val. ++ * @val can be anything that uint64_t allows (no more than "16 EiB"). ++ * Use IEC binary units like KiB, MiB, and so forth. ++ * Caller is responsible for passing it to g_free(). ++ */ ++char *size_to_str(uint64_t val) ++{ ++ uint64_t div; ++ int i; ++ ++ /* ++ * The exponent (returned in i) minus one gives us ++ * floor(log2(val * 1024 / 1000). The correction makes us ++ * switch to the higher power when the integer part is >= 1000. ++ * (see e41b509d68afb1f for more info) ++ */ ++ frexp(val / (1000.0 / 1024.0), &i); ++ i = (i - 1) / 10 * 10; ++ div = 1ULL << i; ++ ++ return g_strdup_printf("%0.3g %sB", (double)val / div, iec_binary_prefix(i)); ++} ++ ++char *freq_to_str(uint64_t freq_hz) ++{ ++ double freq = freq_hz; ++ size_t exp10 = 0; ++ ++ while (freq >= 1000.0) { ++ freq /= 1000.0; ++ exp10 += 3; ++ } ++ ++ return g_strdup_printf("%0.3g %sHz", freq, si_prefix(exp10)); ++} ++ ++int qemu_pstrcmp0(const char **str1, const char **str2) ++{ ++ return g_strcmp0(*str1, *str2); ++} ++ ++static inline bool starts_with_prefix(const char *dir) ++{ ++ size_t prefix_len = strlen(CONFIG_PREFIX); ++ /* ++ * dir[prefix_len] is only accessed if the length of dir is ++ * >= prefix_len, so no out of bounds access is possible. ++ */ ++#pragma GCC diagnostic push ++#if !defined(__clang__) || __has_warning("-Warray-bounds=") ++#pragma GCC diagnostic ignored "-Warray-bounds=" ++#endif ++ return !memcmp(dir, CONFIG_PREFIX, prefix_len) && ++ (!dir[prefix_len] || G_IS_DIR_SEPARATOR(dir[prefix_len])); ++#pragma GCC diagnostic pop ++} ++ ++/* Return the next path component in dir, and store its length in *p_len. */ ++static inline const char *next_component(const char *dir, int *p_len) ++{ ++ int len; ++ while ((*dir && G_IS_DIR_SEPARATOR(*dir)) || ++ (*dir == '.' && (G_IS_DIR_SEPARATOR(dir[1]) || dir[1] == '\0'))) { ++ dir++; ++ } ++ len = 0; ++ while (dir[len] && !G_IS_DIR_SEPARATOR(dir[len])) { ++ len++; ++ } ++ *p_len = len; ++ return dir; ++} ++ ++static const char *exec_dir; ++ ++void qemu_init_exec_dir(const char *argv0) ++{ ++#ifdef G_OS_WIN32 ++ char *p; ++ char buf[MAX_PATH]; ++ DWORD len; ++ ++ if (exec_dir) { ++ return; ++ } ++ ++ len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); ++ if (len == 0) { ++ return; ++ } ++ ++ buf[len] = 0; ++ p = buf + len - 1; ++ while (p != buf && *p != '\\') { ++ p--; ++ } ++ *p = 0; ++ if (access(buf, R_OK) == 0) { ++ exec_dir = g_strdup(buf); ++ } else { ++ exec_dir = CONFIG_BINDIR; ++ } ++#else ++ char *p = NULL; ++ char buf[PATH_MAX]; ++ ++ if (exec_dir) { ++ return; ++ } ++ ++#if defined(__linux__) ++ { ++ int len; ++ len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); ++ if (len > 0) { ++ buf[len] = 0; ++ p = buf; ++ } ++ } ++#elif defined(__FreeBSD__) \ ++ || (defined(__NetBSD__) && defined(KERN_PROC_PATHNAME)) ++ { ++#if defined(__FreeBSD__) ++ static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; ++#else ++ static int mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME}; ++#endif ++ size_t len = sizeof(buf) - 1; ++ ++ *buf = '\0'; ++ if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && ++ *buf) { ++ buf[sizeof(buf) - 1] = '\0'; ++ p = buf; ++ } ++ } ++#elif defined(__APPLE__) ++ { ++ char fpath[PATH_MAX]; ++ uint32_t len = sizeof(fpath); ++ if (_NSGetExecutablePath(fpath, &len) == 0) { ++ p = realpath(fpath, buf); ++ if (!p) { ++ return; ++ } ++ } ++ } ++#elif defined(__HAIKU__) ++ { ++ image_info ii; ++ int32_t c = 0; ++ ++ *buf = '\0'; ++ while (get_next_image_info(0, &c, &ii) == B_OK) { ++ if (ii.type == B_APP_IMAGE) { ++ strncpy(buf, ii.name, sizeof(buf)); ++ buf[sizeof(buf) - 1] = 0; ++ p = buf; ++ break; ++ } ++ } ++ } ++#endif ++ /* If we don't have any way of figuring out the actual executable ++ location then try argv[0]. */ ++ if (!p && argv0) { ++ p = realpath(argv0, buf); ++ } ++ if (p) { ++ exec_dir = g_path_get_dirname(p); ++ } else { ++ exec_dir = CONFIG_BINDIR; ++ } ++#endif ++} ++ ++const char *qemu_get_exec_dir(void) ++{ ++ return exec_dir; ++} ++ ++char *get_relocated_path(const char *dir) ++{ ++ size_t prefix_len = strlen(CONFIG_PREFIX); ++ const char *bindir = CONFIG_BINDIR; ++ GString *result; ++ int len_dir, len_bindir; ++ ++ /* Fail if qemu_init_exec_dir was not called. */ ++ assert(exec_dir[0]); ++ ++ result = g_string_new(exec_dir); ++ g_string_append(result, "/qemu-bundle"); ++ if (access(result->str, R_OK) == 0) { ++#ifdef G_OS_WIN32 ++ const char *src = dir; ++ size_t size = mbsrtowcs(NULL, &src, 0, &(mbstate_t){0}) + 1; ++ PWSTR wdir = g_new(WCHAR, size); ++ mbsrtowcs(wdir, &src, size, &(mbstate_t){0}); ++ ++ PCWSTR wdir_skipped_root; ++ if (PathCchSkipRoot(wdir, &wdir_skipped_root) == S_OK) { ++ size = wcsrtombs(NULL, &wdir_skipped_root, 0, &(mbstate_t){0}); ++ char *cursor = result->str + result->len; ++ g_string_set_size(result, result->len + size); ++ wcsrtombs(cursor, &wdir_skipped_root, size + 1, &(mbstate_t){0}); ++ } else { ++ g_string_append(result, dir); ++ } ++ ++ g_free(wdir); ++#else ++ g_string_append(result, dir); ++#endif ++ goto out; ++ } ++ ++ if (IS_ENABLED(CONFIG_RELOCATABLE) && ++ starts_with_prefix(dir) && starts_with_prefix(bindir)) { ++ g_string_assign(result, exec_dir); ++ ++ /* Advance over common components. */ ++ len_dir = len_bindir = prefix_len; ++ do { ++ dir += len_dir; ++ bindir += len_bindir; ++ dir = next_component(dir, &len_dir); ++ bindir = next_component(bindir, &len_bindir); ++ } while (len_dir && len_dir == len_bindir && !memcmp(dir, bindir, len_dir)); ++ ++ /* Ascend from bindir to the common prefix with dir. */ ++ while (len_bindir) { ++ bindir += len_bindir; ++ g_string_append(result, "/.."); ++ bindir = next_component(bindir, &len_bindir); ++ } ++ ++ if (*dir) { ++ assert(G_IS_DIR_SEPARATOR(dir[-1])); ++ g_string_append(result, dir - 1); ++ } ++ goto out; ++ } ++ ++ g_string_assign(result, dir); ++out: ++ return g_string_free(result, false); ++} +diff --git a/qcow2/lib/util/defer-call.c b/qcow2/lib/util/defer-call.c +new file mode 100644 +index 00000000..037dc0ab +--- /dev/null ++++ b/qcow2/lib/util/defer-call.c +@@ -0,0 +1,156 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Deferred calls ++ * ++ * Copyright Red Hat. ++ * ++ * This API defers a function call within a defer_call_begin()/defer_call_end() ++ * section, allowing multiple calls to batch up. This is a performance ++ * optimization that is used in the block layer to submit several I/O requests ++ * at once instead of individually: ++ * ++ * defer_call_begin(); <-- start of section ++ * ... ++ * defer_call(my_func, my_obj); <-- deferred my_func(my_obj) call ++ * defer_call(my_func, my_obj); <-- another ++ * defer_call(my_func, my_obj); <-- another ++ * ... ++ * defer_call_end(); <-- end of section, my_func(my_obj) is called once ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/coroutine-tls.h" ++#include "qemu/notify.h" ++#include "qemu/thread.h" ++#include "qemu/defer-call.h" ++ ++/* A function call that has been deferred until defer_call_end() */ ++typedef struct { ++ void (*fn)(void *); ++ void *opaque; ++} DeferredCall; ++ ++/* Per-thread state */ ++typedef struct { ++ unsigned nesting_level; ++ GArray *deferred_call_array; ++} DeferCallThreadState; ++ ++/* Use get_ptr_defer_call_thread_state() to fetch this thread-local value */ ++QEMU_DEFINE_STATIC_CO_TLS(DeferCallThreadState, defer_call_thread_state); ++ ++/* Called at thread cleanup time */ ++static void defer_call_atexit(Notifier *n, void *value) ++{ ++ DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); ++ g_array_free(thread_state->deferred_call_array, TRUE); ++} ++ ++/* This won't involve coroutines, so use __thread */ ++static __thread Notifier defer_call_atexit_notifier; ++ ++/** ++ * defer_call: ++ * @fn: a function pointer to be invoked ++ * @opaque: a user-defined argument to @fn() ++ * ++ * Call @fn(@opaque) immediately if not within a ++ * defer_call_begin()/defer_call_end() section. ++ * ++ * Otherwise defer the call until the end of the outermost ++ * defer_call_begin()/defer_call_end() section in this thread. If the same ++ * @fn/@opaque pair has already been deferred, it will only be called once upon ++ * defer_call_end() so that accumulated calls are batched into a single call. ++ * ++ * The caller must ensure that @opaque is not freed before @fn() is invoked. ++ */ ++void defer_call(void (*fn)(void *), void *opaque) ++{ ++ DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); ++ ++ /* Call immediately if we're not deferring calls */ ++ if (thread_state->nesting_level == 0) { ++ fn(opaque); ++ return; ++ } ++ ++ GArray *array = thread_state->deferred_call_array; ++ if (!array) { ++ array = g_array_new(FALSE, FALSE, sizeof(DeferredCall)); ++ thread_state->deferred_call_array = array; ++ defer_call_atexit_notifier.notify = defer_call_atexit; ++ qemu_thread_atexit_add(&defer_call_atexit_notifier); ++ } ++ ++ DeferredCall *fns = (DeferredCall *)array->data; ++ DeferredCall new_fn = { ++ .fn = fn, ++ .opaque = opaque, ++ }; ++ ++ /* ++ * There won't be many, so do a linear search. If this becomes a bottleneck ++ * then a binary search (glib 2.62+) or different data structure could be ++ * used. ++ */ ++ for (guint i = 0; i < array->len; i++) { ++ if (memcmp(&fns[i], &new_fn, sizeof(new_fn)) == 0) { ++ return; /* already exists */ ++ } ++ } ++ ++ g_array_append_val(array, new_fn); ++} ++ ++/** ++ * defer_call_begin: Defer defer_call() functions until defer_call_end() ++ * ++ * defer_call_begin() and defer_call_end() are thread-local operations. The ++ * caller must ensure that each defer_call_begin() has a matching ++ * defer_call_end() in the same thread. ++ * ++ * Nesting is supported. defer_call() functions are only called at the ++ * outermost defer_call_end(). ++ */ ++void defer_call_begin(void) ++{ ++ DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); ++ ++ assert(thread_state->nesting_level < UINT32_MAX); ++ ++ thread_state->nesting_level++; ++} ++ ++/** ++ * defer_call_end: Run any pending defer_call() functions ++ * ++ * There must have been a matching defer_call_begin() call in the same thread ++ * prior to this defer_call_end() call. ++ */ ++void defer_call_end(void) ++{ ++ DeferCallThreadState *thread_state = get_ptr_defer_call_thread_state(); ++ ++ assert(thread_state->nesting_level > 0); ++ ++ if (--thread_state->nesting_level > 0) { ++ return; ++ } ++ ++ GArray *array = thread_state->deferred_call_array; ++ if (!array) { ++ return; ++ } ++ ++ DeferredCall *fns = (DeferredCall *)array->data; ++ ++ for (guint i = 0; i < array->len; i++) { ++ fns[i].fn(fns[i].opaque); ++ } ++ ++ /* ++ * This resets the array without freeing memory so that appending is cheap ++ * in the future. ++ */ ++ g_array_set_size(array, 0); ++} +diff --git a/qcow2/lib/util/error-report.c b/qcow2/lib/util/error-report.c +new file mode 100644 +index 00000000..1b17c11d +--- /dev/null ++++ b/qcow2/lib/util/error-report.c +@@ -0,0 +1,394 @@ ++/* ++ * Error reporting ++ * ++ * Copyright (C) 2010 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "monitor/monitor.h" ++#include "qemu/error-report.h" ++ ++/* ++ * @report_type is the type of message: error, warning or ++ * informational. ++ */ ++typedef enum { ++ REPORT_TYPE_ERROR, ++ REPORT_TYPE_WARNING, ++ REPORT_TYPE_INFO, ++} report_type; ++ ++/* Prepend timestamp to messages */ ++bool message_with_timestamp; ++bool error_with_guestname; ++const char *error_guest_name; ++ ++int error_printf(const char *fmt, ...) ++{ ++ va_list ap; ++ int ret; ++ ++ va_start(ap, fmt); ++ ret = error_vprintf(fmt, ap); ++ va_end(ap); ++ return ret; ++} ++ ++static Location std_loc = { ++ .kind = LOC_NONE ++}; ++static Location *cur_loc = &std_loc; ++ ++/* ++ * Push location saved in LOC onto the location stack, return it. ++ * The top of that stack is the current location. ++ * Needs a matching loc_pop(). ++ */ ++Location *loc_push_restore(Location *loc) ++{ ++ assert(!loc->prev); ++ loc->prev = cur_loc; ++ cur_loc = loc; ++ return loc; ++} ++ ++/* ++ * Initialize *LOC to "nowhere", push it onto the location stack. ++ * The top of that stack is the current location. ++ * Needs a matching loc_pop(). ++ * Return LOC. ++ */ ++Location *loc_push_none(Location *loc) ++{ ++ loc->kind = LOC_NONE; ++ loc->prev = NULL; ++ return loc_push_restore(loc); ++} ++ ++/* ++ * Pop the location stack. ++ * LOC must be the current location, i.e. the top of the stack. ++ */ ++Location *loc_pop(Location *loc) ++{ ++ assert(cur_loc == loc && loc->prev); ++ cur_loc = loc->prev; ++ loc->prev = NULL; ++ return loc; ++} ++ ++/* ++ * Save the current location in LOC, return LOC. ++ */ ++Location *loc_save(Location *loc) ++{ ++ *loc = *cur_loc; ++ loc->prev = NULL; ++ return loc; ++} ++ ++/* ++ * Change the current location to the one saved in LOC. ++ */ ++void loc_restore(Location *loc) ++{ ++ Location *prev = cur_loc->prev; ++ assert(!loc->prev); ++ *cur_loc = *loc; ++ cur_loc->prev = prev; ++} ++ ++/* ++ * Change the current location to "nowhere in particular". ++ */ ++void loc_set_none(void) ++{ ++ cur_loc->kind = LOC_NONE; ++} ++ ++/* ++ * Change the current location to argument ARGV[IDX..IDX+CNT-1]. ++ */ ++void loc_set_cmdline(char **argv, int idx, int cnt) ++{ ++ cur_loc->kind = LOC_CMDLINE; ++ cur_loc->num = cnt; ++ cur_loc->ptr = argv + idx; ++} ++ ++/* ++ * Change the current location to file FNAME, line LNO. ++ */ ++void loc_set_file(const char *fname, int lno) ++{ ++ assert (fname || cur_loc->kind == LOC_FILE); ++ cur_loc->kind = LOC_FILE; ++ cur_loc->num = lno; ++ if (fname) { ++ cur_loc->ptr = fname; ++ } ++} ++ ++/* ++ * Print current location to current monitor if we have one, else to stderr. ++ */ ++static void print_loc(void) ++{ ++ const char *sep = ""; ++ int i; ++ const char *const *argp; ++ ++ if (!monitor_cur() && g_get_prgname()) { ++ error_printf("%s:", g_get_prgname()); ++ sep = " "; ++ } ++ switch (cur_loc->kind) { ++ case LOC_CMDLINE: ++ argp = cur_loc->ptr; ++ for (i = 0; i < cur_loc->num; i++) { ++ error_printf("%s%s", sep, argp[i]); ++ sep = " "; ++ } ++ error_printf(": "); ++ break; ++ case LOC_FILE: ++ error_printf("%s:", (const char *)cur_loc->ptr); ++ if (cur_loc->num) { ++ error_printf("%d:", cur_loc->num); ++ } ++ error_printf(" "); ++ break; ++ default: ++ error_printf("%s", sep); ++ } ++} ++ ++static char * ++real_time_iso8601(void) ++{ ++ g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); ++ return g_date_time_format_iso8601(dt); ++} ++ ++/* ++ * Print a message to current monitor if we have one, else to stderr. ++ * @report_type is the type of message: error, warning or informational. ++ * Format arguments like vsprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++G_GNUC_PRINTF(2, 0) ++static void vreport(report_type type, const char *fmt, va_list ap) ++{ ++ gchar *timestr; ++ ++ if (message_with_timestamp && !monitor_cur()) { ++ timestr = real_time_iso8601(); ++ error_printf("%s ", timestr); ++ g_free(timestr); ++ } ++ ++ /* Only prepend guest name if -msg guest-name and -name guest=... are set */ ++ if (error_with_guestname && error_guest_name && !monitor_cur()) { ++ error_printf("%s ", error_guest_name); ++ } ++ ++ print_loc(); ++ ++ switch (type) { ++ case REPORT_TYPE_ERROR: ++ break; ++ case REPORT_TYPE_WARNING: ++ error_printf("warning: "); ++ break; ++ case REPORT_TYPE_INFO: ++ error_printf("info: "); ++ break; ++ } ++ ++ error_vprintf(fmt, ap); ++ error_printf("\n"); ++} ++ ++/* ++ * Print an error message to current monitor if we have one, else to stderr. ++ * Format arguments like vsprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ * It's wrong to call this in a QMP monitor. Use error_setg() there. ++ */ ++void error_vreport(const char *fmt, va_list ap) ++{ ++ vreport(REPORT_TYPE_ERROR, fmt, ap); ++} ++ ++/* ++ * Print a warning message to current monitor if we have one, else to stderr. ++ * Format arguments like vsprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++void warn_vreport(const char *fmt, va_list ap) ++{ ++ vreport(REPORT_TYPE_WARNING, fmt, ap); ++} ++ ++/* ++ * Print an information message to current monitor if we have one, else to ++ * stderr. ++ * Format arguments like vsprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++void info_vreport(const char *fmt, va_list ap) ++{ ++ vreport(REPORT_TYPE_INFO, fmt, ap); ++} ++ ++/* ++ * Print an error message to current monitor if we have one, else to stderr. ++ * Format arguments like sprintf(). The resulting message should be ++ * a single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ * It's wrong to call this in a QMP monitor. Use error_setg() there. ++ */ ++void error_report(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_ERROR, fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * Print a warning message to current monitor if we have one, else to stderr. ++ * Format arguments like sprintf(). The resulting message should be a ++ * single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++void warn_report(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_WARNING, fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * Print an information message to current monitor if we have one, else to ++ * stderr. ++ * Format arguments like sprintf(). The resulting message should be a ++ * single phrase, with no newline or trailing punctuation. ++ * Prepend the current location and append a newline. ++ */ ++void info_report(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_INFO, fmt, ap); ++ va_end(ap); ++} ++ ++/* ++ * Like error_report(), except print just once. ++ * If *printed is false, print the message, and flip *printed to true. ++ * Return whether the message was printed. ++ */ ++bool error_report_once_cond(bool *printed, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ assert(printed); ++ if (*printed) { ++ return false; ++ } ++ *printed = true; ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_ERROR, fmt, ap); ++ va_end(ap); ++ return true; ++} ++ ++/* ++ * Like warn_report(), except print just once. ++ * If *printed is false, print the message, and flip *printed to true. ++ * Return whether the message was printed. ++ */ ++bool warn_report_once_cond(bool *printed, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ assert(printed); ++ if (*printed) { ++ return false; ++ } ++ *printed = true; ++ va_start(ap, fmt); ++ vreport(REPORT_TYPE_WARNING, fmt, ap); ++ va_end(ap); ++ return true; ++} ++ ++static char *qemu_glog_domains; ++ ++static void qemu_log_func(const gchar *log_domain, ++ GLogLevelFlags log_level, ++ const gchar *message, ++ gpointer user_data) ++{ ++ switch (log_level & G_LOG_LEVEL_MASK) { ++ case G_LOG_LEVEL_DEBUG: ++ case G_LOG_LEVEL_INFO: ++ /* ++ * Use same G_MESSAGES_DEBUG logic as glib to enable/disable debug ++ * messages ++ */ ++ if (qemu_glog_domains == NULL) { ++ break; ++ } ++ if (strcmp(qemu_glog_domains, "all") != 0 && ++ (log_domain == NULL || !strstr(qemu_glog_domains, log_domain))) { ++ break; ++ } ++ /* Fall through */ ++ case G_LOG_LEVEL_MESSAGE: ++ info_report("%s%s%s", ++ log_domain ?: "", log_domain ? ": " : "", message); ++ ++ break; ++ case G_LOG_LEVEL_WARNING: ++ warn_report("%s%s%s", ++ log_domain ?: "", log_domain ? ": " : "", message); ++ break; ++ case G_LOG_LEVEL_CRITICAL: ++ case G_LOG_LEVEL_ERROR: ++ error_report("%s%s%s", ++ log_domain ?: "", log_domain ? ": " : "", message); ++ break; ++ } ++} ++ ++void error_init(const char *argv0) ++{ ++ const char *p = strrchr(argv0, '/'); ++ ++ /* Set the program name for error_print_loc(). */ ++ g_set_prgname(p ? p + 1 : argv0); ++ ++ /* ++ * This sets up glib logging so libraries using it also print their logs ++ * through error_report(), warn_report(), info_report(). ++ */ ++ g_log_set_default_handler(qemu_log_func, NULL); ++ g_warn_if_fail(qemu_glog_domains == NULL); ++ qemu_glog_domains = g_strdup(g_getenv("G_MESSAGES_DEBUG")); ++} +diff --git a/qcow2/lib/util/error.c b/qcow2/lib/util/error.c +new file mode 100644 +index 00000000..e5e24720 +--- /dev/null ++++ b/qcow2/lib/util/error.c +@@ -0,0 +1,308 @@ ++/* ++ * QEMU Error Objects ++ * ++ * Copyright IBM, Corp. 2011 ++ * Copyright (C) 2011-2015 Red Hat, Inc. ++ * ++ * Authors: ++ * Anthony Liguori ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2. See ++ * the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/error-report.h" ++ ++struct Error ++{ ++ char *msg; ++ ErrorClass err_class; ++ const char *src, *func; ++ int line; ++ GString *hint; ++}; ++ ++Error *error_abort; ++Error *error_fatal; ++Error *error_warn; ++ ++static void error_handle(Error **errp, Error *err) ++{ ++ if (errp == &error_abort) { ++ fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", ++ err->func, err->src, err->line); ++ error_report("%s", error_get_pretty(err)); ++ if (err->hint) { ++ error_printf("%s", err->hint->str); ++ } ++ abort(); ++ } ++ if (errp == &error_fatal) { ++ error_report_err(err); ++ exit(1); ++ } ++ if (errp == &error_warn) { ++ warn_report_err(err); ++ } else if (errp && !*errp) { ++ *errp = err; ++ } else { ++ error_free(err); ++ } ++} ++ ++G_GNUC_PRINTF(6, 0) ++static void error_setv(Error **errp, ++ const char *src, int line, const char *func, ++ ErrorClass err_class, const char *fmt, va_list ap, ++ const char *suffix) ++{ ++ Error *err; ++ int saved_errno = errno; ++ ++ if (errp == NULL) { ++ return; ++ } ++ assert(*errp == NULL); ++ ++ err = g_malloc0(sizeof(*err)); ++ err->msg = g_strdup_vprintf(fmt, ap); ++ if (suffix) { ++ char *msg = err->msg; ++ err->msg = g_strdup_printf("%s: %s", msg, suffix); ++ g_free(msg); ++ } ++ err->err_class = err_class; ++ err->src = src; ++ err->line = line; ++ err->func = func; ++ ++ error_handle(errp, err); ++ ++ errno = saved_errno; ++} ++ ++void error_set_internal(Error **errp, ++ const char *src, int line, const char *func, ++ ErrorClass err_class, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_setv(errp, src, line, func, err_class, fmt, ap, NULL); ++ va_end(ap); ++} ++ ++void error_setg_internal(Error **errp, ++ const char *src, int line, const char *func, ++ const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap, NULL); ++ va_end(ap); ++} ++ ++void error_setg_errno_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int os_errno, const char *fmt, ...) ++{ ++ va_list ap; ++ int saved_errno = errno; ++ ++ va_start(ap, fmt); ++ error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap, ++ os_errno != 0 ? strerror(os_errno) : NULL); ++ va_end(ap); ++ ++ errno = saved_errno; ++} ++ ++void error_setg_file_open_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int os_errno, const char *filename) ++{ ++ error_setg_errno_internal(errp, src, line, func, os_errno, ++ "Could not open '%s'", filename); ++} ++ ++void error_vprepend(Error *const *errp, const char *fmt, va_list ap) ++{ ++ GString *newmsg; ++ ++ if (!errp) { ++ return; ++ } ++ ++ newmsg = g_string_new(NULL); ++ g_string_vprintf(newmsg, fmt, ap); ++ g_string_append(newmsg, (*errp)->msg); ++ g_free((*errp)->msg); ++ (*errp)->msg = g_string_free(newmsg, 0); ++} ++ ++void error_prepend(Error *const *errp, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_vprepend(errp, fmt, ap); ++ va_end(ap); ++} ++ ++void error_append_hint(Error *const *errp, const char *fmt, ...) ++{ ++ va_list ap; ++ int saved_errno = errno; ++ Error *err; ++ ++ if (!errp) { ++ return; ++ } ++ err = *errp; ++ assert(err && errp != &error_abort && errp != &error_fatal); ++ ++ if (!err->hint) { ++ err->hint = g_string_new(NULL); ++ } ++ va_start(ap, fmt); ++ g_string_append_vprintf(err->hint, fmt, ap); ++ va_end(ap); ++ ++ errno = saved_errno; ++} ++ ++#ifdef _WIN32 ++ ++void error_setg_win32_internal(Error **errp, ++ const char *src, int line, const char *func, ++ int win32_err, const char *fmt, ...) ++{ ++ va_list ap; ++ char *suffix = NULL; ++ ++ if (errp == NULL) { ++ return; ++ } ++ ++ if (win32_err != 0) { ++ suffix = g_win32_error_message(win32_err); ++ } ++ ++ va_start(ap, fmt); ++ error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, ++ fmt, ap, suffix); ++ va_end(ap); ++ ++ g_free(suffix); ++} ++ ++#endif ++ ++Error *error_copy(const Error *err) ++{ ++ Error *err_new; ++ ++ err_new = g_malloc0(sizeof(*err)); ++ err_new->msg = g_strdup(err->msg); ++ err_new->err_class = err->err_class; ++ err_new->src = err->src; ++ err_new->line = err->line; ++ err_new->func = err->func; ++ if (err->hint) { ++ err_new->hint = g_string_new(err->hint->str); ++ } ++ ++ return err_new; ++} ++ ++ErrorClass error_get_class(const Error *err) ++{ ++ return err->err_class; ++} ++ ++const char *error_get_pretty(const Error *err) ++{ ++ return err->msg; ++} ++ ++void error_report_err(Error *err) ++{ ++ error_report("%s", error_get_pretty(err)); ++ if (err->hint) { ++ error_printf("%s", err->hint->str); ++ } ++ error_free(err); ++} ++ ++void warn_report_err(Error *err) ++{ ++ warn_report("%s", error_get_pretty(err)); ++ if (err->hint) { ++ error_printf("%s", err->hint->str); ++ } ++ error_free(err); ++} ++ ++void error_reportf_err(Error *err, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_vprepend(&err, fmt, ap); ++ va_end(ap); ++ error_report_err(err); ++} ++ ++ ++void warn_reportf_err(Error *err, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_vprepend(&err, fmt, ap); ++ va_end(ap); ++ warn_report_err(err); ++} ++ ++void error_free(Error *err) ++{ ++ if (err) { ++ g_free(err->msg); ++ if (err->hint) { ++ g_string_free(err->hint, true); ++ } ++ g_free(err); ++ } ++} ++ ++void error_free_or_abort(Error **errp) ++{ ++ assert(errp && *errp); ++ error_free(*errp); ++ *errp = NULL; ++} ++ ++void error_propagate(Error **dst_errp, Error *local_err) ++{ ++ if (!local_err) { ++ return; ++ } ++ error_handle(dst_errp, local_err); ++} ++ ++void error_propagate_prepend(Error **dst_errp, Error *err, ++ const char *fmt, ...) ++{ ++ va_list ap; ++ ++ if (dst_errp && !*dst_errp) { ++ va_start(ap, fmt); ++ error_vprepend(&err, fmt, ap); ++ va_end(ap); ++ } /* else error is being ignored, don't bother with prepending */ ++ error_propagate(dst_errp, err); ++} +diff --git a/qcow2/lib/util/event_notifier-posix.c b/qcow2/lib/util/event_notifier-posix.c +new file mode 100644 +index 00000000..76420c5b +--- /dev/null ++++ b/qcow2/lib/util/event_notifier-posix.c +@@ -0,0 +1,142 @@ ++/* ++ * event notifier support ++ * ++ * Copyright Red Hat, Inc. 2010 ++ * ++ * Authors: ++ * Michael S. Tsirkin ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++#include "qemu/event_notifier.h" ++#include "qemu/main-loop.h" ++ ++#ifdef CONFIG_EVENTFD ++#include ++#endif ++ ++#ifdef CONFIG_EVENTFD ++/* ++ * Initialize @e with existing file descriptor @fd. ++ * @fd must be a genuine eventfd object, emulation with pipe won't do. ++ */ ++void event_notifier_init_fd(EventNotifier *e, int fd) ++{ ++ e->rfd = fd; ++ e->wfd = fd; ++ e->initialized = true; ++} ++#endif ++ ++int event_notifier_init(EventNotifier *e, int active) ++{ ++ int fds[2]; ++ int ret; ++ ++#ifdef CONFIG_EVENTFD ++ ret = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); ++#else ++ ret = -1; ++ errno = ENOSYS; ++#endif ++ if (ret >= 0) { ++ e->rfd = e->wfd = ret; ++ } else { ++ if (errno != ENOSYS) { ++ return -errno; ++ } ++ if (!g_unix_open_pipe(fds, FD_CLOEXEC, NULL)) { ++ return -errno; ++ } ++ if (!g_unix_set_fd_nonblocking(fds[0], true, NULL)) { ++ ret = -errno; ++ goto fail; ++ } ++ if (!g_unix_set_fd_nonblocking(fds[1], true, NULL)) { ++ ret = -errno; ++ goto fail; ++ } ++ e->rfd = fds[0]; ++ e->wfd = fds[1]; ++ } ++ e->initialized = true; ++ if (active) { ++ event_notifier_set(e); ++ } ++ return 0; ++ ++fail: ++ close(fds[0]); ++ close(fds[1]); ++ return ret; ++} ++ ++void event_notifier_cleanup(EventNotifier *e) ++{ ++ if (!e->initialized) { ++ return; ++ } ++ ++ if (e->rfd != e->wfd) { ++ close(e->rfd); ++ } ++ ++ e->rfd = -1; ++ close(e->wfd); ++ e->wfd = -1; ++ e->initialized = false; ++} ++ ++int event_notifier_get_fd(const EventNotifier *e) ++{ ++ return e->rfd; ++} ++ ++int event_notifier_get_wfd(const EventNotifier *e) ++{ ++ return e->wfd; ++} ++ ++int event_notifier_set(EventNotifier *e) ++{ ++ static const uint64_t value = 1; ++ ssize_t ret; ++ ++ if (!e->initialized) { ++ return -1; ++ } ++ ++ do { ++ ret = write(e->wfd, &value, sizeof(value)); ++ } while (ret < 0 && errno == EINTR); ++ ++ /* EAGAIN is fine, a read must be pending. */ ++ if (ret < 0 && errno != EAGAIN) { ++ return -errno; ++ } ++ return 0; ++} ++ ++int event_notifier_test_and_clear(EventNotifier *e) ++{ ++ int value; ++ ssize_t len; ++ char buffer[512]; ++ ++ if (!e->initialized) { ++ return 0; ++ } ++ ++ /* Drain the notify pipe. For eventfd, only 8 bytes will be read. */ ++ value = 0; ++ do { ++ len = read(e->rfd, buffer, sizeof(buffer)); ++ value |= (len > 0); ++ } while ((len == -1 && errno == EINTR) || len == sizeof(buffer)); ++ ++ return value; ++} +diff --git a/qcow2/lib/util/fdmon-epoll.c b/qcow2/lib/util/fdmon-epoll.c +new file mode 100644 +index 00000000..c6413cb1 +--- /dev/null ++++ b/qcow2/lib/util/fdmon-epoll.c +@@ -0,0 +1,156 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * epoll(7) file descriptor monitoring ++ */ ++ ++#include "qemu/osdep.h" ++#include ++#include "qemu/rcu_queue.h" ++#include "aio-posix.h" ++ ++/* The fd number threshold to switch to epoll */ ++#define EPOLL_ENABLE_THRESHOLD 64 ++ ++void fdmon_epoll_disable(AioContext *ctx) ++{ ++ if (ctx->epollfd >= 0) { ++ close(ctx->epollfd); ++ ctx->epollfd = -1; ++ } ++ ++ /* Switch back */ ++ ctx->fdmon_ops = &fdmon_poll_ops; ++} ++ ++static inline int epoll_events_from_pfd(int pfd_events) ++{ ++ return (pfd_events & G_IO_IN ? EPOLLIN : 0) | ++ (pfd_events & G_IO_OUT ? EPOLLOUT : 0) | ++ (pfd_events & G_IO_HUP ? EPOLLHUP : 0) | ++ (pfd_events & G_IO_ERR ? EPOLLERR : 0); ++} ++ ++static void fdmon_epoll_update(AioContext *ctx, ++ AioHandler *old_node, ++ AioHandler *new_node) ++{ ++ struct epoll_event event = { ++ .data.ptr = new_node, ++ .events = new_node ? epoll_events_from_pfd(new_node->pfd.events) : 0, ++ }; ++ int r; ++ ++ if (!new_node) { ++ r = epoll_ctl(ctx->epollfd, EPOLL_CTL_DEL, old_node->pfd.fd, &event); ++ } else if (!old_node) { ++ r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, new_node->pfd.fd, &event); ++ } else { ++ r = epoll_ctl(ctx->epollfd, EPOLL_CTL_MOD, new_node->pfd.fd, &event); ++ } ++ ++ if (r) { ++ fdmon_epoll_disable(ctx); ++ } ++} ++ ++static int fdmon_epoll_wait(AioContext *ctx, AioHandlerList *ready_list, ++ int64_t timeout) ++{ ++ GPollFD pfd = { ++ .fd = ctx->epollfd, ++ .events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR, ++ }; ++ AioHandler *node; ++ int i, ret = 0; ++ struct epoll_event events[128]; ++ ++ if (timeout > 0) { ++ ret = qemu_poll_ns(&pfd, 1, timeout); ++ if (ret > 0) { ++ timeout = 0; ++ } ++ } ++ if (timeout <= 0 || ret > 0) { ++ ret = epoll_wait(ctx->epollfd, events, ++ ARRAY_SIZE(events), ++ timeout); ++ if (ret <= 0) { ++ goto out; ++ } ++ for (i = 0; i < ret; i++) { ++ int ev = events[i].events; ++ int revents = (ev & EPOLLIN ? G_IO_IN : 0) | ++ (ev & EPOLLOUT ? G_IO_OUT : 0) | ++ (ev & EPOLLHUP ? G_IO_HUP : 0) | ++ (ev & EPOLLERR ? G_IO_ERR : 0); ++ ++ node = events[i].data.ptr; ++ aio_add_ready_handler(ready_list, node, revents); ++ } ++ } ++out: ++ return ret; ++} ++ ++static const FDMonOps fdmon_epoll_ops = { ++ .update = fdmon_epoll_update, ++ .wait = fdmon_epoll_wait, ++ .need_wait = aio_poll_disabled, ++}; ++ ++static bool fdmon_epoll_try_enable(AioContext *ctx) ++{ ++ AioHandler *node; ++ struct epoll_event event; ++ ++ QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { ++ int r; ++ if (QLIST_IS_INSERTED(node, node_deleted) || !node->pfd.events) { ++ continue; ++ } ++ event.events = epoll_events_from_pfd(node->pfd.events); ++ event.data.ptr = node; ++ r = epoll_ctl(ctx->epollfd, EPOLL_CTL_ADD, node->pfd.fd, &event); ++ if (r) { ++ return false; ++ } ++ } ++ ++ ctx->fdmon_ops = &fdmon_epoll_ops; ++ return true; ++} ++ ++bool fdmon_epoll_try_upgrade(AioContext *ctx, unsigned npfd) ++{ ++ bool ok; ++ ++ if (ctx->epollfd < 0) { ++ return false; ++ } ++ ++ if (npfd < EPOLL_ENABLE_THRESHOLD) { ++ return false; ++ } ++ ++ /* The list must not change while we add fds to epoll */ ++ if (!qemu_lockcnt_dec_if_lock(&ctx->list_lock)) { ++ return false; ++ } ++ ++ ok = fdmon_epoll_try_enable(ctx); ++ ++ qemu_lockcnt_inc_and_unlock(&ctx->list_lock); ++ ++ if (!ok) { ++ fdmon_epoll_disable(ctx); ++ } ++ return ok; ++} ++ ++void fdmon_epoll_setup(AioContext *ctx) ++{ ++ ctx->epollfd = epoll_create1(EPOLL_CLOEXEC); ++ if (ctx->epollfd == -1) { ++ fprintf(stderr, "Failed to create epoll instance: %s", strerror(errno)); ++ } ++} +diff --git a/qcow2/lib/util/fdmon-poll.c b/qcow2/lib/util/fdmon-poll.c +new file mode 100644 +index 00000000..17df917c +--- /dev/null ++++ b/qcow2/lib/util/fdmon-poll.c +@@ -0,0 +1,107 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * poll(2) file descriptor monitoring ++ * ++ * Uses ppoll(2) when available, g_poll() otherwise. ++ */ ++ ++#include "qemu/osdep.h" ++#include "aio-posix.h" ++#include "qemu/rcu_queue.h" ++ ++/* ++ * These thread-local variables are used only in fdmon_poll_wait() around the ++ * call to the poll() system call. In particular they are not used while ++ * aio_poll is performing callbacks, which makes it much easier to think about ++ * reentrancy! ++ * ++ * Stack-allocated arrays would be perfect but they have size limitations; ++ * heap allocation is expensive enough that we want to reuse arrays across ++ * calls to aio_poll(). And because poll() has to be called without holding ++ * any lock, the arrays cannot be stored in AioContext. Thread-local data ++ * has none of the disadvantages of these three options. ++ */ ++static __thread GPollFD *pollfds; ++static __thread AioHandler **nodes; ++static __thread unsigned npfd, nalloc; ++static __thread Notifier pollfds_cleanup_notifier; ++ ++static void pollfds_cleanup(Notifier *n, void *unused) ++{ ++ g_assert(npfd == 0); ++ g_free(pollfds); ++ g_free(nodes); ++ nalloc = 0; ++} ++ ++static void add_pollfd(AioHandler *node) ++{ ++ if (npfd == nalloc) { ++ if (nalloc == 0) { ++ pollfds_cleanup_notifier.notify = pollfds_cleanup; ++ qemu_thread_atexit_add(&pollfds_cleanup_notifier); ++ nalloc = 8; ++ } else { ++ g_assert(nalloc <= INT_MAX); ++ nalloc *= 2; ++ } ++ pollfds = g_renew(GPollFD, pollfds, nalloc); ++ nodes = g_renew(AioHandler *, nodes, nalloc); ++ } ++ nodes[npfd] = node; ++ pollfds[npfd] = (GPollFD) { ++ .fd = node->pfd.fd, ++ .events = node->pfd.events, ++ }; ++ npfd++; ++} ++ ++static int fdmon_poll_wait(AioContext *ctx, AioHandlerList *ready_list, ++ int64_t timeout) ++{ ++ AioHandler *node; ++ int ret; ++ ++ assert(npfd == 0); ++ ++ QLIST_FOREACH_RCU(node, &ctx->aio_handlers, node) { ++ if (!QLIST_IS_INSERTED(node, node_deleted) && node->pfd.events) { ++ add_pollfd(node); ++ } ++ } ++ ++ /* epoll(7) is faster above a certain number of fds */ ++ if (fdmon_epoll_try_upgrade(ctx, npfd)) { ++ npfd = 0; /* we won't need pollfds[], reset npfd */ ++ return ctx->fdmon_ops->wait(ctx, ready_list, timeout); ++ } ++ ++ ret = qemu_poll_ns(pollfds, npfd, timeout); ++ if (ret > 0) { ++ int i; ++ ++ for (i = 0; i < npfd; i++) { ++ int revents = pollfds[i].revents; ++ ++ if (revents) { ++ aio_add_ready_handler(ready_list, nodes[i], revents); ++ } ++ } ++ } ++ ++ npfd = 0; ++ return ret; ++} ++ ++static void fdmon_poll_update(AioContext *ctx, ++ AioHandler *old_node, ++ AioHandler *new_node) ++{ ++ /* Do nothing, AioHandler already contains the state we'll need */ ++} ++ ++const FDMonOps fdmon_poll_ops = { ++ .update = fdmon_poll_update, ++ .wait = fdmon_poll_wait, ++ .need_wait = aio_poll_disabled, ++}; +diff --git a/qcow2/lib/util/hbitmap.c b/qcow2/lib/util/hbitmap.c +new file mode 100644 +index 00000000..6d6e1b59 +--- /dev/null ++++ b/qcow2/lib/util/hbitmap.c +@@ -0,0 +1,955 @@ ++/* ++ * Hierarchical Bitmap Data Type ++ * ++ * Copyright Red Hat, Inc., 2012 ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/hbitmap.h" ++#include "qemu/host-utils.h" ++#include "trace.h" ++#include "crypto/hash.h" ++ ++/* HBitmaps provides an array of bits. The bits are stored as usual in an ++ * array of unsigned longs, but HBitmap is also optimized to provide fast ++ * iteration over set bits; going from one bit to the next is O(logB n) ++ * worst case, with B = sizeof(long) * CHAR_BIT: the result is low enough ++ * that the number of levels is in fact fixed. ++ * ++ * In order to do this, it stacks multiple bitmaps with progressively coarser ++ * granularity; in all levels except the last, bit N is set iff the N-th ++ * unsigned long is nonzero in the immediately next level. When iteration ++ * completes on the last level it can examine the 2nd-last level to quickly ++ * skip entire words, and even do so recursively to skip blocks of 64 words or ++ * powers thereof (32 on 32-bit machines). ++ * ++ * Given an index in the bitmap, it can be split in group of bits like ++ * this (for the 64-bit case): ++ * ++ * bits 0-57 => word in the last bitmap | bits 58-63 => bit in the word ++ * bits 0-51 => word in the 2nd-last bitmap | bits 52-57 => bit in the word ++ * bits 0-45 => word in the 3rd-last bitmap | bits 46-51 => bit in the word ++ * ++ * So it is easy to move up simply by shifting the index right by ++ * log2(BITS_PER_LONG) bits. To move down, you shift the index left ++ * similarly, and add the word index within the group. Iteration uses ++ * ffs (find first set bit) to find the next word to examine; this ++ * operation can be done in constant time in most current architectures. ++ * ++ * Setting or clearing a range of m bits on all levels, the work to perform ++ * is O(m + m/W + m/W^2 + ...), which is O(m) like on a regular bitmap. ++ * ++ * When iterating on a bitmap, each bit (on any level) is only visited ++ * once. Hence, The total cost of visiting a bitmap with m bits in it is ++ * the number of bits that are set in all bitmaps. Unless the bitmap is ++ * extremely sparse, this is also O(m + m/W + m/W^2 + ...), so the amortized ++ * cost of advancing from one bit to the next is usually constant (worst case ++ * O(logB n) as in the non-amortized complexity). ++ */ ++ ++struct HBitmap { ++ /* ++ * Size of the bitmap, as requested in hbitmap_alloc or in hbitmap_truncate. ++ */ ++ uint64_t orig_size; ++ ++ /* Number of total bits in the bottom level. */ ++ uint64_t size; ++ ++ /* Number of set bits in the bottom level. */ ++ uint64_t count; ++ ++ /* A scaling factor. Given a granularity of G, each bit in the bitmap will ++ * will actually represent a group of 2^G elements. Each operation on a ++ * range of bits first rounds the bits to determine which group they land ++ * in, and then affect the entire page; iteration will only visit the first ++ * bit of each group. Here is an example of operations in a size-16, ++ * granularity-1 HBitmap: ++ * ++ * initial state 00000000 ++ * set(start=0, count=9) 11111000 (iter: 0, 2, 4, 6, 8) ++ * reset(start=1, count=3) 00111000 (iter: 4, 6, 8) ++ * set(start=9, count=2) 00111100 (iter: 4, 6, 8, 10) ++ * reset(start=5, count=5) 00000000 ++ * ++ * From an implementation point of view, when setting or resetting bits, ++ * the bitmap will scale bit numbers right by this amount of bits. When ++ * iterating, the bitmap will scale bit numbers left by this amount of ++ * bits. ++ */ ++ int granularity; ++ ++ /* A meta dirty bitmap to track the dirtiness of bits in this HBitmap. */ ++ HBitmap *meta; ++ ++ /* A number of progressively less coarse bitmaps (i.e. level 0 is the ++ * coarsest). Each bit in level N represents a word in level N+1 that ++ * has a set bit, except the last level where each bit represents the ++ * actual bitmap. ++ * ++ * Note that all bitmaps have the same number of levels. Even a 1-bit ++ * bitmap will still allocate HBITMAP_LEVELS arrays. ++ */ ++ unsigned long *levels[HBITMAP_LEVELS]; ++ ++ /* The length of each levels[] array. */ ++ uint64_t sizes[HBITMAP_LEVELS]; ++}; ++ ++/* Advance hbi to the next nonzero word and return it. hbi->pos ++ * is updated. Returns zero if we reach the end of the bitmap. ++ */ ++static unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) ++{ ++ size_t pos = hbi->pos; ++ const HBitmap *hb = hbi->hb; ++ unsigned i = HBITMAP_LEVELS - 1; ++ ++ unsigned long cur; ++ do { ++ i--; ++ pos >>= BITS_PER_LEVEL; ++ cur = hbi->cur[i] & hb->levels[i][pos]; ++ } while (cur == 0); ++ ++ /* Check for end of iteration. We always use fewer than BITS_PER_LONG ++ * bits in the level 0 bitmap; thus we can repurpose the most significant ++ * bit as a sentinel. The sentinel is set in hbitmap_alloc and ensures ++ * that the above loop ends even without an explicit check on i. ++ */ ++ ++ if (i == 0 && cur == (1UL << (BITS_PER_LONG - 1))) { ++ return 0; ++ } ++ for (; i < HBITMAP_LEVELS - 1; i++) { ++ /* Shift back pos to the left, matching the right shifts above. ++ * The index of this word's least significant set bit provides ++ * the low-order bits. ++ */ ++ assert(cur); ++ pos = (pos << BITS_PER_LEVEL) + ctzl(cur); ++ hbi->cur[i] = cur & (cur - 1); ++ ++ /* Set up next level for iteration. */ ++ cur = hb->levels[i + 1][pos]; ++ } ++ ++ hbi->pos = pos; ++ trace_hbitmap_iter_skip_words(hbi->hb, hbi, pos, cur); ++ ++ assert(cur); ++ return cur; ++} ++ ++int64_t hbitmap_iter_next(HBitmapIter *hbi) ++{ ++ unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1] & ++ hbi->hb->levels[HBITMAP_LEVELS - 1][hbi->pos]; ++ int64_t item; ++ ++ if (cur == 0) { ++ cur = hbitmap_iter_skip_words(hbi); ++ if (cur == 0) { ++ return -1; ++ } ++ } ++ ++ /* The next call will resume work from the next bit. */ ++ hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); ++ item = ((uint64_t)hbi->pos << BITS_PER_LEVEL) + ctzl(cur); ++ ++ return item << hbi->granularity; ++} ++ ++void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first) ++{ ++ unsigned i, bit; ++ uint64_t pos; ++ ++ hbi->hb = hb; ++ pos = first >> hb->granularity; ++ assert(pos < hb->size); ++ hbi->pos = pos >> BITS_PER_LEVEL; ++ hbi->granularity = hb->granularity; ++ ++ for (i = HBITMAP_LEVELS; i-- > 0; ) { ++ bit = pos & (BITS_PER_LONG - 1); ++ pos >>= BITS_PER_LEVEL; ++ ++ /* Drop bits representing items before first. */ ++ hbi->cur[i] = hb->levels[i][pos] & ~((1UL << bit) - 1); ++ ++ /* We have already added level i+1, so the lowest set bit has ++ * been processed. Clear it. ++ */ ++ if (i != HBITMAP_LEVELS - 1) { ++ hbi->cur[i] &= ~(1UL << bit); ++ } ++ } ++} ++ ++int64_t hbitmap_next_dirty(const HBitmap *hb, int64_t start, int64_t count) ++{ ++ HBitmapIter hbi; ++ int64_t first_dirty_off; ++ uint64_t end; ++ ++ assert(start >= 0 && count >= 0); ++ ++ if (start >= hb->orig_size || count == 0) { ++ return -1; ++ } ++ ++ end = count > hb->orig_size - start ? hb->orig_size : start + count; ++ ++ hbitmap_iter_init(&hbi, hb, start); ++ first_dirty_off = hbitmap_iter_next(&hbi); ++ ++ if (first_dirty_off < 0 || first_dirty_off >= end) { ++ return -1; ++ } ++ ++ return MAX(start, first_dirty_off); ++} ++ ++int64_t hbitmap_next_zero(const HBitmap *hb, int64_t start, int64_t count) ++{ ++ size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL; ++ unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1]; ++ unsigned long cur = last_lev[pos]; ++ unsigned start_bit_offset; ++ uint64_t end_bit, sz; ++ int64_t res; ++ ++ assert(start >= 0 && count >= 0); ++ ++ if (start >= hb->orig_size || count == 0) { ++ return -1; ++ } ++ ++ end_bit = count > hb->orig_size - start ? ++ hb->size : ++ ((start + count - 1) >> hb->granularity) + 1; ++ sz = (end_bit + BITS_PER_LONG - 1) >> BITS_PER_LEVEL; ++ ++ /* There may be some zero bits in @cur before @start. We are not interested ++ * in them, let's set them. ++ */ ++ start_bit_offset = (start >> hb->granularity) & (BITS_PER_LONG - 1); ++ cur |= (1UL << start_bit_offset) - 1; ++ assert((start >> hb->granularity) < hb->size); ++ ++ if (cur == (unsigned long)-1) { ++ do { ++ pos++; ++ } while (pos < sz && last_lev[pos] == (unsigned long)-1); ++ ++ if (pos >= sz) { ++ return -1; ++ } ++ ++ cur = last_lev[pos]; ++ } ++ ++ res = (pos << BITS_PER_LEVEL) + ctol(cur); ++ if (res >= end_bit) { ++ return -1; ++ } ++ ++ res = res << hb->granularity; ++ if (res < start) { ++ assert(((start - res) >> hb->granularity) == 0); ++ return start; ++ } ++ ++ return res; ++} ++ ++bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end, ++ int64_t max_dirty_count, ++ int64_t *dirty_start, int64_t *dirty_count) ++{ ++ int64_t next_zero; ++ ++ assert(start >= 0 && end >= 0 && max_dirty_count > 0); ++ ++ end = MIN(end, hb->orig_size); ++ if (start >= end) { ++ return false; ++ } ++ ++ start = hbitmap_next_dirty(hb, start, end - start); ++ if (start < 0) { ++ return false; ++ } ++ ++ end = start + MIN(end - start, max_dirty_count); ++ ++ next_zero = hbitmap_next_zero(hb, start, end - start); ++ if (next_zero >= 0) { ++ end = next_zero; ++ } ++ ++ *dirty_start = start; ++ *dirty_count = end - start; ++ ++ return true; ++} ++ ++bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count, ++ int64_t *pnum) ++{ ++ int64_t next_dirty, next_zero; ++ ++ assert(start >= 0); ++ assert(count > 0); ++ assert(start + count <= hb->orig_size); ++ ++ next_dirty = hbitmap_next_dirty(hb, start, count); ++ if (next_dirty == -1) { ++ *pnum = count; ++ return false; ++ } ++ ++ if (next_dirty > start) { ++ *pnum = next_dirty - start; ++ return false; ++ } ++ ++ assert(next_dirty == start); ++ ++ next_zero = hbitmap_next_zero(hb, start, count); ++ if (next_zero == -1) { ++ *pnum = count; ++ return true; ++ } ++ ++ assert(next_zero > start); ++ *pnum = next_zero - start; ++ return true; ++} ++ ++bool hbitmap_empty(const HBitmap *hb) ++{ ++ return hb->count == 0; ++} ++ ++int hbitmap_granularity(const HBitmap *hb) ++{ ++ return hb->granularity; ++} ++ ++uint64_t hbitmap_count(const HBitmap *hb) ++{ ++ return hb->count << hb->granularity; ++} ++ ++/** ++ * hbitmap_iter_next_word: ++ * @hbi: HBitmapIter to operate on. ++ * @p_cur: Location where to store the next non-zero word. ++ * ++ * Return the index of the next nonzero word that is set in @hbi's ++ * associated HBitmap, and set *p_cur to the content of that word ++ * (bits before the index that was passed to hbitmap_iter_init are ++ * trimmed on the first call). Return -1, and set *p_cur to zero, ++ * if all remaining words are zero. ++ */ ++static size_t hbitmap_iter_next_word(HBitmapIter *hbi, unsigned long *p_cur) ++{ ++ unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1]; ++ ++ if (cur == 0) { ++ cur = hbitmap_iter_skip_words(hbi); ++ if (cur == 0) { ++ *p_cur = 0; ++ return -1; ++ } ++ } ++ ++ /* The next call will resume work from the next word. */ ++ hbi->cur[HBITMAP_LEVELS - 1] = 0; ++ *p_cur = cur; ++ return hbi->pos; ++} ++ ++/* Count the number of set bits between start and end, not accounting for ++ * the granularity. Also an example of how to use hbitmap_iter_next_word. ++ */ ++static uint64_t hb_count_between(HBitmap *hb, uint64_t start, uint64_t last) ++{ ++ HBitmapIter hbi; ++ uint64_t count = 0; ++ uint64_t end = last + 1; ++ unsigned long cur; ++ size_t pos; ++ ++ hbitmap_iter_init(&hbi, hb, start << hb->granularity); ++ for (;;) { ++ pos = hbitmap_iter_next_word(&hbi, &cur); ++ if (pos >= (end >> BITS_PER_LEVEL)) { ++ break; ++ } ++ count += ctpopl(cur); ++ } ++ ++ if (pos == (end >> BITS_PER_LEVEL)) { ++ /* Drop bits representing the END-th and subsequent items. */ ++ int bit = end & (BITS_PER_LONG - 1); ++ cur &= (1UL << bit) - 1; ++ count += ctpopl(cur); ++ } ++ ++ return count; ++} ++ ++/* Setting starts at the last layer and propagates up if an element ++ * changes. ++ */ ++static inline bool hb_set_elem(unsigned long *elem, uint64_t start, uint64_t last) ++{ ++ unsigned long mask; ++ unsigned long old; ++ ++ assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); ++ assert(start <= last); ++ ++ mask = 2UL << (last & (BITS_PER_LONG - 1)); ++ mask -= 1UL << (start & (BITS_PER_LONG - 1)); ++ old = *elem; ++ *elem |= mask; ++ return old != *elem; ++} ++ ++/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... ++ * Returns true if at least one bit is changed. */ ++static bool hb_set_between(HBitmap *hb, int level, uint64_t start, ++ uint64_t last) ++{ ++ size_t pos = start >> BITS_PER_LEVEL; ++ size_t lastpos = last >> BITS_PER_LEVEL; ++ bool changed = false; ++ size_t i; ++ ++ i = pos; ++ if (i < lastpos) { ++ uint64_t next = (start | (BITS_PER_LONG - 1)) + 1; ++ changed |= hb_set_elem(&hb->levels[level][i], start, next - 1); ++ for (;;) { ++ start = next; ++ next += BITS_PER_LONG; ++ if (++i == lastpos) { ++ break; ++ } ++ changed |= (hb->levels[level][i] == 0); ++ hb->levels[level][i] = ~0UL; ++ } ++ } ++ changed |= hb_set_elem(&hb->levels[level][i], start, last); ++ ++ /* If there was any change in this layer, we may have to update ++ * the one above. ++ */ ++ if (level > 0 && changed) { ++ hb_set_between(hb, level - 1, pos, lastpos); ++ } ++ return changed; ++} ++ ++void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count) ++{ ++ /* Compute range in the last layer. */ ++ uint64_t first, n; ++ uint64_t last = start + count - 1; ++ ++ if (count == 0) { ++ return; ++ } ++ ++ trace_hbitmap_set(hb, start, count, ++ start >> hb->granularity, last >> hb->granularity); ++ ++ first = start >> hb->granularity; ++ last >>= hb->granularity; ++ assert(last < hb->size); ++ n = last - first + 1; ++ ++ hb->count += n - hb_count_between(hb, first, last); ++ if (hb_set_between(hb, HBITMAP_LEVELS - 1, first, last) && ++ hb->meta) { ++ hbitmap_set(hb->meta, start, count); ++ } ++} ++ ++/* Resetting works the other way round: propagate up if the new ++ * value is zero. ++ */ ++static inline bool hb_reset_elem(unsigned long *elem, uint64_t start, uint64_t last) ++{ ++ unsigned long mask; ++ bool blanked; ++ ++ assert((last >> BITS_PER_LEVEL) == (start >> BITS_PER_LEVEL)); ++ assert(start <= last); ++ ++ mask = 2UL << (last & (BITS_PER_LONG - 1)); ++ mask -= 1UL << (start & (BITS_PER_LONG - 1)); ++ blanked = *elem != 0 && ((*elem & ~mask) == 0); ++ *elem &= ~mask; ++ return blanked; ++} ++ ++/* The recursive workhorse (the depth is limited to HBITMAP_LEVELS)... ++ * Returns true if at least one bit is changed. */ ++static bool hb_reset_between(HBitmap *hb, int level, uint64_t start, ++ uint64_t last) ++{ ++ size_t pos = start >> BITS_PER_LEVEL; ++ size_t lastpos = last >> BITS_PER_LEVEL; ++ bool changed = false; ++ size_t i; ++ ++ i = pos; ++ if (i < lastpos) { ++ uint64_t next = (start | (BITS_PER_LONG - 1)) + 1; ++ ++ /* Here we need a more complex test than when setting bits. Even if ++ * something was changed, we must not blank bits in the upper level ++ * unless the lower-level word became entirely zero. So, remove pos ++ * from the upper-level range if bits remain set. ++ */ ++ if (hb_reset_elem(&hb->levels[level][i], start, next - 1)) { ++ changed = true; ++ } else { ++ pos++; ++ } ++ ++ for (;;) { ++ start = next; ++ next += BITS_PER_LONG; ++ if (++i == lastpos) { ++ break; ++ } ++ changed |= (hb->levels[level][i] != 0); ++ hb->levels[level][i] = 0UL; ++ } ++ } ++ ++ /* Same as above, this time for lastpos. */ ++ if (hb_reset_elem(&hb->levels[level][i], start, last)) { ++ changed = true; ++ } else { ++ lastpos--; ++ } ++ ++ if (level > 0 && changed) { ++ hb_reset_between(hb, level - 1, pos, lastpos); ++ } ++ ++ return changed; ++ ++} ++ ++void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count) ++{ ++ /* Compute range in the last layer. */ ++ uint64_t first; ++ uint64_t last = start + count - 1; ++ uint64_t gran = 1ULL << hb->granularity; ++ ++ if (count == 0) { ++ return; ++ } ++ ++ assert(QEMU_IS_ALIGNED(start, gran)); ++ assert(QEMU_IS_ALIGNED(count, gran) || (start + count == hb->orig_size)); ++ ++ trace_hbitmap_reset(hb, start, count, ++ start >> hb->granularity, last >> hb->granularity); ++ ++ first = start >> hb->granularity; ++ last >>= hb->granularity; ++ assert(last < hb->size); ++ ++ hb->count -= hb_count_between(hb, first, last); ++ if (hb_reset_between(hb, HBITMAP_LEVELS - 1, first, last) && ++ hb->meta) { ++ hbitmap_set(hb->meta, start, count); ++ } ++} ++ ++void hbitmap_reset_all(HBitmap *hb) ++{ ++ unsigned int i; ++ ++ /* Same as hbitmap_alloc() except for memset() instead of malloc() */ ++ for (i = HBITMAP_LEVELS; --i >= 1; ) { ++ memset(hb->levels[i], 0, hb->sizes[i] * sizeof(unsigned long)); ++ } ++ ++ hb->levels[0][0] = 1UL << (BITS_PER_LONG - 1); ++ hb->count = 0; ++} ++ ++bool hbitmap_is_serializable(const HBitmap *hb) ++{ ++ /* Every serialized chunk must be aligned to 64 bits so that endianness ++ * requirements can be fulfilled on both 64 bit and 32 bit hosts. ++ * We have hbitmap_serialization_align() which converts this ++ * alignment requirement from bitmap bits to items covered (e.g. sectors). ++ * That value is: ++ * 64 << hb->granularity ++ * Since this value must not exceed UINT64_MAX, hb->granularity must be ++ * less than 58 (== 64 - 6, where 6 is ld(64), i.e. 1 << 6 == 64). ++ * ++ * In order for hbitmap_serialization_align() to always return a ++ * meaningful value, bitmaps that are to be serialized must have a ++ * granularity of less than 58. */ ++ ++ return hb->granularity < 58; ++} ++ ++bool hbitmap_get(const HBitmap *hb, uint64_t item) ++{ ++ /* Compute position and bit in the last layer. */ ++ uint64_t pos = item >> hb->granularity; ++ unsigned long bit = 1UL << (pos & (BITS_PER_LONG - 1)); ++ assert(pos < hb->size); ++ ++ return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; ++} ++ ++uint64_t hbitmap_serialization_align(const HBitmap *hb) ++{ ++ assert(hbitmap_is_serializable(hb)); ++ ++ /* Require at least 64 bit granularity to be safe on both 64 bit and 32 bit ++ * hosts. */ ++ return UINT64_C(64) << hb->granularity; ++} ++ ++/* Start should be aligned to serialization granularity, chunk size should be ++ * aligned to serialization granularity too, except for last chunk. ++ */ ++static void serialization_chunk(const HBitmap *hb, ++ uint64_t start, uint64_t count, ++ unsigned long **first_el, uint64_t *el_count) ++{ ++ uint64_t last = start + count - 1; ++ uint64_t gran = hbitmap_serialization_align(hb); ++ ++ assert((start & (gran - 1)) == 0); ++ assert((last >> hb->granularity) < hb->size); ++ if ((last >> hb->granularity) != hb->size - 1) { ++ assert((count & (gran - 1)) == 0); ++ } ++ ++ start = (start >> hb->granularity) >> BITS_PER_LEVEL; ++ last = (last >> hb->granularity) >> BITS_PER_LEVEL; ++ ++ *first_el = &hb->levels[HBITMAP_LEVELS - 1][start]; ++ *el_count = last - start + 1; ++} ++ ++uint64_t hbitmap_serialization_size(const HBitmap *hb, ++ uint64_t start, uint64_t count) ++{ ++ uint64_t el_count; ++ unsigned long *cur; ++ ++ if (!count) { ++ return 0; ++ } ++ serialization_chunk(hb, start, count, &cur, &el_count); ++ ++ return el_count * sizeof(unsigned long); ++} ++ ++void hbitmap_serialize_part(const HBitmap *hb, uint8_t *buf, ++ uint64_t start, uint64_t count) ++{ ++ uint64_t el_count; ++ unsigned long *cur, *end; ++ ++ if (!count) { ++ return; ++ } ++ serialization_chunk(hb, start, count, &cur, &el_count); ++ end = cur + el_count; ++ ++ while (cur != end) { ++ unsigned long el = ++ (BITS_PER_LONG == 32 ? cpu_to_le32(*cur) : cpu_to_le64(*cur)); ++ ++ memcpy(buf, &el, sizeof(el)); ++ buf += sizeof(el); ++ cur++; ++ } ++} ++ ++void hbitmap_deserialize_part(HBitmap *hb, uint8_t *buf, ++ uint64_t start, uint64_t count, ++ bool finish) ++{ ++ uint64_t el_count; ++ unsigned long *cur, *end; ++ ++ if (!count) { ++ return; ++ } ++ serialization_chunk(hb, start, count, &cur, &el_count); ++ end = cur + el_count; ++ ++ while (cur != end) { ++ memcpy(cur, buf, sizeof(*cur)); ++ ++ if (BITS_PER_LONG == 32) { ++ le32_to_cpus((uint32_t *)cur); ++ } else { ++ le64_to_cpus((uint64_t *)cur); ++ } ++ ++ buf += sizeof(unsigned long); ++ cur++; ++ } ++ if (finish) { ++ hbitmap_deserialize_finish(hb); ++ } ++} ++ ++void hbitmap_deserialize_zeroes(HBitmap *hb, uint64_t start, uint64_t count, ++ bool finish) ++{ ++ uint64_t el_count; ++ unsigned long *first; ++ ++ if (!count) { ++ return; ++ } ++ serialization_chunk(hb, start, count, &first, &el_count); ++ ++ memset(first, 0, el_count * sizeof(unsigned long)); ++ if (finish) { ++ hbitmap_deserialize_finish(hb); ++ } ++} ++ ++void hbitmap_deserialize_ones(HBitmap *hb, uint64_t start, uint64_t count, ++ bool finish) ++{ ++ uint64_t el_count; ++ unsigned long *first; ++ ++ if (!count) { ++ return; ++ } ++ serialization_chunk(hb, start, count, &first, &el_count); ++ ++ memset(first, 0xff, el_count * sizeof(unsigned long)); ++ if (finish) { ++ hbitmap_deserialize_finish(hb); ++ } ++} ++ ++void hbitmap_deserialize_finish(HBitmap *bitmap) ++{ ++ int64_t i, size, prev_size; ++ int lev; ++ ++ /* restore levels starting from penultimate to zero level, assuming ++ * that the last level is ok */ ++ size = MAX((bitmap->size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); ++ for (lev = HBITMAP_LEVELS - 1; lev-- > 0; ) { ++ prev_size = size; ++ size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); ++ memset(bitmap->levels[lev], 0, size * sizeof(unsigned long)); ++ ++ for (i = 0; i < prev_size; ++i) { ++ if (bitmap->levels[lev + 1][i]) { ++ bitmap->levels[lev][i >> BITS_PER_LEVEL] |= ++ 1UL << (i & (BITS_PER_LONG - 1)); ++ } ++ } ++ } ++ ++ bitmap->levels[0][0] |= 1UL << (BITS_PER_LONG - 1); ++ bitmap->count = hb_count_between(bitmap, 0, bitmap->size - 1); ++} ++ ++void hbitmap_free(HBitmap *hb) ++{ ++ unsigned i; ++ assert(!hb->meta); ++ for (i = HBITMAP_LEVELS; i-- > 0; ) { ++ g_free(hb->levels[i]); ++ } ++ g_free(hb); ++} ++ ++HBitmap *hbitmap_alloc(uint64_t size, int granularity) ++{ ++ HBitmap *hb = g_new0(struct HBitmap, 1); ++ unsigned i; ++ ++ assert(size <= INT64_MAX); ++ hb->orig_size = size; ++ ++ assert(granularity >= 0 && granularity < 64); ++ size = (size + (1ULL << granularity) - 1) >> granularity; ++ assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE)); ++ ++ hb->size = size; ++ hb->granularity = granularity; ++ for (i = HBITMAP_LEVELS; i-- > 0; ) { ++ size = MAX((size + BITS_PER_LONG - 1) >> BITS_PER_LEVEL, 1); ++ hb->sizes[i] = size; ++ hb->levels[i] = g_new0(unsigned long, size); ++ } ++ ++ /* We necessarily have free bits in level 0 due to the definition ++ * of HBITMAP_LEVELS, so use one for a sentinel. This speeds up ++ * hbitmap_iter_skip_words. ++ */ ++ assert(size == 1); ++ hb->levels[0][0] |= 1UL << (BITS_PER_LONG - 1); ++ return hb; ++} ++ ++void hbitmap_truncate(HBitmap *hb, uint64_t size) ++{ ++ bool shrink; ++ unsigned i; ++ uint64_t num_elements = size; ++ uint64_t old; ++ ++ assert(size <= INT64_MAX); ++ hb->orig_size = size; ++ ++ /* Size comes in as logical elements, adjust for granularity. */ ++ size = (size + (1ULL << hb->granularity) - 1) >> hb->granularity; ++ assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE)); ++ shrink = size < hb->size; ++ ++ /* bit sizes are identical; nothing to do. */ ++ if (size == hb->size) { ++ return; ++ } ++ ++ /* If we're losing bits, let's clear those bits before we invalidate all of ++ * our invariants. This helps keep the bitcount consistent, and will prevent ++ * us from carrying around garbage bits beyond the end of the map. ++ */ ++ if (shrink) { ++ /* Don't clear partial granularity groups; ++ * start at the first full one. */ ++ uint64_t start = ROUND_UP(num_elements, UINT64_C(1) << hb->granularity); ++ uint64_t fix_count = (hb->size << hb->granularity) - start; ++ ++ assert(fix_count); ++ hbitmap_reset(hb, start, fix_count); ++ } ++ ++ hb->size = size; ++ for (i = HBITMAP_LEVELS; i-- > 0; ) { ++ size = MAX(BITS_TO_LONGS(size), 1); ++ if (hb->sizes[i] == size) { ++ break; ++ } ++ old = hb->sizes[i]; ++ hb->sizes[i] = size; ++ hb->levels[i] = g_renew(unsigned long, hb->levels[i], size); ++ if (!shrink) { ++ memset(&hb->levels[i][old], 0x00, ++ (size - old) * sizeof(*hb->levels[i])); ++ } ++ } ++ if (hb->meta) { ++ hbitmap_truncate(hb->meta, hb->size << hb->granularity); ++ } ++} ++ ++/** ++ * hbitmap_sparse_merge: performs dst = dst | src ++ * works with differing granularities. ++ * best used when src is sparsely populated. ++ */ ++static void hbitmap_sparse_merge(HBitmap *dst, const HBitmap *src) ++{ ++ int64_t offset; ++ int64_t count; ++ ++ for (offset = 0; ++ hbitmap_next_dirty_area(src, offset, src->orig_size, INT64_MAX, ++ &offset, &count); ++ offset += count) ++ { ++ hbitmap_set(dst, offset, count); ++ } ++} ++ ++/** ++ * Given HBitmaps A and B, let R := A (BITOR) B. ++ * Bitmaps A and B will not be modified, ++ * except when bitmap R is an alias of A or B. ++ * Bitmaps must have same size. ++ */ ++void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) ++{ ++ int i; ++ uint64_t j; ++ ++ assert(a->orig_size == result->orig_size); ++ assert(b->orig_size == result->orig_size); ++ ++ if ((!hbitmap_count(a) && result == b) || ++ (!hbitmap_count(b) && result == a)) { ++ return; ++ } ++ ++ if (!hbitmap_count(a) && !hbitmap_count(b)) { ++ hbitmap_reset_all(result); ++ return; ++ } ++ ++ if (a->granularity != b->granularity) { ++ if ((a != result) && (b != result)) { ++ hbitmap_reset_all(result); ++ } ++ if (a != result) { ++ hbitmap_sparse_merge(result, a); ++ } ++ if (b != result) { ++ hbitmap_sparse_merge(result, b); ++ } ++ return; ++ } ++ ++ /* This merge is O(size), as BITS_PER_LONG and HBITMAP_LEVELS are constant. ++ * It may be possible to improve running times for sparsely populated maps ++ * by using hbitmap_iter_next, but this is suboptimal for dense maps. ++ */ ++ assert(a->size == b->size); ++ for (i = HBITMAP_LEVELS - 1; i >= 0; i--) { ++ for (j = 0; j < a->sizes[i]; j++) { ++ result->levels[i][j] = a->levels[i][j] | b->levels[i][j]; ++ } ++ } ++ ++ /* Recompute the dirty count */ ++ result->count = hb_count_between(result, 0, result->size - 1); ++} ++ ++char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) ++{ ++ size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long); ++ char *data = (char *)bitmap->levels[HBITMAP_LEVELS - 1]; ++ char *hash = NULL; ++ qcrypto_hash_digest(QCRYPTO_HASH_ALG_SHA256, data, size, &hash, errp); ++ ++ return hash; ++} +diff --git a/qcow2/lib/util/host-utils.c b/qcow2/lib/util/host-utils.c +new file mode 100644 +index 00000000..fb91bcba +--- /dev/null ++++ b/qcow2/lib/util/host-utils.c +@@ -0,0 +1,448 @@ ++/* ++ * Utility compute operations used by translated code. ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * Copyright (c) 2007 Aurelien Jarno ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/host-utils.h" ++ ++#ifndef CONFIG_INT128 ++/* Long integer helpers */ ++static inline void mul64(uint64_t *plow, uint64_t *phigh, ++ uint64_t a, uint64_t b) ++{ ++ typedef union { ++ uint64_t ll; ++ struct { ++#if HOST_BIG_ENDIAN ++ uint32_t high, low; ++#else ++ uint32_t low, high; ++#endif ++ } l; ++ } LL; ++ LL rl, rm, rn, rh, a0, b0; ++ uint64_t c; ++ ++ a0.ll = a; ++ b0.ll = b; ++ ++ rl.ll = (uint64_t)a0.l.low * b0.l.low; ++ rm.ll = (uint64_t)a0.l.low * b0.l.high; ++ rn.ll = (uint64_t)a0.l.high * b0.l.low; ++ rh.ll = (uint64_t)a0.l.high * b0.l.high; ++ ++ c = (uint64_t)rl.l.high + rm.l.low + rn.l.low; ++ rl.l.high = c; ++ c >>= 32; ++ c = c + rm.l.high + rn.l.high + rh.l.low; ++ rh.l.low = c; ++ rh.l.high += (uint32_t)(c >> 32); ++ ++ *plow = rl.ll; ++ *phigh = rh.ll; ++} ++ ++/* Unsigned 64x64 -> 128 multiplication */ ++void mulu64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) ++{ ++ mul64(plow, phigh, a, b); ++} ++ ++/* Signed 64x64 -> 128 multiplication */ ++void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b) ++{ ++ uint64_t rh; ++ ++ mul64(plow, &rh, a, b); ++ ++ /* Adjust for signs. */ ++ if (b < 0) { ++ rh -= a; ++ } ++ if (a < 0) { ++ rh -= b; ++ } ++ *phigh = rh; ++} ++ ++/* ++ * Unsigned 128-by-64 division. ++ * Returns the remainder. ++ * Returns quotient via plow and phigh. ++ * Also returns the remainder via the function return value. ++ */ ++uint64_t divu128(uint64_t *plow, uint64_t *phigh, uint64_t divisor) ++{ ++ uint64_t dhi = *phigh; ++ uint64_t dlo = *plow; ++ uint64_t rem, dhighest; ++ int sh; ++ ++ if (divisor == 0 || dhi == 0) { ++ *plow = dlo / divisor; ++ *phigh = 0; ++ return dlo % divisor; ++ } else { ++ sh = clz64(divisor); ++ ++ if (dhi < divisor) { ++ if (sh != 0) { ++ /* normalize the divisor, shifting the dividend accordingly */ ++ divisor <<= sh; ++ dhi = (dhi << sh) | (dlo >> (64 - sh)); ++ dlo <<= sh; ++ } ++ ++ *phigh = 0; ++ *plow = udiv_qrnnd(&rem, dhi, dlo, divisor); ++ } else { ++ if (sh != 0) { ++ /* normalize the divisor, shifting the dividend accordingly */ ++ divisor <<= sh; ++ dhighest = dhi >> (64 - sh); ++ dhi = (dhi << sh) | (dlo >> (64 - sh)); ++ dlo <<= sh; ++ ++ *phigh = udiv_qrnnd(&dhi, dhighest, dhi, divisor); ++ } else { ++ /** ++ * dhi >= divisor ++ * Since the MSB of divisor is set (sh == 0), ++ * (dhi - divisor) < divisor ++ * ++ * Thus, the high part of the quotient is 1, and we can ++ * calculate the low part with a single call to udiv_qrnnd ++ * after subtracting divisor from dhi ++ */ ++ dhi -= divisor; ++ *phigh = 1; ++ } ++ ++ *plow = udiv_qrnnd(&rem, dhi, dlo, divisor); ++ } ++ ++ /* ++ * since the dividend/divisor might have been normalized, ++ * the remainder might also have to be shifted back ++ */ ++ return rem >> sh; ++ } ++} ++ ++/* ++ * Signed 128-by-64 division. ++ * Returns quotient via plow and phigh. ++ * Also returns the remainder via the function return value. ++ */ ++int64_t divs128(uint64_t *plow, int64_t *phigh, int64_t divisor) ++{ ++ bool neg_quotient = false, neg_remainder = false; ++ uint64_t unsig_hi = *phigh, unsig_lo = *plow; ++ uint64_t rem; ++ ++ if (*phigh < 0) { ++ neg_quotient = !neg_quotient; ++ neg_remainder = !neg_remainder; ++ ++ if (unsig_lo == 0) { ++ unsig_hi = -unsig_hi; ++ } else { ++ unsig_hi = ~unsig_hi; ++ unsig_lo = -unsig_lo; ++ } ++ } ++ ++ if (divisor < 0) { ++ neg_quotient = !neg_quotient; ++ ++ divisor = -divisor; ++ } ++ ++ rem = divu128(&unsig_lo, &unsig_hi, (uint64_t)divisor); ++ ++ if (neg_quotient) { ++ if (unsig_lo == 0) { ++ *phigh = -unsig_hi; ++ *plow = 0; ++ } else { ++ *phigh = ~unsig_hi; ++ *plow = -unsig_lo; ++ } ++ } else { ++ *phigh = unsig_hi; ++ *plow = unsig_lo; ++ } ++ ++ if (neg_remainder) { ++ return -rem; ++ } else { ++ return rem; ++ } ++} ++#endif ++ ++/** ++ * urshift - 128-bit Unsigned Right Shift. ++ * @plow: in/out - lower 64-bit integer. ++ * @phigh: in/out - higher 64-bit integer. ++ * @shift: in - bytes to shift, between 0 and 127. ++ * ++ * Result is zero-extended and stored in plow/phigh, which are ++ * input/output variables. Shift values outside the range will ++ * be mod to 128. In other words, the caller is responsible to ++ * verify/assert both the shift range and plow/phigh pointers. ++ */ ++void urshift(uint64_t *plow, uint64_t *phigh, int32_t shift) ++{ ++ shift &= 127; ++ if (shift == 0) { ++ return; ++ } ++ ++ uint64_t h = *phigh >> (shift & 63); ++ if (shift >= 64) { ++ *plow = h; ++ *phigh = 0; ++ } else { ++ *plow = (*plow >> (shift & 63)) | (*phigh << (64 - (shift & 63))); ++ *phigh = h; ++ } ++} ++ ++/** ++ * ulshift - 128-bit Unsigned Left Shift. ++ * @plow: in/out - lower 64-bit integer. ++ * @phigh: in/out - higher 64-bit integer. ++ * @shift: in - bytes to shift, between 0 and 127. ++ * @overflow: out - true if any 1-bit is shifted out. ++ * ++ * Result is zero-extended and stored in plow/phigh, which are ++ * input/output variables. Shift values outside the range will ++ * be mod to 128. In other words, the caller is responsible to ++ * verify/assert both the shift range and plow/phigh pointers. ++ */ ++void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow) ++{ ++ uint64_t low = *plow; ++ uint64_t high = *phigh; ++ ++ shift &= 127; ++ if (shift == 0) { ++ return; ++ } ++ ++ /* check if any bit will be shifted out */ ++ urshift(&low, &high, 128 - shift); ++ if (low | high) { ++ *overflow = true; ++ } ++ ++ if (shift >= 64) { ++ *phigh = *plow << (shift & 63); ++ *plow = 0; ++ } else { ++ *phigh = (*plow >> (64 - (shift & 63))) | (*phigh << (shift & 63)); ++ *plow = *plow << shift; ++ } ++} ++ ++/* ++ * Unsigned 256-by-128 division. ++ * Returns the remainder via r. ++ * Returns lower 128 bit of quotient. ++ * Needs a normalized divisor (most significant bit set to 1). ++ * ++ * Adapted from include/qemu/host-utils.h udiv_qrnnd, ++ * from the GNU Multi Precision Library - longlong.h __udiv_qrnnd ++ * (https://gmplib.org/repo/gmp/file/tip/longlong.h) ++ * ++ * Licensed under the GPLv2/LGPLv3 ++ */ ++static Int128 udiv256_qrnnd(Int128 *r, Int128 n1, Int128 n0, Int128 d) ++{ ++ Int128 d0, d1, q0, q1, r1, r0, m; ++ uint64_t mp0, mp1; ++ ++ d0 = int128_make64(int128_getlo(d)); ++ d1 = int128_make64(int128_gethi(d)); ++ ++ r1 = int128_remu(n1, d1); ++ q1 = int128_divu(n1, d1); ++ mp0 = int128_getlo(q1); ++ mp1 = int128_gethi(q1); ++ mulu128(&mp0, &mp1, int128_getlo(d0)); ++ m = int128_make128(mp0, mp1); ++ r1 = int128_make128(int128_gethi(n0), int128_getlo(r1)); ++ if (int128_ult(r1, m)) { ++ q1 = int128_sub(q1, int128_one()); ++ r1 = int128_add(r1, d); ++ if (int128_uge(r1, d)) { ++ if (int128_ult(r1, m)) { ++ q1 = int128_sub(q1, int128_one()); ++ r1 = int128_add(r1, d); ++ } ++ } ++ } ++ r1 = int128_sub(r1, m); ++ ++ r0 = int128_remu(r1, d1); ++ q0 = int128_divu(r1, d1); ++ mp0 = int128_getlo(q0); ++ mp1 = int128_gethi(q0); ++ mulu128(&mp0, &mp1, int128_getlo(d0)); ++ m = int128_make128(mp0, mp1); ++ r0 = int128_make128(int128_getlo(n0), int128_getlo(r0)); ++ if (int128_ult(r0, m)) { ++ q0 = int128_sub(q0, int128_one()); ++ r0 = int128_add(r0, d); ++ if (int128_uge(r0, d)) { ++ if (int128_ult(r0, m)) { ++ q0 = int128_sub(q0, int128_one()); ++ r0 = int128_add(r0, d); ++ } ++ } ++ } ++ r0 = int128_sub(r0, m); ++ ++ *r = r0; ++ return int128_or(int128_lshift(q1, 64), q0); ++} ++ ++/* ++ * Unsigned 256-by-128 division. ++ * Returns the remainder. ++ * Returns quotient via plow and phigh. ++ * Also returns the remainder via the function return value. ++ */ ++Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor) ++{ ++ Int128 dhi = *phigh; ++ Int128 dlo = *plow; ++ Int128 rem, dhighest; ++ int sh; ++ ++ if (!int128_nz(divisor) || !int128_nz(dhi)) { ++ *plow = int128_divu(dlo, divisor); ++ *phigh = int128_zero(); ++ return int128_remu(dlo, divisor); ++ } else { ++ sh = clz128(divisor); ++ ++ if (int128_ult(dhi, divisor)) { ++ if (sh != 0) { ++ /* normalize the divisor, shifting the dividend accordingly */ ++ divisor = int128_lshift(divisor, sh); ++ dhi = int128_or(int128_lshift(dhi, sh), ++ int128_urshift(dlo, (128 - sh))); ++ dlo = int128_lshift(dlo, sh); ++ } ++ ++ *phigh = int128_zero(); ++ *plow = udiv256_qrnnd(&rem, dhi, dlo, divisor); ++ } else { ++ if (sh != 0) { ++ /* normalize the divisor, shifting the dividend accordingly */ ++ divisor = int128_lshift(divisor, sh); ++ dhighest = int128_rshift(dhi, (128 - sh)); ++ dhi = int128_or(int128_lshift(dhi, sh), ++ int128_urshift(dlo, (128 - sh))); ++ dlo = int128_lshift(dlo, sh); ++ ++ *phigh = udiv256_qrnnd(&dhi, dhighest, dhi, divisor); ++ } else { ++ /* ++ * dhi >= divisor ++ * Since the MSB of divisor is set (sh == 0), ++ * (dhi - divisor) < divisor ++ * ++ * Thus, the high part of the quotient is 1, and we can ++ * calculate the low part with a single call to udiv_qrnnd ++ * after subtracting divisor from dhi ++ */ ++ dhi = int128_sub(dhi, divisor); ++ *phigh = int128_one(); ++ } ++ ++ *plow = udiv256_qrnnd(&rem, dhi, dlo, divisor); ++ } ++ ++ /* ++ * since the dividend/divisor might have been normalized, ++ * the remainder might also have to be shifted back ++ */ ++ rem = int128_urshift(rem, sh); ++ return rem; ++ } ++} ++ ++/* ++ * Signed 256-by-128 division. ++ * Returns quotient via plow and phigh. ++ * Also returns the remainder via the function return value. ++ */ ++Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor) ++{ ++ bool neg_quotient = false, neg_remainder = false; ++ Int128 unsig_hi = *phigh, unsig_lo = *plow; ++ Int128 rem; ++ ++ if (!int128_nonneg(*phigh)) { ++ neg_quotient = !neg_quotient; ++ neg_remainder = !neg_remainder; ++ ++ if (!int128_nz(unsig_lo)) { ++ unsig_hi = int128_neg(unsig_hi); ++ } else { ++ unsig_hi = int128_not(unsig_hi); ++ unsig_lo = int128_neg(unsig_lo); ++ } ++ } ++ ++ if (!int128_nonneg(divisor)) { ++ neg_quotient = !neg_quotient; ++ ++ divisor = int128_neg(divisor); ++ } ++ ++ rem = divu256(&unsig_lo, &unsig_hi, divisor); ++ ++ if (neg_quotient) { ++ if (!int128_nz(unsig_lo)) { ++ *phigh = int128_neg(unsig_hi); ++ *plow = int128_zero(); ++ } else { ++ *phigh = int128_not(unsig_hi); ++ *plow = int128_neg(unsig_lo); ++ } ++ } else { ++ *phigh = unsig_hi; ++ *plow = unsig_lo; ++ } ++ ++ if (neg_remainder) { ++ return int128_neg(rem); ++ } else { ++ return rem; ++ } ++} +diff --git a/qcow2/lib/util/id.c b/qcow2/lib/util/id.c +new file mode 100644 +index 00000000..ded41c50 +--- /dev/null ++++ b/qcow2/lib/util/id.c +@@ -0,0 +1,69 @@ ++/* ++ * Dealing with identifiers ++ * ++ * Copyright (C) 2014 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2.1 ++ * or later. See the COPYING.LIB file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/ctype.h" ++#include "qemu/id.h" ++ ++bool id_wellformed(const char *id) ++{ ++ int i; ++ ++ if (!qemu_isalpha(id[0])) { ++ return false; ++ } ++ for (i = 1; id[i]; i++) { ++ if (!qemu_isalnum(id[i]) && !strchr("-._", id[i])) { ++ return false; ++ } ++ } ++ return true; ++} ++ ++#define ID_SPECIAL_CHAR '#' ++ ++static const char *const id_subsys_str[ID_MAX] = { ++ [ID_QDEV] = "qdev", ++ [ID_BLOCK] = "block", ++ [ID_CHR] = "chr", ++ [ID_NET] = "net", ++}; ++ ++/* ++ * Generates an ID of the form PREFIX SUBSYSTEM NUMBER ++ * where: ++ * ++ * - PREFIX is the reserved character '#' ++ * - SUBSYSTEM identifies the subsystem creating the ID ++ * - NUMBER is a decimal number unique within SUBSYSTEM. ++ * ++ * Example: "#block146" ++ * ++ * Note that these IDs do not satisfy id_wellformed(). ++ * ++ * The caller is responsible for freeing the returned string with g_free() ++ */ ++char *id_generate(IdSubSystems id) ++{ ++ static uint64_t id_counters[ID_MAX]; ++ uint32_t rnd; ++ ++ assert(id < ARRAY_SIZE(id_subsys_str)); ++ assert(id_subsys_str[id]); ++ ++ rnd = g_random_int_range(0, 100); ++ ++ return g_strdup_printf("%c%s%" PRIu64 "%02" PRId32, ID_SPECIAL_CHAR, ++ id_subsys_str[id], ++ id_counters[id]++, ++ rnd); ++} +diff --git a/qcow2/lib/util/iov.c b/qcow2/lib/util/iov.c +new file mode 100644 +index 00000000..7e73948f +--- /dev/null ++++ b/qcow2/lib/util/iov.c +@@ -0,0 +1,712 @@ ++/* ++ * Helpers for getting linearized buffers from iov / filling buffers into iovs ++ * ++ * Copyright IBM, Corp. 2007, 2008 ++ * Copyright (C) 2010 Red Hat, Inc. ++ * ++ * Author(s): ++ * Anthony Liguori ++ * Amit Shah ++ * Michael Tokarev ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/iov.h" ++#include "qemu/sockets.h" ++#include "qemu/cutils.h" ++ ++size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, const void *buf, size_t bytes) ++{ ++ size_t done; ++ unsigned int i; ++ for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { ++ if (offset < iov[i].iov_len) { ++ size_t len = MIN(iov[i].iov_len - offset, bytes - done); ++ memcpy(iov[i].iov_base + offset, buf + done, len); ++ done += len; ++ offset = 0; ++ } else { ++ offset -= iov[i].iov_len; ++ } ++ } ++ assert(offset == 0); ++ return done; ++} ++ ++size_t iov_to_buf_full(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, void *buf, size_t bytes) ++{ ++ size_t done; ++ unsigned int i; ++ for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { ++ if (offset < iov[i].iov_len) { ++ size_t len = MIN(iov[i].iov_len - offset, bytes - done); ++ memcpy(buf + done, iov[i].iov_base + offset, len); ++ done += len; ++ offset = 0; ++ } else { ++ offset -= iov[i].iov_len; ++ } ++ } ++ assert(offset == 0); ++ return done; ++} ++ ++size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, ++ size_t offset, int fillc, size_t bytes) ++{ ++ size_t done; ++ unsigned int i; ++ for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { ++ if (offset < iov[i].iov_len) { ++ size_t len = MIN(iov[i].iov_len - offset, bytes - done); ++ memset(iov[i].iov_base + offset, fillc, len); ++ done += len; ++ offset = 0; ++ } else { ++ offset -= iov[i].iov_len; ++ } ++ } ++ assert(offset == 0); ++ return done; ++} ++ ++size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt) ++{ ++ size_t len; ++ unsigned int i; ++ ++ len = 0; ++ for (i = 0; i < iov_cnt; i++) { ++ len += iov[i].iov_len; ++ } ++ return len; ++} ++ ++/* helper function for iov_send_recv() */ ++static ssize_t ++do_send_recv(int sockfd, struct iovec *iov, unsigned iov_cnt, bool do_send) ++{ ++#ifdef CONFIG_POSIX ++ ssize_t ret; ++ struct msghdr msg; ++ memset(&msg, 0, sizeof(msg)); ++ msg.msg_iov = iov; ++ msg.msg_iovlen = iov_cnt; ++ do { ++ ret = do_send ++ ? sendmsg(sockfd, &msg, 0) ++ : recvmsg(sockfd, &msg, 0); ++ } while (ret < 0 && errno == EINTR); ++ return ret; ++#else ++ /* else send piece-by-piece */ ++ /*XXX Note: windows has WSASend() and WSARecv() */ ++ unsigned i = 0; ++ ssize_t ret = 0; ++ ssize_t off = 0; ++ while (i < iov_cnt) { ++ ssize_t r = do_send ++ ? send(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0) ++ : recv(sockfd, iov[i].iov_base + off, iov[i].iov_len - off, 0); ++ if (r > 0) { ++ ret += r; ++ off += r; ++ if (off < iov[i].iov_len) { ++ continue; ++ } ++ } else if (!r) { ++ break; ++ } else if (errno == EINTR) { ++ continue; ++ } else { ++ /* else it is some "other" error, ++ * only return if there was no data processed. */ ++ if (ret == 0) { ++ ret = -1; ++ } ++ break; ++ } ++ off = 0; ++ i++; ++ } ++ return ret; ++#endif ++} ++ ++ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt, ++ size_t offset, size_t bytes, ++ bool do_send) ++{ ++ ssize_t total = 0; ++ ssize_t ret; ++ size_t orig_len, tail; ++ unsigned niov; ++ struct iovec *local_iov, *iov; ++ ++ if (bytes <= 0) { ++ return 0; ++ } ++ ++ local_iov = g_new0(struct iovec, iov_cnt); ++ iov_copy(local_iov, iov_cnt, _iov, iov_cnt, offset, bytes); ++ offset = 0; ++ iov = local_iov; ++ ++ while (bytes > 0) { ++ /* Find the start position, skipping `offset' bytes: ++ * first, skip all full-sized vector elements, */ ++ for (niov = 0; niov < iov_cnt && offset >= iov[niov].iov_len; ++niov) { ++ offset -= iov[niov].iov_len; ++ } ++ ++ /* niov == iov_cnt would only be valid if bytes == 0, which ++ * we already ruled out in the loop condition. */ ++ assert(niov < iov_cnt); ++ iov += niov; ++ iov_cnt -= niov; ++ ++ if (offset) { ++ /* second, skip `offset' bytes from the (now) first element, ++ * undo it on exit */ ++ iov[0].iov_base += offset; ++ iov[0].iov_len -= offset; ++ } ++ /* Find the end position skipping `bytes' bytes: */ ++ /* first, skip all full-sized elements */ ++ tail = bytes; ++ for (niov = 0; niov < iov_cnt && iov[niov].iov_len <= tail; ++niov) { ++ tail -= iov[niov].iov_len; ++ } ++ if (tail) { ++ /* second, fixup the last element, and remember the original ++ * length */ ++ assert(niov < iov_cnt); ++ assert(iov[niov].iov_len > tail); ++ orig_len = iov[niov].iov_len; ++ iov[niov++].iov_len = tail; ++ ret = do_send_recv(sockfd, iov, niov, do_send); ++ /* Undo the changes above before checking for errors */ ++ iov[niov-1].iov_len = orig_len; ++ } else { ++ ret = do_send_recv(sockfd, iov, niov, do_send); ++ } ++ if (offset) { ++ iov[0].iov_base -= offset; ++ iov[0].iov_len += offset; ++ } ++ ++ if (ret < 0) { ++ assert(errno != EINTR); ++ g_free(local_iov); ++ if (errno == EAGAIN && total > 0) { ++ return total; ++ } ++ return -1; ++ } ++ ++ if (ret == 0 && !do_send) { ++ /* recv returns 0 when the peer has performed an orderly ++ * shutdown. */ ++ break; ++ } ++ ++ /* Prepare for the next iteration */ ++ offset += ret; ++ total += ret; ++ bytes -= ret; ++ } ++ ++ g_free(local_iov); ++ return total; ++} ++ ++ ++void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, ++ FILE *fp, const char *prefix, size_t limit) ++{ ++ int v; ++ size_t size = 0; ++ char *buf; ++ ++ for (v = 0; v < iov_cnt; v++) { ++ size += iov[v].iov_len; ++ } ++ size = size > limit ? limit : size; ++ buf = g_malloc(size); ++ iov_to_buf(iov, iov_cnt, 0, buf, size); ++ qemu_hexdump(fp, prefix, buf, size); ++ g_free(buf); ++} ++ ++unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, ++ const struct iovec *iov, unsigned int iov_cnt, ++ size_t offset, size_t bytes) ++{ ++ size_t len; ++ unsigned int i, j; ++ for (i = 0, j = 0; ++ i < iov_cnt && j < dst_iov_cnt && (offset || bytes); i++) { ++ if (offset >= iov[i].iov_len) { ++ offset -= iov[i].iov_len; ++ continue; ++ } ++ len = MIN(bytes, iov[i].iov_len - offset); ++ ++ dst_iov[j].iov_base = iov[i].iov_base + offset; ++ dst_iov[j].iov_len = len; ++ j++; ++ bytes -= len; ++ offset = 0; ++ } ++ assert(offset == 0); ++ return j; ++} ++ ++/* io vectors */ ++ ++void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint) ++{ ++ qiov->iov = g_new(struct iovec, alloc_hint); ++ qiov->niov = 0; ++ qiov->nalloc = alloc_hint; ++ qiov->size = 0; ++} ++ ++void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov) ++{ ++ int i; ++ ++ qiov->iov = iov; ++ qiov->niov = niov; ++ qiov->nalloc = -1; ++ qiov->size = 0; ++ for (i = 0; i < niov; i++) ++ qiov->size += iov[i].iov_len; ++} ++ ++void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) ++{ ++ assert(qiov->nalloc != -1); ++ ++ if (qiov->niov == qiov->nalloc) { ++ qiov->nalloc = 2 * qiov->nalloc + 1; ++ qiov->iov = g_renew(struct iovec, qiov->iov, qiov->nalloc); ++ } ++ qiov->iov[qiov->niov].iov_base = base; ++ qiov->iov[qiov->niov].iov_len = len; ++ qiov->size += len; ++ ++qiov->niov; ++} ++ ++/* ++ * Concatenates (partial) iovecs from src_iov to the end of dst. ++ * It starts copying after skipping `soffset' bytes at the ++ * beginning of src and adds individual vectors from src to ++ * dst copies up to `sbytes' bytes total, or up to the end ++ * of src_iov if it comes first. This way, it is okay to specify ++ * very large value for `sbytes' to indicate "up to the end ++ * of src". ++ * Only vector pointers are processed, not the actual data buffers. ++ */ ++size_t qemu_iovec_concat_iov(QEMUIOVector *dst, ++ struct iovec *src_iov, unsigned int src_cnt, ++ size_t soffset, size_t sbytes) ++{ ++ int i; ++ size_t done; ++ ++ if (!sbytes) { ++ return 0; ++ } ++ assert(dst->nalloc != -1); ++ for (i = 0, done = 0; done < sbytes && i < src_cnt; i++) { ++ if (soffset < src_iov[i].iov_len) { ++ size_t len = MIN(src_iov[i].iov_len - soffset, sbytes - done); ++ qemu_iovec_add(dst, src_iov[i].iov_base + soffset, len); ++ done += len; ++ soffset = 0; ++ } else { ++ soffset -= src_iov[i].iov_len; ++ } ++ } ++ assert(soffset == 0); /* offset beyond end of src */ ++ ++ return done; ++} ++ ++/* ++ * Concatenates (partial) iovecs from src to the end of dst. ++ * It starts copying after skipping `soffset' bytes at the ++ * beginning of src and adds individual vectors from src to ++ * dst copies up to `sbytes' bytes total, or up to the end ++ * of src if it comes first. This way, it is okay to specify ++ * very large value for `sbytes' to indicate "up to the end ++ * of src". ++ * Only vector pointers are processed, not the actual data buffers. ++ */ ++void qemu_iovec_concat(QEMUIOVector *dst, ++ QEMUIOVector *src, size_t soffset, size_t sbytes) ++{ ++ qemu_iovec_concat_iov(dst, src->iov, src->niov, soffset, sbytes); ++} ++ ++/* ++ * qiov_find_iov ++ * ++ * Return pointer to iovec structure, where byte at @offset in original vector ++ * @iov exactly is. ++ * Set @remaining_offset to be offset inside that iovec to the same byte. ++ */ ++static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset, ++ size_t *remaining_offset) ++{ ++ while (offset > 0 && offset >= iov->iov_len) { ++ offset -= iov->iov_len; ++ iov++; ++ } ++ *remaining_offset = offset; ++ ++ return iov; ++} ++ ++/* ++ * qemu_iovec_slice ++ * ++ * Find subarray of iovec's, containing requested range. @head would ++ * be offset in first iov (returned by the function), @tail would be ++ * count of extra bytes in last iovec (returned iov + @niov - 1). ++ */ ++struct iovec *qemu_iovec_slice(QEMUIOVector *qiov, ++ size_t offset, size_t len, ++ size_t *head, size_t *tail, int *niov) ++{ ++ struct iovec *iov, *end_iov; ++ ++ assert(offset + len <= qiov->size); ++ ++ iov = iov_skip_offset(qiov->iov, offset, head); ++ end_iov = iov_skip_offset(iov, *head + len, tail); ++ ++ if (*tail > 0) { ++ assert(*tail < end_iov->iov_len); ++ *tail = end_iov->iov_len - *tail; ++ end_iov++; ++ } ++ ++ *niov = end_iov - iov; ++ ++ return iov; ++} ++ ++int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len) ++{ ++ size_t head, tail; ++ int niov; ++ ++ qemu_iovec_slice(qiov, offset, len, &head, &tail, &niov); ++ ++ return niov; ++} ++ ++/* ++ * Check if the contents of subrange of qiov data is all zeroes. ++ */ ++bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes) ++{ ++ struct iovec *iov; ++ size_t current_offset; ++ ++ assert(offset + bytes <= qiov->size); ++ ++ iov = iov_skip_offset(qiov->iov, offset, ¤t_offset); ++ ++ while (bytes) { ++ uint8_t *base = (uint8_t *)iov->iov_base + current_offset; ++ size_t len = MIN(iov->iov_len - current_offset, bytes); ++ ++ if (!buffer_is_zero(base, len)) { ++ return false; ++ } ++ ++ current_offset = 0; ++ bytes -= len; ++ iov++; ++ } ++ ++ return true; ++} ++ ++void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source, ++ size_t offset, size_t len) ++{ ++ struct iovec *slice_iov; ++ int slice_niov; ++ size_t slice_head, slice_tail; ++ ++ assert(source->size >= len); ++ assert(source->size - len >= offset); ++ ++ slice_iov = qemu_iovec_slice(source, offset, len, ++ &slice_head, &slice_tail, &slice_niov); ++ if (slice_niov == 1) { ++ qemu_iovec_init_buf(qiov, slice_iov[0].iov_base + slice_head, len); ++ } else { ++ qemu_iovec_init(qiov, slice_niov); ++ qemu_iovec_concat_iov(qiov, slice_iov, slice_niov, slice_head, len); ++ } ++} ++ ++void qemu_iovec_destroy(QEMUIOVector *qiov) ++{ ++ if (qiov->nalloc != -1) { ++ g_free(qiov->iov); ++ } ++ ++ memset(qiov, 0, sizeof(*qiov)); ++} ++ ++void qemu_iovec_reset(QEMUIOVector *qiov) ++{ ++ assert(qiov->nalloc != -1); ++ ++ qiov->niov = 0; ++ qiov->size = 0; ++} ++ ++size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset, ++ void *buf, size_t bytes) ++{ ++ return iov_to_buf(qiov->iov, qiov->niov, offset, buf, bytes); ++} ++ ++size_t qemu_iovec_from_buf(QEMUIOVector *qiov, size_t offset, ++ const void *buf, size_t bytes) ++{ ++ return iov_from_buf(qiov->iov, qiov->niov, offset, buf, bytes); ++} ++ ++size_t qemu_iovec_memset(QEMUIOVector *qiov, size_t offset, ++ int fillc, size_t bytes) ++{ ++ return iov_memset(qiov->iov, qiov->niov, offset, fillc, bytes); ++} ++ ++/** ++ * Check that I/O vector contents are identical ++ * ++ * The IO vectors must have the same structure (same length of all parts). ++ * A typical usage is to compare vectors created with qemu_iovec_clone(). ++ * ++ * @a: I/O vector ++ * @b: I/O vector ++ * @ret: Offset to first mismatching byte or -1 if match ++ */ ++ssize_t qemu_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) ++{ ++ int i; ++ ssize_t offset = 0; ++ ++ assert(a->niov == b->niov); ++ for (i = 0; i < a->niov; i++) { ++ size_t len = 0; ++ uint8_t *p = (uint8_t *)a->iov[i].iov_base; ++ uint8_t *q = (uint8_t *)b->iov[i].iov_base; ++ ++ assert(a->iov[i].iov_len == b->iov[i].iov_len); ++ while (len < a->iov[i].iov_len && *p++ == *q++) { ++ len++; ++ } ++ ++ offset += len; ++ ++ if (len != a->iov[i].iov_len) { ++ return offset; ++ } ++ } ++ return -1; ++} ++ ++typedef struct { ++ int src_index; ++ struct iovec *src_iov; ++ void *dest_base; ++} IOVectorSortElem; ++ ++static int sortelem_cmp_src_base(const void *a, const void *b) ++{ ++ const IOVectorSortElem *elem_a = a; ++ const IOVectorSortElem *elem_b = b; ++ ++ /* Don't overflow */ ++ if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) { ++ return -1; ++ } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) { ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++static int sortelem_cmp_src_index(const void *a, const void *b) ++{ ++ const IOVectorSortElem *elem_a = a; ++ const IOVectorSortElem *elem_b = b; ++ ++ return elem_a->src_index - elem_b->src_index; ++} ++ ++/** ++ * Copy contents of I/O vector ++ * ++ * The relative relationships of overlapping iovecs are preserved. This is ++ * necessary to ensure identical semantics in the cloned I/O vector. ++ */ ++void qemu_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, void *buf) ++{ ++ g_autofree IOVectorSortElem *sortelems = g_new(IOVectorSortElem, src->niov); ++ void *last_end; ++ int i; ++ ++ /* Sort by source iovecs by base address */ ++ for (i = 0; i < src->niov; i++) { ++ sortelems[i].src_index = i; ++ sortelems[i].src_iov = &src->iov[i]; ++ } ++ qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base); ++ ++ /* Allocate buffer space taking into account overlapping iovecs */ ++ last_end = NULL; ++ for (i = 0; i < src->niov; i++) { ++ struct iovec *cur = sortelems[i].src_iov; ++ ptrdiff_t rewind = 0; ++ ++ /* Detect overlap */ ++ if (last_end && last_end > cur->iov_base) { ++ rewind = last_end - cur->iov_base; ++ } ++ ++ sortelems[i].dest_base = buf - rewind; ++ buf += cur->iov_len - MIN(rewind, cur->iov_len); ++ last_end = MAX(cur->iov_base + cur->iov_len, last_end); ++ } ++ ++ /* Sort by source iovec index and build destination iovec */ ++ qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index); ++ for (i = 0; i < src->niov; i++) { ++ qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len); ++ } ++} ++ ++void iov_discard_undo(IOVDiscardUndo *undo) ++{ ++ /* Restore original iovec if it was modified */ ++ if (undo->modified_iov) { ++ *undo->modified_iov = undo->orig; ++ } ++} ++ ++size_t iov_discard_front_undoable(struct iovec **iov, ++ unsigned int *iov_cnt, ++ size_t bytes, ++ IOVDiscardUndo *undo) ++{ ++ size_t total = 0; ++ struct iovec *cur; ++ ++ if (undo) { ++ undo->modified_iov = NULL; ++ } ++ ++ for (cur = *iov; *iov_cnt > 0; cur++) { ++ if (cur->iov_len > bytes) { ++ if (undo) { ++ undo->modified_iov = cur; ++ undo->orig = *cur; ++ } ++ ++ cur->iov_base += bytes; ++ cur->iov_len -= bytes; ++ total += bytes; ++ break; ++ } ++ ++ bytes -= cur->iov_len; ++ total += cur->iov_len; ++ *iov_cnt -= 1; ++ } ++ ++ *iov = cur; ++ return total; ++} ++ ++size_t iov_discard_front(struct iovec **iov, unsigned int *iov_cnt, ++ size_t bytes) ++{ ++ return iov_discard_front_undoable(iov, iov_cnt, bytes, NULL); ++} ++ ++size_t iov_discard_back_undoable(struct iovec *iov, ++ unsigned int *iov_cnt, ++ size_t bytes, ++ IOVDiscardUndo *undo) ++{ ++ size_t total = 0; ++ struct iovec *cur; ++ ++ if (undo) { ++ undo->modified_iov = NULL; ++ } ++ ++ if (*iov_cnt == 0) { ++ return 0; ++ } ++ ++ cur = iov + (*iov_cnt - 1); ++ ++ while (*iov_cnt > 0) { ++ if (cur->iov_len > bytes) { ++ if (undo) { ++ undo->modified_iov = cur; ++ undo->orig = *cur; ++ } ++ ++ cur->iov_len -= bytes; ++ total += bytes; ++ break; ++ } ++ ++ bytes -= cur->iov_len; ++ total += cur->iov_len; ++ cur--; ++ *iov_cnt -= 1; ++ } ++ ++ return total; ++} ++ ++size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt, ++ size_t bytes) ++{ ++ return iov_discard_back_undoable(iov, iov_cnt, bytes, NULL); ++} ++ ++void qemu_iovec_discard_back(QEMUIOVector *qiov, size_t bytes) ++{ ++ size_t total; ++ unsigned int niov = qiov->niov; ++ ++ assert(qiov->size >= bytes); ++ total = iov_discard_back(qiov->iov, &niov, bytes); ++ assert(total == bytes); ++ ++ qiov->niov = niov; ++ qiov->size -= bytes; ++} +diff --git a/qcow2/lib/util/lockcnt.c b/qcow2/lib/util/lockcnt.c +new file mode 100644 +index 00000000..5da36946 +--- /dev/null ++++ b/qcow2/lib/util/lockcnt.c +@@ -0,0 +1,399 @@ ++/* ++ * QemuLockCnt implementation ++ * ++ * Copyright Red Hat, Inc. 2017 ++ * ++ * Author: ++ * Paolo Bonzini ++ */ ++#include "qemu/osdep.h" ++#include "qemu/thread.h" ++#include "qemu/atomic.h" ++#include "trace.h" ++ ++#ifdef CONFIG_LINUX ++#include "qemu/futex.h" ++ ++/* On Linux, bits 0-1 are a futex-based lock, bits 2-31 are the counter. ++ * For the mutex algorithm see Ulrich Drepper's "Futexes Are Tricky" (ok, ++ * this is not the most relaxing citation I could make...). It is similar ++ * to mutex2 in the paper. ++ */ ++ ++#define QEMU_LOCKCNT_STATE_MASK 3 ++#define QEMU_LOCKCNT_STATE_FREE 0 /* free, uncontended */ ++#define QEMU_LOCKCNT_STATE_LOCKED 1 /* locked, uncontended */ ++#define QEMU_LOCKCNT_STATE_WAITING 2 /* locked, contended */ ++ ++#define QEMU_LOCKCNT_COUNT_STEP 4 ++#define QEMU_LOCKCNT_COUNT_SHIFT 2 ++ ++void qemu_lockcnt_init(QemuLockCnt *lockcnt) ++{ ++ lockcnt->count = 0; ++} ++ ++void qemu_lockcnt_destroy(QemuLockCnt *lockcnt) ++{ ++} ++ ++/* *val is the current value of lockcnt->count. ++ * ++ * If the lock is free, try a cmpxchg from *val to new_if_free; return ++ * true and set *val to the old value found by the cmpxchg in ++ * lockcnt->count. ++ * ++ * If the lock is taken, wait for it to be released and return false ++ * *without trying again to take the lock*. Again, set *val to the ++ * new value of lockcnt->count. ++ * ++ * If *waited is true on return, new_if_free's bottom two bits must not ++ * be QEMU_LOCKCNT_STATE_LOCKED on subsequent calls, because the caller ++ * does not know if there are other waiters. Furthermore, after *waited ++ * is set the caller has effectively acquired the lock. If it returns ++ * with the lock not taken, it must wake another futex waiter. ++ */ ++static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, ++ int new_if_free, bool *waited) ++{ ++ /* Fast path for when the lock is free. */ ++ if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_FREE) { ++ int expected = *val; ++ ++ trace_lockcnt_fast_path_attempt(lockcnt, expected, new_if_free); ++ *val = qatomic_cmpxchg(&lockcnt->count, expected, new_if_free); ++ if (*val == expected) { ++ trace_lockcnt_fast_path_success(lockcnt, expected, new_if_free); ++ *val = new_if_free; ++ return true; ++ } ++ } ++ ++ /* The slow path moves from locked to waiting if necessary, then ++ * does a futex wait. Both steps can be repeated ad nauseam, ++ * only getting out of the loop if we can have another shot at the ++ * fast path. Once we can, get out to compute the new destination ++ * value for the fast path. ++ */ ++ while ((*val & QEMU_LOCKCNT_STATE_MASK) != QEMU_LOCKCNT_STATE_FREE) { ++ if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_LOCKED) { ++ int expected = *val; ++ int new = expected - QEMU_LOCKCNT_STATE_LOCKED + QEMU_LOCKCNT_STATE_WAITING; ++ ++ trace_lockcnt_futex_wait_prepare(lockcnt, expected, new); ++ *val = qatomic_cmpxchg(&lockcnt->count, expected, new); ++ if (*val == expected) { ++ *val = new; ++ } ++ continue; ++ } ++ ++ if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_WAITING) { ++ *waited = true; ++ trace_lockcnt_futex_wait(lockcnt, *val); ++ qemu_futex_wait(&lockcnt->count, *val); ++ *val = qatomic_read(&lockcnt->count); ++ trace_lockcnt_futex_wait_resume(lockcnt, *val); ++ continue; ++ } ++ ++ abort(); ++ } ++ return false; ++} ++ ++static void lockcnt_wake(QemuLockCnt *lockcnt) ++{ ++ trace_lockcnt_futex_wake(lockcnt); ++ qemu_futex_wake(&lockcnt->count, 1); ++} ++ ++void qemu_lockcnt_inc(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ bool waited = false; ++ ++ for (;;) { ++ if (val >= QEMU_LOCKCNT_COUNT_STEP) { ++ int expected = val; ++ val = qatomic_cmpxchg(&lockcnt->count, val, ++ val + QEMU_LOCKCNT_COUNT_STEP); ++ if (val == expected) { ++ break; ++ } ++ } else { ++ /* The fast path is (0, unlocked)->(1, unlocked). */ ++ if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, QEMU_LOCKCNT_COUNT_STEP, ++ &waited)) { ++ break; ++ } ++ } ++ } ++ ++ /* If we were woken by another thread, we should also wake one because ++ * we are effectively releasing the lock that was given to us. This is ++ * the case where qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING ++ * in the low bits, and qemu_lockcnt_inc_and_unlock would find it and ++ * wake someone. ++ */ ++ if (waited) { ++ lockcnt_wake(lockcnt); ++ } ++} ++ ++void qemu_lockcnt_dec(QemuLockCnt *lockcnt) ++{ ++ qatomic_sub(&lockcnt->count, QEMU_LOCKCNT_COUNT_STEP); ++} ++ ++/* Decrement a counter, and return locked if it is decremented to zero. ++ * If the function returns true, it is impossible for the counter to ++ * become nonzero until the next qemu_lockcnt_unlock. ++ */ ++bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ int locked_state = QEMU_LOCKCNT_STATE_LOCKED; ++ bool waited = false; ++ ++ for (;;) { ++ if (val >= 2 * QEMU_LOCKCNT_COUNT_STEP) { ++ int expected = val; ++ val = qatomic_cmpxchg(&lockcnt->count, val, ++ val - QEMU_LOCKCNT_COUNT_STEP); ++ if (val == expected) { ++ break; ++ } ++ } else { ++ /* If count is going 1->0, take the lock. The fast path is ++ * (1, unlocked)->(0, locked) or (1, unlocked)->(0, waiting). ++ */ ++ if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, locked_state, &waited)) { ++ return true; ++ } ++ ++ if (waited) { ++ /* At this point we do not know if there are more waiters. Assume ++ * there are. ++ */ ++ locked_state = QEMU_LOCKCNT_STATE_WAITING; ++ } ++ } ++ } ++ ++ /* If we were woken by another thread, but we're returning in unlocked ++ * state, we should also wake a thread because we are effectively ++ * releasing the lock that was given to us. This is the case where ++ * qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING in the low ++ * bits, and qemu_lockcnt_unlock would find it and wake someone. ++ */ ++ if (waited) { ++ lockcnt_wake(lockcnt); ++ } ++ return false; ++} ++ ++/* If the counter is one, decrement it and return locked. Otherwise do ++ * nothing. ++ * ++ * If the function returns true, it is impossible for the counter to ++ * become nonzero until the next qemu_lockcnt_unlock. ++ */ ++bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ int locked_state = QEMU_LOCKCNT_STATE_LOCKED; ++ bool waited = false; ++ ++ while (val < 2 * QEMU_LOCKCNT_COUNT_STEP) { ++ /* If count is going 1->0, take the lock. The fast path is ++ * (1, unlocked)->(0, locked) or (1, unlocked)->(0, waiting). ++ */ ++ if (qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, locked_state, &waited)) { ++ return true; ++ } ++ ++ if (waited) { ++ /* At this point we do not know if there are more waiters. Assume ++ * there are. ++ */ ++ locked_state = QEMU_LOCKCNT_STATE_WAITING; ++ } ++ } ++ ++ /* If we were woken by another thread, but we're returning in unlocked ++ * state, we should also wake a thread because we are effectively ++ * releasing the lock that was given to us. This is the case where ++ * qemu_lockcnt_lock would leave QEMU_LOCKCNT_STATE_WAITING in the low ++ * bits, and qemu_lockcnt_inc_and_unlock would find it and wake someone. ++ */ ++ if (waited) { ++ lockcnt_wake(lockcnt); ++ } ++ return false; ++} ++ ++void qemu_lockcnt_lock(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ int step = QEMU_LOCKCNT_STATE_LOCKED; ++ bool waited = false; ++ ++ /* The third argument is only used if the low bits of val are 0 ++ * (QEMU_LOCKCNT_STATE_FREE), so just blindly mix in the desired ++ * state. ++ */ ++ while (!qemu_lockcnt_cmpxchg_or_wait(lockcnt, &val, val + step, &waited)) { ++ if (waited) { ++ /* At this point we do not know if there are more waiters. Assume ++ * there are. ++ */ ++ step = QEMU_LOCKCNT_STATE_WAITING; ++ } ++ } ++} ++ ++void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt) ++{ ++ int expected, new, val; ++ ++ val = qatomic_read(&lockcnt->count); ++ do { ++ expected = val; ++ new = (val + QEMU_LOCKCNT_COUNT_STEP) & ~QEMU_LOCKCNT_STATE_MASK; ++ trace_lockcnt_unlock_attempt(lockcnt, val, new); ++ val = qatomic_cmpxchg(&lockcnt->count, val, new); ++ } while (val != expected); ++ ++ trace_lockcnt_unlock_success(lockcnt, val, new); ++ if (val & QEMU_LOCKCNT_STATE_WAITING) { ++ lockcnt_wake(lockcnt); ++ } ++} ++ ++void qemu_lockcnt_unlock(QemuLockCnt *lockcnt) ++{ ++ int expected, new, val; ++ ++ val = qatomic_read(&lockcnt->count); ++ do { ++ expected = val; ++ new = val & ~QEMU_LOCKCNT_STATE_MASK; ++ trace_lockcnt_unlock_attempt(lockcnt, val, new); ++ val = qatomic_cmpxchg(&lockcnt->count, val, new); ++ } while (val != expected); ++ ++ trace_lockcnt_unlock_success(lockcnt, val, new); ++ if (val & QEMU_LOCKCNT_STATE_WAITING) { ++ lockcnt_wake(lockcnt); ++ } ++} ++ ++unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt) ++{ ++ return qatomic_read(&lockcnt->count) >> QEMU_LOCKCNT_COUNT_SHIFT; ++} ++#else ++void qemu_lockcnt_init(QemuLockCnt *lockcnt) ++{ ++ qemu_mutex_init(&lockcnt->mutex); ++ lockcnt->count = 0; ++} ++ ++void qemu_lockcnt_destroy(QemuLockCnt *lockcnt) ++{ ++ qemu_mutex_destroy(&lockcnt->mutex); ++} ++ ++void qemu_lockcnt_inc(QemuLockCnt *lockcnt) ++{ ++ int old; ++ for (;;) { ++ old = qatomic_read(&lockcnt->count); ++ if (old == 0) { ++ qemu_lockcnt_lock(lockcnt); ++ qemu_lockcnt_inc_and_unlock(lockcnt); ++ return; ++ } else { ++ if (qatomic_cmpxchg(&lockcnt->count, old, old + 1) == old) { ++ return; ++ } ++ } ++ } ++} ++ ++void qemu_lockcnt_dec(QemuLockCnt *lockcnt) ++{ ++ qatomic_dec(&lockcnt->count); ++} ++ ++/* Decrement a counter, and return locked if it is decremented to zero. ++ * It is impossible for the counter to become nonzero while the mutex ++ * is taken. ++ */ ++bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt) ++{ ++ int val = qatomic_read(&lockcnt->count); ++ while (val > 1) { ++ int old = qatomic_cmpxchg(&lockcnt->count, val, val - 1); ++ if (old != val) { ++ val = old; ++ continue; ++ } ++ ++ return false; ++ } ++ ++ qemu_lockcnt_lock(lockcnt); ++ if (qatomic_fetch_dec(&lockcnt->count) == 1) { ++ return true; ++ } ++ ++ qemu_lockcnt_unlock(lockcnt); ++ return false; ++} ++ ++/* Decrement a counter and return locked if it is decremented to zero. ++ * Otherwise do nothing. ++ * ++ * It is impossible for the counter to become nonzero while the mutex ++ * is taken. ++ */ ++bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt) ++{ ++ /* No need for acquire semantics if we return false. */ ++ int val = qatomic_read(&lockcnt->count); ++ if (val > 1) { ++ return false; ++ } ++ ++ qemu_lockcnt_lock(lockcnt); ++ if (qatomic_fetch_dec(&lockcnt->count) == 1) { ++ return true; ++ } ++ ++ qemu_lockcnt_inc_and_unlock(lockcnt); ++ return false; ++} ++ ++void qemu_lockcnt_lock(QemuLockCnt *lockcnt) ++{ ++ qemu_mutex_lock(&lockcnt->mutex); ++} ++ ++void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt) ++{ ++ qatomic_inc(&lockcnt->count); ++ qemu_mutex_unlock(&lockcnt->mutex); ++} ++ ++void qemu_lockcnt_unlock(QemuLockCnt *lockcnt) ++{ ++ qemu_mutex_unlock(&lockcnt->mutex); ++} ++ ++unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt) ++{ ++ return qatomic_read(&lockcnt->count); ++} ++#endif +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +new file mode 100644 +index 00000000..a0386cfe +--- /dev/null ++++ b/qcow2/lib/util/main-loop.c +@@ -0,0 +1,653 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/cutils.h" ++#include "qemu/timer.h" ++#include "sysemu/cpu-timers.h" ++#include "sysemu/replay.h" ++#include "qemu/main-loop.h" ++#include "block/aio.h" ++#include "block/thread-pool.h" ++#include "qemu/error-report.h" ++#include "qemu/queue.h" ++#include "qom/object.h" ++ ++#ifndef _WIN32 ++#include ++#endif ++ ++#ifndef _WIN32 ++ ++/* If we have signalfd, we mask out the signals we want to handle and then ++ * use signalfd to listen for them. We rely on whatever the current signal ++ * handler is to dispatch the signals when we receive them. ++ */ ++/* ++ * Disable CFI checks. ++ * We are going to call a signal handler directly. Such handler may or may not ++ * have been defined in our binary, so there's no guarantee that the pointer ++ * used to set the handler is a cfi-valid pointer. Since the handlers are ++ * stored in kernel memory, changing the handler to an attacker-defined ++ * function requires being able to call a sigaction() syscall, ++ * which is not as easy as overwriting a pointer in memory. ++ */ ++QEMU_DISABLE_CFI ++static void sigfd_handler(void *opaque) ++{ ++ int fd = (intptr_t)opaque; ++ struct qemu_signalfd_siginfo info; ++ struct sigaction action; ++ ssize_t len; ++ ++ while (1) { ++ len = RETRY_ON_EINTR(read(fd, &info, sizeof(info))); ++ ++ if (len == -1 && errno == EAGAIN) { ++ break; ++ } ++ ++ if (len != sizeof(info)) { ++ error_report("read from sigfd returned %zd: %s", len, ++ g_strerror(errno)); ++ return; ++ } ++ ++ sigaction(info.ssi_signo, NULL, &action); ++ if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) { ++ sigaction_invoke(&action, &info); ++ } else if (action.sa_handler) { ++ action.sa_handler(info.ssi_signo); ++ } ++ } ++} ++ ++static int qemu_signal_init(Error **errp) ++{ ++ int sigfd; ++ sigset_t set; ++ ++ /* ++ * SIG_IPI must be blocked in the main thread and must not be caught ++ * by sigwait() in the signal thread. Otherwise, the cpu thread will ++ * not catch it reliably. ++ */ ++ sigemptyset(&set); ++ sigaddset(&set, SIG_IPI); ++ sigaddset(&set, SIGIO); ++ sigaddset(&set, SIGALRM); ++ sigaddset(&set, SIGBUS); ++ /* SIGINT cannot be handled via signalfd, so that ^C can be used ++ * to interrupt QEMU when it is being run under gdb. SIGHUP and ++ * SIGTERM are also handled asynchronously, even though it is not ++ * strictly necessary, because they use the same handler as SIGINT. ++ */ ++ pthread_sigmask(SIG_BLOCK, &set, NULL); ++ ++ sigdelset(&set, SIG_IPI); ++ sigfd = qemu_signalfd(&set); ++ if (sigfd == -1) { ++ error_setg_errno(errp, errno, "failed to create signalfd"); ++ return -errno; ++ } ++ ++ g_unix_set_fd_nonblocking(sigfd, true, NULL); ++ ++ qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd); ++ ++ return 0; ++} ++ ++#else /* _WIN32 */ ++ ++static int qemu_signal_init(Error **errp) ++{ ++ return 0; ++} ++#endif ++ ++static AioContext *qemu_aio_context; ++static QEMUBH *qemu_notify_bh; ++ ++static void notify_event_cb(void *opaque) ++{ ++ /* No need to do anything; this bottom half is only used to ++ * kick the kernel out of ppoll/poll/WaitForMultipleObjects. ++ */ ++} ++ ++AioContext *qemu_get_aio_context(void) ++{ ++ return qemu_aio_context; ++} ++ ++void qemu_notify_event(void) ++{ ++ if (!qemu_aio_context) { ++ return; ++ } ++ qemu_bh_schedule(qemu_notify_bh); ++} ++ ++static GArray *gpollfds; ++ ++int qemu_init_main_loop(Error **errp) ++{ ++ int ret; ++ GSource *src; ++ ++ init_clocks(qemu_timer_notify_cb); ++ ++ ret = qemu_signal_init(errp); ++ if (ret) { ++ return ret; ++ } ++ ++ qemu_aio_context = aio_context_new(errp); ++ if (!qemu_aio_context) { ++ return -EMFILE; ++ } ++ qemu_set_current_aio_context(qemu_aio_context); ++ qemu_notify_bh = qemu_bh_new(notify_event_cb, NULL); ++ gpollfds = g_array_new(FALSE, FALSE, sizeof(GPollFD)); ++ src = aio_get_g_source(qemu_aio_context); ++ g_source_set_name(src, "aio-context"); ++ g_source_attach(src, NULL); ++ g_source_unref(src); ++ src = iohandler_get_g_source(); ++ g_source_set_name(src, "io-handler"); ++ g_source_attach(src, NULL); ++ g_source_unref(src); ++ return 0; ++} ++ ++static void main_loop_update_params(EventLoopBase *base, Error **errp) ++{ ++ ERRP_GUARD(); ++ ++ if (!qemu_aio_context) { ++ error_setg(errp, "qemu aio context not ready"); ++ return; ++ } ++ ++ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch); ++ ++ aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min, ++ base->thread_pool_max, errp); ++} ++ ++MainLoop *mloop; ++ ++static void main_loop_init(EventLoopBase *base, Error **errp) ++{ ++ MainLoop *m = MAIN_LOOP(base); ++ ++ if (mloop) { ++ error_setg(errp, "only one main-loop instance allowed"); ++ return; ++ } ++ ++ main_loop_update_params(base, errp); ++ ++ mloop = m; ++ return; ++} ++ ++static bool main_loop_can_be_deleted(EventLoopBase *base) ++{ ++ return false; ++} ++ ++static void main_loop_class_init(ObjectClass *oc, void *class_data) ++{ ++ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc); ++ ++ bc->init = main_loop_init; ++ bc->update_params = main_loop_update_params; ++ bc->can_be_deleted = main_loop_can_be_deleted; ++} ++ ++static const TypeInfo main_loop_info = { ++ .name = TYPE_MAIN_LOOP, ++ .parent = TYPE_EVENT_LOOP_BASE, ++ .class_init = main_loop_class_init, ++ .instance_size = sizeof(MainLoop), ++}; ++ ++static void main_loop_register_types(void) ++{ ++ type_register_static(&main_loop_info); ++} ++ ++type_init(main_loop_register_types) ++ ++static int max_priority; ++ ++#ifndef _WIN32 ++static int glib_pollfds_idx; ++static int glib_n_poll_fds; ++ ++static void glib_pollfds_fill(int64_t *cur_timeout) ++{ ++ GMainContext *context = g_main_context_default(); ++ int timeout = 0; ++ int64_t timeout_ns; ++ int n; ++ ++ g_main_context_prepare(context, &max_priority); ++ ++ glib_pollfds_idx = gpollfds->len; ++ n = glib_n_poll_fds; ++ do { ++ GPollFD *pfds; ++ glib_n_poll_fds = n; ++ g_array_set_size(gpollfds, glib_pollfds_idx + glib_n_poll_fds); ++ pfds = &g_array_index(gpollfds, GPollFD, glib_pollfds_idx); ++ n = g_main_context_query(context, max_priority, &timeout, pfds, ++ glib_n_poll_fds); ++ } while (n != glib_n_poll_fds); ++ ++ if (timeout < 0) { ++ timeout_ns = -1; ++ } else { ++ timeout_ns = (int64_t)timeout * (int64_t)SCALE_MS; ++ } ++ ++ *cur_timeout = qemu_soonest_timeout(timeout_ns, *cur_timeout); ++} ++ ++static void glib_pollfds_poll(void) ++{ ++ GMainContext *context = g_main_context_default(); ++ GPollFD *pfds = &g_array_index(gpollfds, GPollFD, glib_pollfds_idx); ++ ++ if (g_main_context_check(context, max_priority, pfds, glib_n_poll_fds)) { ++ g_main_context_dispatch(context); ++ } ++} ++ ++#define MAX_MAIN_LOOP_SPIN (1000) ++ ++static int os_host_main_loop_wait(int64_t timeout) ++{ ++ GMainContext *context = g_main_context_default(); ++ int ret; ++ ++ g_main_context_acquire(context); ++ ++ glib_pollfds_fill(&timeout); ++ ++ bql_unlock(); ++ replay_mutex_unlock(); ++ ++ ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); ++ ++ replay_mutex_lock(); ++ bql_lock(); ++ ++ glib_pollfds_poll(); ++ ++ g_main_context_release(context); ++ ++ return ret; ++} ++#else ++/***********************************************************/ ++/* Polling handling */ ++ ++typedef struct PollingEntry { ++ PollingFunc *func; ++ void *opaque; ++ struct PollingEntry *next; ++} PollingEntry; ++ ++static PollingEntry *first_polling_entry; ++ ++int qemu_add_polling_cb(PollingFunc *func, void *opaque) ++{ ++ PollingEntry **ppe, *pe; ++ pe = g_new0(PollingEntry, 1); ++ pe->func = func; ++ pe->opaque = opaque; ++ for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next); ++ *ppe = pe; ++ return 0; ++} ++ ++void qemu_del_polling_cb(PollingFunc *func, void *opaque) ++{ ++ PollingEntry **ppe, *pe; ++ for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) { ++ pe = *ppe; ++ if (pe->func == func && pe->opaque == opaque) { ++ *ppe = pe->next; ++ g_free(pe); ++ break; ++ } ++ } ++} ++ ++/***********************************************************/ ++/* Wait objects support */ ++typedef struct WaitObjects { ++ int num; ++ int revents[MAXIMUM_WAIT_OBJECTS]; ++ HANDLE events[MAXIMUM_WAIT_OBJECTS]; ++ WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS]; ++ void *opaque[MAXIMUM_WAIT_OBJECTS]; ++} WaitObjects; ++ ++static WaitObjects wait_objects = {0}; ++ ++int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) ++{ ++ int i; ++ WaitObjects *w = &wait_objects; ++ ++ if (w->num >= MAXIMUM_WAIT_OBJECTS) { ++ return -1; ++ } ++ ++ for (i = 0; i < w->num; i++) { ++ /* check if the same handle is added twice */ ++ if (w->events[i] == handle) { ++ return -1; ++ } ++ } ++ ++ w->events[w->num] = handle; ++ w->func[w->num] = func; ++ w->opaque[w->num] = opaque; ++ w->revents[w->num] = 0; ++ w->num++; ++ return 0; ++} ++ ++void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) ++{ ++ int i, found; ++ WaitObjects *w = &wait_objects; ++ ++ found = 0; ++ for (i = 0; i < w->num; i++) { ++ if (w->events[i] == handle) { ++ found = 1; ++ } ++ if (found && i < (MAXIMUM_WAIT_OBJECTS - 1)) { ++ w->events[i] = w->events[i + 1]; ++ w->func[i] = w->func[i + 1]; ++ w->opaque[i] = w->opaque[i + 1]; ++ w->revents[i] = w->revents[i + 1]; ++ } ++ } ++ if (found) { ++ w->num--; ++ } ++} ++ ++static int pollfds_fill(GArray *pollfds, fd_set *rfds, fd_set *wfds, ++ fd_set *xfds) ++{ ++ int nfds = -1; ++ int i; ++ ++ for (i = 0; i < pollfds->len; i++) { ++ GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); ++ int fd = pfd->fd; ++ int events = pfd->events; ++ if (events & G_IO_IN) { ++ FD_SET(fd, rfds); ++ nfds = MAX(nfds, fd); ++ } ++ if (events & G_IO_OUT) { ++ FD_SET(fd, wfds); ++ nfds = MAX(nfds, fd); ++ } ++ if (events & G_IO_PRI) { ++ FD_SET(fd, xfds); ++ nfds = MAX(nfds, fd); ++ } ++ } ++ return nfds; ++} ++ ++static void pollfds_poll(GArray *pollfds, int nfds, fd_set *rfds, ++ fd_set *wfds, fd_set *xfds) ++{ ++ int i; ++ ++ for (i = 0; i < pollfds->len; i++) { ++ GPollFD *pfd = &g_array_index(pollfds, GPollFD, i); ++ int fd = pfd->fd; ++ int revents = 0; ++ ++ if (FD_ISSET(fd, rfds)) { ++ revents |= G_IO_IN; ++ } ++ if (FD_ISSET(fd, wfds)) { ++ revents |= G_IO_OUT; ++ } ++ if (FD_ISSET(fd, xfds)) { ++ revents |= G_IO_PRI; ++ } ++ pfd->revents = revents & pfd->events; ++ } ++} ++ ++static int os_host_main_loop_wait(int64_t timeout) ++{ ++ GMainContext *context = g_main_context_default(); ++ GPollFD poll_fds[1024 * 2]; /* this is probably overkill */ ++ int select_ret = 0; ++ int g_poll_ret, ret, i, n_poll_fds; ++ PollingEntry *pe; ++ WaitObjects *w = &wait_objects; ++ gint poll_timeout; ++ int64_t poll_timeout_ns; ++ static struct timeval tv0; ++ fd_set rfds, wfds, xfds; ++ int nfds; ++ ++ g_main_context_acquire(context); ++ ++ /* XXX: need to suppress polling by better using win32 events */ ++ ret = 0; ++ for (pe = first_polling_entry; pe != NULL; pe = pe->next) { ++ ret |= pe->func(pe->opaque); ++ } ++ if (ret != 0) { ++ g_main_context_release(context); ++ return ret; ++ } ++ ++ FD_ZERO(&rfds); ++ FD_ZERO(&wfds); ++ FD_ZERO(&xfds); ++ nfds = pollfds_fill(gpollfds, &rfds, &wfds, &xfds); ++ if (nfds >= 0) { ++ select_ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv0); ++ if (select_ret != 0) { ++ timeout = 0; ++ } ++ if (select_ret > 0) { ++ pollfds_poll(gpollfds, nfds, &rfds, &wfds, &xfds); ++ } ++ } ++ ++ g_main_context_prepare(context, &max_priority); ++ n_poll_fds = g_main_context_query(context, max_priority, &poll_timeout, ++ poll_fds, ARRAY_SIZE(poll_fds)); ++ g_assert(n_poll_fds + w->num <= ARRAY_SIZE(poll_fds)); ++ ++ for (i = 0; i < w->num; i++) { ++ poll_fds[n_poll_fds + i].fd = (DWORD_PTR)w->events[i]; ++ poll_fds[n_poll_fds + i].events = G_IO_IN; ++ } ++ ++ if (poll_timeout < 0) { ++ poll_timeout_ns = -1; ++ } else { ++ poll_timeout_ns = (int64_t)poll_timeout * (int64_t)SCALE_MS; ++ } ++ ++ poll_timeout_ns = qemu_soonest_timeout(poll_timeout_ns, timeout); ++ ++ bql_unlock(); ++ ++ replay_mutex_unlock(); ++ ++ g_poll_ret = qemu_poll_ns(poll_fds, n_poll_fds + w->num, poll_timeout_ns); ++ ++ replay_mutex_lock(); ++ ++ bql_lock(); ++ if (g_poll_ret > 0) { ++ for (i = 0; i < w->num; i++) { ++ w->revents[i] = poll_fds[n_poll_fds + i].revents; ++ } ++ for (i = 0; i < w->num; i++) { ++ if (w->revents[i] && w->func[i]) { ++ w->func[i](w->opaque[i]); ++ } ++ } ++ } ++ ++ if (g_main_context_check(context, max_priority, poll_fds, n_poll_fds)) { ++ g_main_context_dispatch(context); ++ } ++ ++ g_main_context_release(context); ++ ++ return select_ret || g_poll_ret; ++} ++#endif ++ ++static NotifierList main_loop_poll_notifiers = ++ NOTIFIER_LIST_INITIALIZER(main_loop_poll_notifiers); ++ ++void main_loop_poll_add_notifier(Notifier *notify) ++{ ++ notifier_list_add(&main_loop_poll_notifiers, notify); ++} ++ ++void main_loop_poll_remove_notifier(Notifier *notify) ++{ ++ notifier_remove(notify); ++} ++ ++void main_loop_wait(int nonblocking) ++{ ++ MainLoopPoll mlpoll = { ++ .state = MAIN_LOOP_POLL_FILL, ++ .timeout = UINT32_MAX, ++ .pollfds = gpollfds, ++ }; ++ int ret; ++ int64_t timeout_ns; ++ ++ if (nonblocking) { ++ mlpoll.timeout = 0; ++ } ++ ++ /* poll any events */ ++ g_array_set_size(gpollfds, 0); /* reset for new iteration */ ++ /* XXX: separate device handlers from system ones */ ++ notifier_list_notify(&main_loop_poll_notifiers, &mlpoll); ++ ++ if (mlpoll.timeout == UINT32_MAX) { ++ timeout_ns = -1; ++ } else { ++ timeout_ns = (uint64_t)mlpoll.timeout * (int64_t)(SCALE_MS); ++ } ++ ++ timeout_ns = qemu_soonest_timeout(timeout_ns, ++ timerlistgroup_deadline_ns( ++ &main_loop_tlg)); ++ ++ ret = os_host_main_loop_wait(timeout_ns); ++ mlpoll.state = ret < 0 ? MAIN_LOOP_POLL_ERR : MAIN_LOOP_POLL_OK; ++ notifier_list_notify(&main_loop_poll_notifiers, &mlpoll); ++ ++ if (icount_enabled()) { ++ /* ++ * CPU thread can infinitely wait for event after ++ * missing the warp ++ */ ++ icount_start_warp_timer(); ++ } ++ qemu_clock_run_all_timers(); ++} ++ ++/* Functions to operate on the main QEMU AioContext. */ ++ ++QEMUBH *qemu_bh_new_full(QEMUBHFunc *cb, void *opaque, const char *name, ++ MemReentrancyGuard *reentrancy_guard) ++{ ++ return aio_bh_new_full(qemu_aio_context, cb, opaque, name, ++ reentrancy_guard); ++} ++ ++/* ++ * Functions to operate on the I/O handler AioContext. ++ * This context runs on top of main loop. We can't reuse qemu_aio_context ++ * because iohandlers mustn't be polled by aio_poll(qemu_aio_context). ++ */ ++static AioContext *iohandler_ctx; ++ ++static void iohandler_init(void) ++{ ++ if (!iohandler_ctx) { ++ iohandler_ctx = aio_context_new(&error_abort); ++ } ++} ++ ++AioContext *iohandler_get_aio_context(void) ++{ ++ iohandler_init(); ++ return iohandler_ctx; ++} ++ ++GSource *iohandler_get_g_source(void) ++{ ++ iohandler_init(); ++ return aio_get_g_source(iohandler_ctx); ++} ++ ++void qemu_set_fd_handler(int fd, ++ IOHandler *fd_read, ++ IOHandler *fd_write, ++ void *opaque) ++{ ++ iohandler_init(); ++ aio_set_fd_handler(iohandler_ctx, fd, fd_read, fd_write, NULL, NULL, ++ opaque); ++} ++ ++void event_notifier_set_handler(EventNotifier *e, ++ EventNotifierHandler *handler) ++{ ++ iohandler_init(); ++ aio_set_event_notifier(iohandler_ctx, e, handler, NULL, NULL); ++} +diff --git a/qcow2/lib/util/memalign.c b/qcow2/lib/util/memalign.c +new file mode 100644 +index 00000000..c199ae70 +--- /dev/null ++++ b/qcow2/lib/util/memalign.c +@@ -0,0 +1,92 @@ ++/* ++ * memalign.c: Allocate an aligned memory region ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2010-2016 Red Hat, Inc. ++ * Copyright (c) 2022 Linaro Ltd ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/host-utils.h" ++#include "qemu/memalign.h" ++#include "trace.h" ++ ++void *qemu_try_memalign(size_t alignment, size_t size) ++{ ++ void *ptr; ++ ++ if (alignment < sizeof(void*)) { ++ alignment = sizeof(void*); ++ } else { ++ g_assert(is_power_of_2(alignment)); ++ } ++ ++ /* ++ * Handling of 0 allocations varies among the different ++ * platform APIs (for instance _aligned_malloc() will ++ * fail) -- ensure that we always return a valid non-NULL ++ * pointer that can be freed by qemu_vfree(). ++ */ ++ if (size == 0) { ++ size++; ++ } ++#if defined(CONFIG_POSIX_MEMALIGN) ++ int ret; ++ ret = posix_memalign(&ptr, alignment, size); ++ if (ret != 0) { ++ errno = ret; ++ ptr = NULL; ++ } ++#elif defined(CONFIG_ALIGNED_MALLOC) ++ ptr = _aligned_malloc(size, alignment); ++#elif defined(CONFIG_VALLOC) ++ ptr = valloc(size); ++#elif defined(CONFIG_MEMALIGN) ++ ptr = memalign(alignment, size); ++#else ++ #error No function to allocate aligned memory available ++#endif ++ trace_qemu_memalign(alignment, size, ptr); ++ return ptr; ++} ++ ++void *qemu_memalign(size_t alignment, size_t size) ++{ ++ void *p = qemu_try_memalign(alignment, size); ++ if (p) { ++ return p; ++ } ++ fprintf(stderr, ++ "qemu_memalign: failed to allocate %zu bytes at alignment %zu: %s\n", ++ size, alignment, strerror(errno)); ++ abort(); ++} ++ ++void qemu_vfree(void *ptr) ++{ ++ trace_qemu_vfree(ptr); ++#if !defined(CONFIG_POSIX_MEMALIGN) && defined(CONFIG_ALIGNED_MALLOC) ++ /* Only Windows _aligned_malloc needs a special free function */ ++ _aligned_free(ptr); ++#else ++ free(ptr); ++#endif ++} +diff --git a/qcow2/lib/util/module.c b/qcow2/lib/util/module.c +new file mode 100644 +index 00000000..3eb0f06d +--- /dev/null ++++ b/qcow2/lib/util/module.c +@@ -0,0 +1,405 @@ ++/* ++ * QEMU Module Infrastructure ++ * ++ * Copyright IBM, Corp. 2009 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#ifdef CONFIG_MODULES ++#include ++#endif ++#include "qemu/queue.h" ++#include "qemu/module.h" ++#include "qemu/cutils.h" ++#include "qemu/config-file.h" ++#include "qapi/error.h" ++#ifdef CONFIG_MODULE_UPGRADES ++#include "qemu-version.h" ++#endif ++#include "trace.h" ++ ++typedef struct ModuleEntry ++{ ++ void (*init)(void); ++ QTAILQ_ENTRY(ModuleEntry) node; ++ module_init_type type; ++} ModuleEntry; ++ ++typedef QTAILQ_HEAD(, ModuleEntry) ModuleTypeList; ++ ++static ModuleTypeList init_type_list[MODULE_INIT_MAX]; ++static bool modules_init_done[MODULE_INIT_MAX]; ++ ++static ModuleTypeList dso_init_list; ++ ++static void init_lists(void) ++{ ++ static int inited; ++ int i; ++ ++ if (inited) { ++ return; ++ } ++ ++ for (i = 0; i < MODULE_INIT_MAX; i++) { ++ QTAILQ_INIT(&init_type_list[i]); ++ } ++ ++ QTAILQ_INIT(&dso_init_list); ++ ++ inited = 1; ++} ++ ++ ++static ModuleTypeList *find_type(module_init_type type) ++{ ++ init_lists(); ++ ++ return &init_type_list[type]; ++} ++ ++void register_module_init(void (*fn)(void), module_init_type type) ++{ ++ ModuleEntry *e; ++ ModuleTypeList *l; ++ ++ e = g_malloc0(sizeof(*e)); ++ e->init = fn; ++ e->type = type; ++ ++ l = find_type(type); ++ ++ QTAILQ_INSERT_TAIL(l, e, node); ++} ++ ++void register_dso_module_init(void (*fn)(void), module_init_type type) ++{ ++ ModuleEntry *e; ++ ++ init_lists(); ++ ++ e = g_malloc0(sizeof(*e)); ++ e->init = fn; ++ e->type = type; ++ ++ QTAILQ_INSERT_TAIL(&dso_init_list, e, node); ++} ++ ++void module_call_init(module_init_type type) ++{ ++ ModuleTypeList *l; ++ ModuleEntry *e; ++ ++ if (modules_init_done[type]) { ++ return; ++ } ++ ++ l = find_type(type); ++ ++ QTAILQ_FOREACH(e, l, node) { ++ e->init(); ++ } ++ ++ modules_init_done[type] = true; ++} ++ ++#ifdef CONFIG_MODULES ++ ++static const QemuModinfo module_info_stub[] = { { ++ /* end of list */ ++} }; ++static const QemuModinfo *module_info = module_info_stub; ++static const char *module_arch; ++ ++void module_init_info(const QemuModinfo *info) ++{ ++ module_info = info; ++} ++ ++void module_allow_arch(const char *arch) ++{ ++ module_arch = arch; ++} ++ ++static bool module_check_arch(const QemuModinfo *modinfo) ++{ ++ if (modinfo->arch) { ++ if (!module_arch) { ++ /* no arch set -> ignore all */ ++ return false; ++ } ++ if (strcmp(module_arch, modinfo->arch) != 0) { ++ /* mismatch */ ++ return false; ++ } ++ } ++ return true; ++} ++ ++/* ++ * module_load_dso: attempt to load an existing dso file ++ * ++ * fname: full pathname of the file to load ++ * export_symbols: if true, add the symbols to the global name space ++ * errp: error to set. ++ * ++ * Return value: true on success, false on error, and errp will be set. ++ */ ++static bool module_load_dso(const char *fname, bool export_symbols, ++ Error **errp) ++{ ++ GModule *g_module; ++ void (*sym)(void); ++ ModuleEntry *e, *next; ++ int flags; ++ ++ assert(QTAILQ_EMPTY(&dso_init_list)); ++ ++ flags = 0; ++ if (!export_symbols) { ++ flags |= G_MODULE_BIND_LOCAL; ++ } ++ g_module = g_module_open(fname, flags); ++ if (!g_module) { ++ error_setg(errp, "failed to open module: %s", g_module_error()); ++ return false; ++ } ++ if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) { ++ error_setg(errp, "failed to initialize module: %s", fname); ++ /* ++ * Print some info if this is a QEMU module (but from different build), ++ * this will make debugging user problems easier. ++ */ ++ if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) { ++ error_append_hint(errp, ++ "Only modules from the same build can be loaded.\n"); ++ } ++ g_module_close(g_module); ++ return false; ++ } ++ ++ QTAILQ_FOREACH(e, &dso_init_list, node) { ++ e->init(); ++ register_module_init(e->init, e->type); ++ } ++ trace_module_load_module(fname); ++ QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { ++ QTAILQ_REMOVE(&dso_init_list, e, node); ++ g_free(e); ++ } ++ return true; ++} ++ ++int module_load(const char *prefix, const char *name, Error **errp) ++{ ++ int rv = -1; ++#ifdef CONFIG_MODULE_UPGRADES ++ char *version_dir; ++#endif ++ const char *search_dir; ++ char *dirs[5]; ++ char *module_name; ++ int i = 0, n_dirs = 0; ++ bool export_symbols = false; ++ static GHashTable *loaded_modules; ++ const QemuModinfo *modinfo; ++ const char **sl; ++ ++ if (!g_module_supported()) { ++ error_setg(errp, "%s", "this platform does not support GLib modules"); ++ return -1; ++ } ++ ++ if (!loaded_modules) { ++ loaded_modules = g_hash_table_new(g_str_hash, g_str_equal); ++ } ++ ++ /* allocate all resources managed by the out: label here */ ++ module_name = g_strdup_printf("%s%s", prefix, name); ++ ++ if (g_hash_table_contains(loaded_modules, module_name)) { ++ g_free(module_name); ++ return 2; /* module already loaded */ ++ } ++ g_hash_table_add(loaded_modules, module_name); ++ ++ search_dir = getenv("QEMU_MODULE_DIR"); ++ if (search_dir != NULL) { ++ dirs[n_dirs++] = g_strdup_printf("%s", search_dir); ++ } ++ dirs[n_dirs++] = get_relocated_path(CONFIG_QEMU_MODDIR); ++ ++#ifdef CONFIG_MODULE_UPGRADES ++ version_dir = g_strcanon(g_strdup(QEMU_PKGVERSION), ++ G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "+-.~", ++ '_'); ++ dirs[n_dirs++] = g_strdup_printf("/var/run/qemu/%s", version_dir); ++#endif ++ assert(n_dirs <= ARRAY_SIZE(dirs)); ++ ++ /* end of resources managed by the out: label */ ++ ++ for (modinfo = module_info; modinfo->name != NULL; modinfo++) { ++ if (modinfo->arch) { ++ if (strcmp(modinfo->name, module_name) == 0) { ++ if (!module_check_arch(modinfo)) { ++ error_setg(errp, "module arch does not match: " ++ "expected '%s', got '%s'", module_arch, modinfo->arch); ++ goto out; ++ } ++ } ++ } ++ if (modinfo->deps) { ++ if (strcmp(modinfo->name, module_name) == 0) { ++ /* we depend on other module(s) */ ++ for (sl = modinfo->deps; *sl != NULL; sl++) { ++ int subrv = module_load("", *sl, errp); ++ if (subrv <= 0) { ++ rv = subrv; ++ goto out; ++ } ++ } ++ } else { ++ for (sl = modinfo->deps; *sl != NULL; sl++) { ++ if (strcmp(module_name, *sl) == 0) { ++ /* another module depends on us */ ++ export_symbols = true; ++ } ++ } ++ } ++ } ++ } ++ ++ for (i = 0; i < n_dirs; i++) { ++ char *fname = g_strdup_printf("%s/%s%s", ++ dirs[i], module_name, CONFIG_HOST_DSOSUF); ++ int ret = access(fname, F_OK); ++ if (ret != 0 && (errno == ENOENT || errno == ENOTDIR)) { ++ /* ++ * if we don't find the module in this dir, try the next one. ++ * If we don't find it in any dir, that can be fine too: user ++ * did not install the module. We will return 0 in this case ++ * with no error set. ++ */ ++ g_free(fname); ++ continue; ++ } else if (ret != 0) { ++ /* most common is EACCES here */ ++ error_setg_errno(errp, errno, "error trying to access %s", fname); ++ } else if (module_load_dso(fname, export_symbols, errp)) { ++ rv = 1; /* module successfully loaded */ ++ } ++ g_free(fname); ++ goto out; ++ } ++ rv = 0; /* module not found */ ++ ++out: ++ if (rv <= 0) { ++ g_hash_table_remove(loaded_modules, module_name); ++ g_free(module_name); ++ } ++ for (i = 0; i < n_dirs; i++) { ++ g_free(dirs[i]); ++ } ++ return rv; ++} ++ ++static bool module_loaded_qom_all; ++ ++int module_load_qom(const char *type, Error **errp) ++{ ++ const QemuModinfo *modinfo; ++ const char **sl; ++ int rv = 0; ++ ++ if (!type) { ++ error_setg(errp, "%s", "type is NULL"); ++ return -1; ++ } ++ ++ trace_module_lookup_object_type(type); ++ for (modinfo = module_info; modinfo->name != NULL; modinfo++) { ++ if (!modinfo->objs) { ++ continue; ++ } ++ if (!module_check_arch(modinfo)) { ++ continue; ++ } ++ for (sl = modinfo->objs; *sl != NULL; sl++) { ++ if (strcmp(type, *sl) == 0) { ++ if (rv > 0) { ++ error_setg(errp, "multiple modules providing '%s'", type); ++ return -1; ++ } ++ rv = module_load("", modinfo->name, errp); ++ if (rv < 0) { ++ return rv; ++ } ++ } ++ } ++ } ++ return rv; ++} ++ ++void module_load_qom_all(void) ++{ ++ const QemuModinfo *modinfo; ++ ++ if (module_loaded_qom_all) { ++ return; ++ } ++ ++ for (modinfo = module_info; modinfo->name != NULL; modinfo++) { ++ Error *local_err = NULL; ++ if (!modinfo->objs) { ++ continue; ++ } ++ if (!module_check_arch(modinfo)) { ++ continue; ++ } ++ if (module_load("", modinfo->name, &local_err) < 0) { ++ error_report_err(local_err); ++ } ++ } ++ module_loaded_qom_all = true; ++} ++ ++void qemu_load_module_for_opts(const char *group) ++{ ++ const QemuModinfo *modinfo; ++ const char **sl; ++ ++ for (modinfo = module_info; modinfo->name != NULL; modinfo++) { ++ if (!modinfo->opts) { ++ continue; ++ } ++ for (sl = modinfo->opts; *sl != NULL; sl++) { ++ if (strcmp(group, *sl) == 0) { ++ Error *local_err = NULL; ++ if (module_load("", modinfo->name, &local_err) < 0) { ++ error_report_err(local_err); ++ } ++ } ++ } ++ } ++} ++ ++#else ++ ++void module_allow_arch(const char *arch) {} ++void qemu_load_module_for_opts(const char *group) {} ++int module_load(const char *prefix, const char *name, Error **errp) { return 2; } ++int module_load_qom(const char *type, Error **errp) { return 2; } ++void module_load_qom_all(void) {} ++ ++#endif +diff --git a/qcow2/lib/util/notify.c b/qcow2/lib/util/notify.c +new file mode 100644 +index 00000000..c6e158ff +--- /dev/null ++++ b/qcow2/lib/util/notify.c +@@ -0,0 +1,77 @@ ++/* ++ * Notifier lists ++ * ++ * Copyright IBM, Corp. 2010 ++ * ++ * Authors: ++ * Anthony Liguori ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/notify.h" ++ ++void notifier_list_init(NotifierList *list) ++{ ++ QLIST_INIT(&list->notifiers); ++} ++ ++void notifier_list_add(NotifierList *list, Notifier *notifier) ++{ ++ QLIST_INSERT_HEAD(&list->notifiers, notifier, node); ++} ++ ++void notifier_remove(Notifier *notifier) ++{ ++ QLIST_REMOVE(notifier, node); ++} ++ ++void notifier_list_notify(NotifierList *list, void *data) ++{ ++ Notifier *notifier, *next; ++ ++ QLIST_FOREACH_SAFE(notifier, &list->notifiers, node, next) { ++ notifier->notify(notifier, data); ++ } ++} ++ ++bool notifier_list_empty(NotifierList *list) ++{ ++ return QLIST_EMPTY(&list->notifiers); ++} ++ ++void notifier_with_return_list_init(NotifierWithReturnList *list) ++{ ++ QLIST_INIT(&list->notifiers); ++} ++ ++void notifier_with_return_list_add(NotifierWithReturnList *list, ++ NotifierWithReturn *notifier) ++{ ++ QLIST_INSERT_HEAD(&list->notifiers, notifier, node); ++} ++ ++void notifier_with_return_remove(NotifierWithReturn *notifier) ++{ ++ QLIST_REMOVE(notifier, node); ++} ++ ++int notifier_with_return_list_notify(NotifierWithReturnList *list, void *data, ++ Error **errp) ++{ ++ NotifierWithReturn *notifier, *next; ++ int ret = 0; ++ ++ QLIST_FOREACH_SAFE(notifier, &list->notifiers, node, next) { ++ ret = notifier->notify(notifier, data, errp); ++ if (ret != 0) { ++ break; ++ } ++ } ++ return ret; ++} +diff --git a/qcow2/lib/util/osdep.c b/qcow2/lib/util/osdep.c +new file mode 100644 +index 00000000..77036983 +--- /dev/null ++++ b/qcow2/lib/util/osdep.c +@@ -0,0 +1,615 @@ ++/* ++ * QEMU low level functions ++ * ++ * Copyright (c) 2003 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/cutils.h" ++#include "qemu/sockets.h" ++#include "qemu/error-report.h" ++#include "qemu/madvise.h" ++#include "qemu/mprotect.h" ++#include "qemu/hw-version.h" ++#include "monitor/monitor.h" ++ ++static const char *hw_version = QEMU_HW_VERSION; ++ ++int socket_set_cork(int fd, int v) ++{ ++#if defined(SOL_TCP) && defined(TCP_CORK) ++ return setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v)); ++#else ++ return 0; ++#endif ++} ++ ++int socket_set_nodelay(int fd) ++{ ++ int v = 1; ++ return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); ++} ++ ++int qemu_madvise(void *addr, size_t len, int advice) ++{ ++ if (advice == QEMU_MADV_INVALID) { ++ errno = EINVAL; ++ return -1; ++ } ++#if defined(CONFIG_MADVISE) ++ return madvise(addr, len, advice); ++#elif defined(CONFIG_POSIX_MADVISE) ++ int rc = posix_madvise(addr, len, advice); ++ if (rc) { ++ errno = rc; ++ return -1; ++ } ++ return 0; ++#else ++ errno = ENOSYS; ++ return -1; ++#endif ++} ++ ++static int qemu_mprotect__osdep(void *addr, size_t size, int prot) ++{ ++ g_assert(!((uintptr_t)addr & ~qemu_real_host_page_mask())); ++ g_assert(!(size & ~qemu_real_host_page_mask())); ++ ++#ifdef _WIN32 ++ DWORD old_protect; ++ ++ if (!VirtualProtect(addr, size, prot, &old_protect)) { ++ g_autofree gchar *emsg = g_win32_error_message(GetLastError()); ++ error_report("%s: VirtualProtect failed: %s", __func__, emsg); ++ return -1; ++ } ++ return 0; ++#else ++ if (mprotect(addr, size, prot)) { ++ error_report("%s: mprotect failed: %s", __func__, strerror(errno)); ++ return -1; ++ } ++ return 0; ++#endif ++} ++ ++int qemu_mprotect_rw(void *addr, size_t size) ++{ ++#ifdef _WIN32 ++ return qemu_mprotect__osdep(addr, size, PAGE_READWRITE); ++#else ++ return qemu_mprotect__osdep(addr, size, PROT_READ | PROT_WRITE); ++#endif ++} ++ ++int qemu_mprotect_rwx(void *addr, size_t size) ++{ ++#ifdef _WIN32 ++ return qemu_mprotect__osdep(addr, size, PAGE_EXECUTE_READWRITE); ++#else ++ return qemu_mprotect__osdep(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC); ++#endif ++} ++ ++int qemu_mprotect_none(void *addr, size_t size) ++{ ++#ifdef _WIN32 ++ return qemu_mprotect__osdep(addr, size, PAGE_NOACCESS); ++#else ++ return qemu_mprotect__osdep(addr, size, PROT_NONE); ++#endif ++} ++ ++#ifndef _WIN32 ++ ++static int fcntl_op_setlk = -1; ++static int fcntl_op_getlk = -1; ++ ++/* ++ * Dups an fd and sets the flags ++ */ ++int qemu_dup_flags(int fd, int flags) ++{ ++ int ret; ++ int serrno; ++ int dup_flags; ++ ++ ret = qemu_dup(fd); ++ if (ret == -1) { ++ goto fail; ++ } ++ ++ dup_flags = fcntl(ret, F_GETFL); ++ if (dup_flags == -1) { ++ goto fail; ++ } ++ ++ if ((flags & O_SYNC) != (dup_flags & O_SYNC)) { ++ errno = EINVAL; ++ goto fail; ++ } ++ ++ /* Set/unset flags that we can with fcntl */ ++ if (fcntl(ret, F_SETFL, flags) == -1) { ++ goto fail; ++ } ++ ++ /* Truncate the file in the cases that open() would truncate it */ ++ if (flags & O_TRUNC || ++ ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))) { ++ if (ftruncate(ret, 0) == -1) { ++ goto fail; ++ } ++ } ++ ++ return ret; ++ ++fail: ++ serrno = errno; ++ if (ret != -1) { ++ close(ret); ++ } ++ errno = serrno; ++ return -1; ++} ++ ++int qemu_dup(int fd) ++{ ++ int ret; ++#ifdef F_DUPFD_CLOEXEC ++ ret = fcntl(fd, F_DUPFD_CLOEXEC, 0); ++#else ++ ret = dup(fd); ++ if (ret != -1) { ++ qemu_set_cloexec(ret); ++ } ++#endif ++ return ret; ++} ++ ++static int qemu_parse_fdset(const char *param) ++{ ++ return qemu_parse_fd(param); ++} ++ ++static void qemu_probe_lock_ops(void) ++{ ++ if (fcntl_op_setlk == -1) { ++#ifdef F_OFD_SETLK ++ int fd; ++ int ret; ++ struct flock fl = { ++ .l_whence = SEEK_SET, ++ .l_start = 0, ++ .l_len = 0, ++ .l_type = F_WRLCK, ++ }; ++ ++ fd = open("/dev/null", O_RDWR); ++ if (fd < 0) { ++ fprintf(stderr, ++ "Failed to open /dev/null for OFD lock probing: %s\n", ++ strerror(errno)); ++ fcntl_op_setlk = F_SETLK; ++ fcntl_op_getlk = F_GETLK; ++ return; ++ } ++ ret = fcntl(fd, F_OFD_GETLK, &fl); ++ close(fd); ++ if (!ret) { ++ fcntl_op_setlk = F_OFD_SETLK; ++ fcntl_op_getlk = F_OFD_GETLK; ++ } else { ++ fcntl_op_setlk = F_SETLK; ++ fcntl_op_getlk = F_GETLK; ++ } ++#else ++ fcntl_op_setlk = F_SETLK; ++ fcntl_op_getlk = F_GETLK; ++#endif ++ } ++} ++ ++bool qemu_has_ofd_lock(void) ++{ ++ qemu_probe_lock_ops(); ++#ifdef F_OFD_SETLK ++ return fcntl_op_setlk == F_OFD_SETLK; ++#else ++ return false; ++#endif ++} ++ ++static int qemu_lock_fcntl(int fd, int64_t start, int64_t len, int fl_type) ++{ ++ int ret; ++ struct flock fl = { ++ .l_whence = SEEK_SET, ++ .l_start = start, ++ .l_len = len, ++ .l_type = fl_type, ++ }; ++ qemu_probe_lock_ops(); ++ ret = RETRY_ON_EINTR(fcntl(fd, fcntl_op_setlk, &fl)); ++ return ret == -1 ? -errno : 0; ++} ++ ++int qemu_lock_fd(int fd, int64_t start, int64_t len, bool exclusive) ++{ ++ return qemu_lock_fcntl(fd, start, len, exclusive ? F_WRLCK : F_RDLCK); ++} ++ ++int qemu_unlock_fd(int fd, int64_t start, int64_t len) ++{ ++ return qemu_lock_fcntl(fd, start, len, F_UNLCK); ++} ++ ++int qemu_lock_fd_test(int fd, int64_t start, int64_t len, bool exclusive) ++{ ++ int ret; ++ struct flock fl = { ++ .l_whence = SEEK_SET, ++ .l_start = start, ++ .l_len = len, ++ .l_type = exclusive ? F_WRLCK : F_RDLCK, ++ }; ++ qemu_probe_lock_ops(); ++ ret = fcntl(fd, fcntl_op_getlk, &fl); ++ if (ret == -1) { ++ return -errno; ++ } else { ++ return fl.l_type == F_UNLCK ? 0 : -EAGAIN; ++ } ++} ++#endif ++ ++bool qemu_has_direct_io(void) ++{ ++#ifdef O_DIRECT ++ return true; ++#else ++ return false; ++#endif ++} ++ ++static int qemu_open_cloexec(const char *name, int flags, mode_t mode) ++{ ++ int ret; ++#ifdef O_CLOEXEC ++ ret = open(name, flags | O_CLOEXEC, mode); ++#else ++ ret = open(name, flags, mode); ++ if (ret >= 0) { ++ qemu_set_cloexec(ret); ++ } ++#endif ++ return ret; ++} ++ ++/* ++ * Opens a file with FD_CLOEXEC set ++ */ ++static int ++qemu_open_internal(const char *name, int flags, mode_t mode, Error **errp) ++{ ++ int ret; ++ ++#ifndef _WIN32 ++ const char *fdset_id_str; ++ ++ /* Attempt dup of fd from fd set */ ++ if (strstart(name, "/dev/fdset/", &fdset_id_str)) { ++ int64_t fdset_id; ++ ++ fdset_id = qemu_parse_fdset(fdset_id_str); ++ if (fdset_id == -1) { ++ error_setg(errp, "Could not parse fdset %s", name); ++ errno = EINVAL; ++ return -1; ++ } ++ ++ return monitor_fdset_dup_fd_add(fdset_id, flags, errp); ++ } ++#endif ++ ++ ret = qemu_open_cloexec(name, flags, mode); ++ ++ if (ret == -1) { ++ const char *action = flags & O_CREAT ? "create" : "open"; ++#ifdef O_DIRECT ++ /* Give more helpful error message for O_DIRECT */ ++ if (errno == EINVAL && (flags & O_DIRECT)) { ++ ret = open(name, flags & ~O_DIRECT, mode); ++ if (ret != -1) { ++ close(ret); ++ error_setg(errp, "Could not %s '%s': " ++ "filesystem does not support O_DIRECT", ++ action, name); ++ errno = EINVAL; /* restore first open()'s errno */ ++ return -1; ++ } ++ } ++#endif /* O_DIRECT */ ++ error_setg_errno(errp, errno, "Could not %s '%s'", ++ action, name); ++ } ++ ++ return ret; ++} ++ ++ ++int qemu_open(const char *name, int flags, Error **errp) ++{ ++ assert(!(flags & O_CREAT)); ++ ++ return qemu_open_internal(name, flags, 0, errp); ++} ++ ++ ++int qemu_create(const char *name, int flags, mode_t mode, Error **errp) ++{ ++ assert(!(flags & O_CREAT)); ++ ++ return qemu_open_internal(name, flags | O_CREAT, mode, errp); ++} ++ ++ ++int qemu_open_old(const char *name, int flags, ...) ++{ ++ va_list ap; ++ mode_t mode = 0; ++ int ret; ++ ++ va_start(ap, flags); ++ if (flags & O_CREAT) { ++ mode = va_arg(ap, int); ++ } ++ va_end(ap); ++ ++ ret = qemu_open_internal(name, flags, mode, NULL); ++ ++#ifdef O_DIRECT ++ if (ret == -1 && errno == EINVAL && (flags & O_DIRECT)) { ++ error_report("file system may not support O_DIRECT"); ++ errno = EINVAL; /* in case it was clobbered */ ++ } ++#endif /* O_DIRECT */ ++ ++ return ret; ++} ++ ++int qemu_close(int fd) ++{ ++ /* Close fd that was dup'd from an fdset */ ++ monitor_fdset_dup_fd_remove(fd); ++ return close(fd); ++} ++ ++/* ++ * Delete a file from the filesystem, unless the filename is /dev/fdset/... ++ * ++ * Returns: On success, zero is returned. On error, -1 is returned, ++ * and errno is set appropriately. ++ */ ++int qemu_unlink(const char *name) ++{ ++ if (g_str_has_prefix(name, "/dev/fdset/")) { ++ return 0; ++ } ++ ++ return unlink(name); ++} ++ ++/* ++ * A variant of write(2) which handles partial write. ++ * ++ * Return the number of bytes transferred. ++ * Set errno if fewer than `count' bytes are written. ++ * ++ * This function don't work with non-blocking fd's. ++ * Any of the possibilities with non-blocking fd's is bad: ++ * - return a short write (then name is wrong) ++ * - busy wait adding (errno == EAGAIN) to the loop ++ */ ++ssize_t qemu_write_full(int fd, const void *buf, size_t count) ++{ ++ ssize_t ret = 0; ++ ssize_t total = 0; ++ ++ while (count) { ++ ret = write(fd, buf, count); ++ if (ret < 0) { ++ if (errno == EINTR) ++ continue; ++ break; ++ } ++ ++ count -= ret; ++ buf += ret; ++ total += ret; ++ } ++ ++ return total; ++} ++ ++/* ++ * Opens a socket with FD_CLOEXEC set ++ */ ++int qemu_socket(int domain, int type, int protocol) ++{ ++ int ret; ++ ++#ifdef SOCK_CLOEXEC ++ ret = socket(domain, type | SOCK_CLOEXEC, protocol); ++ if (ret != -1 || errno != EINVAL) { ++ return ret; ++ } ++#endif ++ ret = socket(domain, type, protocol); ++ if (ret >= 0) { ++ qemu_set_cloexec(ret); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Accept a connection and set FD_CLOEXEC ++ */ ++int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) ++{ ++ int ret; ++ ++#ifdef CONFIG_ACCEPT4 ++ ret = accept4(s, addr, addrlen, SOCK_CLOEXEC); ++ if (ret != -1 || errno != ENOSYS) { ++ return ret; ++ } ++#endif ++ ret = accept(s, addr, addrlen); ++ if (ret >= 0) { ++ qemu_set_cloexec(ret); ++ } ++ ++ return ret; ++} ++ ++ssize_t qemu_send_full(int s, const void *buf, size_t count) ++{ ++ ssize_t ret = 0; ++ ssize_t total = 0; ++ ++ while (count) { ++ ret = send(s, buf, count, 0); ++ if (ret < 0) { ++ if (errno == EINTR) { ++ continue; ++ } ++ break; ++ } ++ ++ count -= ret; ++ buf += ret; ++ total += ret; ++ } ++ ++ return total; ++} ++ ++void qemu_set_hw_version(const char *version) ++{ ++ hw_version = version; ++} ++ ++const char *qemu_hw_version(void) ++{ ++ return hw_version; ++} ++ ++#ifdef _WIN32 ++static void socket_cleanup(void) ++{ ++ WSACleanup(); ++} ++#endif ++ ++int socket_init(void) ++{ ++#ifdef _WIN32 ++ WSADATA Data; ++ int ret, err; ++ ++ ret = WSAStartup(MAKEWORD(2, 2), &Data); ++ if (ret != 0) { ++ err = WSAGetLastError(); ++ fprintf(stderr, "WSAStartup: %d\n", err); ++ return -1; ++ } ++ atexit(socket_cleanup); ++#endif ++ return 0; ++} ++ ++ ++#ifndef CONFIG_IOVEC ++static ssize_t ++readv_writev(int fd, const struct iovec *iov, int iov_cnt, bool do_write) ++{ ++ unsigned i = 0; ++ ssize_t ret = 0; ++ ssize_t off = 0; ++ while (i < iov_cnt) { ++ ssize_t r = do_write ++ ? write(fd, iov[i].iov_base + off, iov[i].iov_len - off) ++ : read(fd, iov[i].iov_base + off, iov[i].iov_len - off); ++ if (r > 0) { ++ ret += r; ++ off += r; ++ if (off < iov[i].iov_len) { ++ continue; ++ } ++ } else if (!r) { ++ break; ++ } else if (errno == EINTR) { ++ continue; ++ } else { ++ /* else it is some "other" error, ++ * only return if there was no data processed. */ ++ if (ret == 0) { ++ ret = -1; ++ } ++ break; ++ } ++ off = 0; ++ i++; ++ } ++ return ret; ++} ++ ++ssize_t ++readv(int fd, const struct iovec *iov, int iov_cnt) ++{ ++ return readv_writev(fd, iov, iov_cnt, false); ++} ++ ++ssize_t ++writev(int fd, const struct iovec *iov, int iov_cnt) ++{ ++ return readv_writev(fd, iov, iov_cnt, true); ++} ++#endif ++ ++/* ++ * Make sure data goes on disk, but if possible do not bother to ++ * write out the inode just for timestamp updates. ++ * ++ * Unfortunately even in 2009 many operating systems do not support ++ * fdatasync and have to fall back to fsync. ++ */ ++int qemu_fdatasync(int fd) ++{ ++#ifdef CONFIG_FDATASYNC ++ return fdatasync(fd); ++#else ++ return fsync(fd); ++#endif ++} +diff --git a/qcow2/lib/util/oslib-posix.c b/qcow2/lib/util/oslib-posix.c +new file mode 100644 +index 00000000..11b35e48 +--- /dev/null ++++ b/qcow2/lib/util/oslib-posix.c +@@ -0,0 +1,933 @@ ++/* ++ * os-posix-lib.c ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2010 Red Hat, Inc. ++ * ++ * QEMU library functions on POSIX which are shared between QEMU and ++ * the QEMU tools. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include ++ ++#include ++ ++#include "sysemu/sysemu.h" ++#include "trace.h" ++#include "qapi/error.h" ++#include "qemu/error-report.h" ++#include "qemu/madvise.h" ++#include "qemu/sockets.h" ++#include "qemu/thread.h" ++#include ++#include "qemu/cutils.h" ++#include "qemu/units.h" ++#include "qemu/thread-context.h" ++#include "qemu/main-loop.h" ++ ++#ifdef CONFIG_LINUX ++#include ++#endif ++ ++#ifdef __FreeBSD__ ++#include ++#include ++#include ++#endif ++ ++#ifdef __NetBSD__ ++#include ++#endif ++ ++#include "qemu/mmap-alloc.h" ++ ++#define MAX_MEM_PREALLOC_THREAD_COUNT 16 ++ ++struct MemsetThread; ++ ++static QLIST_HEAD(, MemsetContext) memset_contexts = ++ QLIST_HEAD_INITIALIZER(memset_contexts); ++ ++typedef struct MemsetContext { ++ bool all_threads_created; ++ bool any_thread_failed; ++ struct MemsetThread *threads; ++ int num_threads; ++ QLIST_ENTRY(MemsetContext) next; ++} MemsetContext; ++ ++struct MemsetThread { ++ char *addr; ++ size_t numpages; ++ size_t hpagesize; ++ QemuThread pgthread; ++ sigjmp_buf env; ++ MemsetContext *context; ++}; ++typedef struct MemsetThread MemsetThread; ++ ++/* used by sigbus_handler() */ ++static MemsetContext *sigbus_memset_context; ++struct sigaction sigbus_oldact; ++static QemuMutex sigbus_mutex; ++ ++static QemuMutex page_mutex; ++static QemuCond page_cond; ++ ++int qemu_get_thread_id(void) ++{ ++#if defined(__linux__) ++ return syscall(SYS_gettid); ++#elif defined(__FreeBSD__) ++ /* thread id is up to INT_MAX */ ++ long tid; ++ thr_self(&tid); ++ return (int)tid; ++#elif defined(__NetBSD__) ++ return _lwp_self(); ++#elif defined(__OpenBSD__) ++ return getthrid(); ++#else ++ return getpid(); ++#endif ++} ++ ++int qemu_daemon(int nochdir, int noclose) ++{ ++ return daemon(nochdir, noclose); ++} ++ ++bool qemu_write_pidfile(const char *path, Error **errp) ++{ ++ int fd; ++ char pidstr[32]; ++ ++ while (1) { ++ struct stat a, b; ++ struct flock lock = { ++ .l_type = F_WRLCK, ++ .l_whence = SEEK_SET, ++ .l_len = 0, ++ }; ++ ++ fd = qemu_create(path, O_WRONLY, S_IRUSR | S_IWUSR, errp); ++ if (fd == -1) { ++ return false; ++ } ++ ++ if (fstat(fd, &b) < 0) { ++ error_setg_errno(errp, errno, "Cannot stat file"); ++ goto fail_close; ++ } ++ ++ if (fcntl(fd, F_SETLK, &lock)) { ++ error_setg_errno(errp, errno, "Cannot lock pid file"); ++ goto fail_close; ++ } ++ ++ /* ++ * Now make sure the path we locked is the same one that now ++ * exists on the filesystem. ++ */ ++ if (stat(path, &a) < 0) { ++ /* ++ * PID file disappeared, someone else must be racing with ++ * us, so try again. ++ */ ++ close(fd); ++ continue; ++ } ++ ++ if (a.st_ino == b.st_ino) { ++ break; ++ } ++ ++ /* ++ * PID file was recreated, someone else must be racing with ++ * us, so try again. ++ */ ++ close(fd); ++ } ++ ++ if (ftruncate(fd, 0) < 0) { ++ error_setg_errno(errp, errno, "Failed to truncate pid file"); ++ goto fail_unlink; ++ } ++ ++ snprintf(pidstr, sizeof(pidstr), FMT_pid "\n", getpid()); ++ if (qemu_write_full(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) { ++ error_setg(errp, "Failed to write pid file"); ++ goto fail_unlink; ++ } ++ ++ return true; ++ ++fail_unlink: ++ unlink(path); ++fail_close: ++ close(fd); ++ return false; ++} ++ ++/* alloc shared memory pages */ ++void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, ++ bool noreserve) ++{ ++ const uint32_t qemu_map_flags = (shared ? QEMU_MAP_SHARED : 0) | ++ (noreserve ? QEMU_MAP_NORESERVE : 0); ++ size_t align = QEMU_VMALLOC_ALIGN; ++ void *ptr = qemu_ram_mmap(-1, size, align, qemu_map_flags, 0); ++ ++ if (ptr == MAP_FAILED) { ++ return NULL; ++ } ++ ++ if (alignment) { ++ *alignment = align; ++ } ++ ++ trace_qemu_anon_ram_alloc(size, ptr); ++ return ptr; ++} ++ ++void qemu_anon_ram_free(void *ptr, size_t size) ++{ ++ trace_qemu_anon_ram_free(ptr, size); ++ qemu_ram_munmap(-1, ptr, size); ++} ++ ++void qemu_socket_set_block(int fd) ++{ ++ g_unix_set_fd_nonblocking(fd, false, NULL); ++} ++ ++int qemu_socket_try_set_nonblock(int fd) ++{ ++ return g_unix_set_fd_nonblocking(fd, true, NULL) ? 0 : -errno; ++} ++ ++void qemu_socket_set_nonblock(int fd) ++{ ++ int f; ++ f = qemu_socket_try_set_nonblock(fd); ++ assert(f == 0); ++} ++ ++int socket_set_fast_reuse(int fd) ++{ ++ int val = 1, ret; ++ ++ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, ++ (const char *)&val, sizeof(val)); ++ ++ assert(ret == 0); ++ ++ return ret; ++} ++ ++void qemu_set_cloexec(int fd) ++{ ++ int f; ++ f = fcntl(fd, F_GETFD); ++ assert(f != -1); ++ f = fcntl(fd, F_SETFD, f | FD_CLOEXEC); ++ assert(f != -1); ++} ++ ++int qemu_socketpair(int domain, int type, int protocol, int sv[2]) ++{ ++ int ret; ++ ++#ifdef SOCK_CLOEXEC ++ ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); ++ if (ret != -1 || errno != EINVAL) { ++ return ret; ++ } ++#endif ++ ret = socketpair(domain, type, protocol, sv); ++ if (ret == 0) { ++ qemu_set_cloexec(sv[0]); ++ qemu_set_cloexec(sv[1]); ++ } ++ ++ return ret; ++} ++ ++char * ++qemu_get_local_state_dir(void) ++{ ++ return get_relocated_path(CONFIG_QEMU_LOCALSTATEDIR); ++} ++ ++void qemu_set_tty_echo(int fd, bool echo) ++{ ++ struct termios tty; ++ ++ tcgetattr(fd, &tty); ++ ++ if (echo) { ++ tty.c_lflag |= ECHO | ECHONL | ICANON | IEXTEN; ++ } else { ++ tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); ++ } ++ ++ tcsetattr(fd, TCSANOW, &tty); ++} ++ ++#ifdef CONFIG_LINUX ++static void sigbus_handler(int signal, siginfo_t *siginfo, void *ctx) ++#else /* CONFIG_LINUX */ ++static void sigbus_handler(int signal) ++#endif /* CONFIG_LINUX */ ++{ ++ int i; ++ ++ if (sigbus_memset_context) { ++ for (i = 0; i < sigbus_memset_context->num_threads; i++) { ++ MemsetThread *thread = &sigbus_memset_context->threads[i]; ++ ++ if (qemu_thread_is_self(&thread->pgthread)) { ++ siglongjmp(thread->env, 1); ++ } ++ } ++ } ++ ++#ifdef CONFIG_LINUX ++ /* ++ * We assume that the MCE SIGBUS handler could have been registered. We ++ * should never receive BUS_MCEERR_AO on any of our threads, but only on ++ * the main thread registered for PR_MCE_KILL_EARLY. Further, we should not ++ * receive BUS_MCEERR_AR triggered by action of other threads on one of ++ * our threads. So, no need to check for unrelated SIGBUS when seeing one ++ * for our threads. ++ * ++ * We will forward to the MCE handler, which will either handle the SIGBUS ++ * or reinstall the default SIGBUS handler and reraise the SIGBUS. The ++ * default SIGBUS handler will crash the process, so we don't care. ++ */ ++ if (sigbus_oldact.sa_flags & SA_SIGINFO) { ++ sigbus_oldact.sa_sigaction(signal, siginfo, ctx); ++ return; ++ } ++#endif /* CONFIG_LINUX */ ++ warn_report("qemu_prealloc_mem: unrelated SIGBUS detected and ignored"); ++} ++ ++static void *do_touch_pages(void *arg) ++{ ++ MemsetThread *memset_args = (MemsetThread *)arg; ++ sigset_t set, oldset; ++ int ret = 0; ++ ++ /* ++ * On Linux, the page faults from the loop below can cause mmap_sem ++ * contention with allocation of the thread stacks. Do not start ++ * clearing until all threads have been created. ++ */ ++ qemu_mutex_lock(&page_mutex); ++ while (!memset_args->context->all_threads_created) { ++ qemu_cond_wait(&page_cond, &page_mutex); ++ } ++ qemu_mutex_unlock(&page_mutex); ++ ++ /* unblock SIGBUS */ ++ sigemptyset(&set); ++ sigaddset(&set, SIGBUS); ++ pthread_sigmask(SIG_UNBLOCK, &set, &oldset); ++ ++ if (sigsetjmp(memset_args->env, 1)) { ++ ret = -EFAULT; ++ } else { ++ char *addr = memset_args->addr; ++ size_t numpages = memset_args->numpages; ++ size_t hpagesize = memset_args->hpagesize; ++ size_t i; ++ for (i = 0; i < numpages; i++) { ++ /* ++ * Read & write back the same value, so we don't ++ * corrupt existing user/app data that might be ++ * stored. ++ * ++ * 'volatile' to stop compiler optimizing this away ++ * to a no-op ++ */ ++ *(volatile char *)addr = *addr; ++ addr += hpagesize; ++ } ++ } ++ pthread_sigmask(SIG_SETMASK, &oldset, NULL); ++ return (void *)(uintptr_t)ret; ++} ++ ++static void *do_madv_populate_write_pages(void *arg) ++{ ++ MemsetThread *memset_args = (MemsetThread *)arg; ++ const size_t size = memset_args->numpages * memset_args->hpagesize; ++ char * const addr = memset_args->addr; ++ int ret = 0; ++ ++ /* See do_touch_pages(). */ ++ qemu_mutex_lock(&page_mutex); ++ while (!memset_args->context->all_threads_created) { ++ qemu_cond_wait(&page_cond, &page_mutex); ++ } ++ qemu_mutex_unlock(&page_mutex); ++ ++ if (size && qemu_madvise(addr, size, QEMU_MADV_POPULATE_WRITE)) { ++ ret = -errno; ++ } ++ return (void *)(uintptr_t)ret; ++} ++ ++static inline int get_memset_num_threads(size_t hpagesize, size_t numpages, ++ int max_threads) ++{ ++ long host_procs = sysconf(_SC_NPROCESSORS_ONLN); ++ int ret = 1; ++ ++ if (host_procs > 0) { ++ ret = MIN(MIN(host_procs, MAX_MEM_PREALLOC_THREAD_COUNT), max_threads); ++ } ++ ++ /* Especially with gigantic pages, don't create more threads than pages. */ ++ ret = MIN(ret, numpages); ++ /* Don't start threads to prealloc comparatively little memory. */ ++ ret = MIN(ret, MAX(1, hpagesize * numpages / (64 * MiB))); ++ ++ /* In case sysconf() fails, we fall back to single threaded */ ++ return ret; ++} ++ ++static int wait_and_free_mem_prealloc_context(MemsetContext *context) ++{ ++ int i, ret = 0, tmp; ++ ++ for (i = 0; i < context->num_threads; i++) { ++ tmp = (uintptr_t)qemu_thread_join(&context->threads[i].pgthread); ++ ++ if (tmp) { ++ ret = tmp; ++ } ++ } ++ g_free(context->threads); ++ g_free(context); ++ return ret; ++} ++ ++static int touch_all_pages(char *area, size_t hpagesize, size_t numpages, ++ int max_threads, ThreadContext *tc, bool async, ++ bool use_madv_populate_write) ++{ ++ static gsize initialized = 0; ++ MemsetContext *context = g_malloc0(sizeof(MemsetContext)); ++ size_t numpages_per_thread, leftover; ++ void *(*touch_fn)(void *); ++ int ret, i = 0; ++ char *addr = area; ++ ++ /* ++ * Asynchronous preallocation is only allowed when using MADV_POPULATE_WRITE ++ * and prealloc context for thread placement. ++ */ ++ if (!use_madv_populate_write || !tc) { ++ async = false; ++ } ++ ++ context->num_threads = ++ get_memset_num_threads(hpagesize, numpages, max_threads); ++ ++ if (g_once_init_enter(&initialized)) { ++ qemu_mutex_init(&page_mutex); ++ qemu_cond_init(&page_cond); ++ g_once_init_leave(&initialized, 1); ++ } ++ ++ if (use_madv_populate_write) { ++ /* ++ * Avoid creating a single thread for MADV_POPULATE_WRITE when ++ * preallocating synchronously. ++ */ ++ if (context->num_threads == 1 && !async) { ++ ret = 0; ++ if (qemu_madvise(area, hpagesize * numpages, ++ QEMU_MADV_POPULATE_WRITE)) { ++ ret = -errno; ++ } ++ g_free(context); ++ return ret; ++ } ++ touch_fn = do_madv_populate_write_pages; ++ } else { ++ touch_fn = do_touch_pages; ++ } ++ ++ context->threads = g_new0(MemsetThread, context->num_threads); ++ numpages_per_thread = numpages / context->num_threads; ++ leftover = numpages % context->num_threads; ++ for (i = 0; i < context->num_threads; i++) { ++ context->threads[i].addr = addr; ++ context->threads[i].numpages = numpages_per_thread + (i < leftover); ++ context->threads[i].hpagesize = hpagesize; ++ context->threads[i].context = context; ++ if (tc) { ++ thread_context_create_thread(tc, &context->threads[i].pgthread, ++ "touch_pages", ++ touch_fn, &context->threads[i], ++ QEMU_THREAD_JOINABLE); ++ } else { ++ qemu_thread_create(&context->threads[i].pgthread, "touch_pages", ++ touch_fn, &context->threads[i], ++ QEMU_THREAD_JOINABLE); ++ } ++ addr += context->threads[i].numpages * hpagesize; ++ } ++ ++ if (async) { ++ /* ++ * async requests currently require the BQL. Add it to the list and kick ++ * preallocation off during qemu_finish_async_prealloc_mem(). ++ */ ++ assert(bql_locked()); ++ QLIST_INSERT_HEAD(&memset_contexts, context, next); ++ return 0; ++ } ++ ++ if (!use_madv_populate_write) { ++ sigbus_memset_context = context; ++ } ++ ++ qemu_mutex_lock(&page_mutex); ++ context->all_threads_created = true; ++ qemu_cond_broadcast(&page_cond); ++ qemu_mutex_unlock(&page_mutex); ++ ++ ret = wait_and_free_mem_prealloc_context(context); ++ ++ if (!use_madv_populate_write) { ++ sigbus_memset_context = NULL; ++ } ++ return ret; ++} ++ ++bool qemu_finish_async_prealloc_mem(Error **errp) ++{ ++ int ret = 0, tmp; ++ MemsetContext *context, *next_context; ++ ++ /* Waiting for preallocation requires the BQL. */ ++ assert(bql_locked()); ++ if (QLIST_EMPTY(&memset_contexts)) { ++ return true; ++ } ++ ++ qemu_mutex_lock(&page_mutex); ++ QLIST_FOREACH(context, &memset_contexts, next) { ++ context->all_threads_created = true; ++ } ++ qemu_cond_broadcast(&page_cond); ++ qemu_mutex_unlock(&page_mutex); ++ ++ QLIST_FOREACH_SAFE(context, &memset_contexts, next, next_context) { ++ QLIST_REMOVE(context, next); ++ tmp = wait_and_free_mem_prealloc_context(context); ++ if (tmp) { ++ ret = tmp; ++ } ++ } ++ ++ if (ret) { ++ error_setg_errno(errp, -ret, ++ "qemu_prealloc_mem: preallocating memory failed"); ++ return false; ++ } ++ return true; ++} ++ ++static bool madv_populate_write_possible(char *area, size_t pagesize) ++{ ++ return !qemu_madvise(area, pagesize, QEMU_MADV_POPULATE_WRITE) || ++ errno != EINVAL; ++} ++ ++bool qemu_prealloc_mem(int fd, char *area, size_t sz, int max_threads, ++ ThreadContext *tc, bool async, Error **errp) ++{ ++ static gsize initialized; ++ int ret; ++ size_t hpagesize = qemu_fd_getpagesize(fd); ++ size_t numpages = DIV_ROUND_UP(sz, hpagesize); ++ bool use_madv_populate_write; ++ struct sigaction act; ++ bool rv = true; ++ ++ /* ++ * Sense on every invocation, as MADV_POPULATE_WRITE cannot be used for ++ * some special mappings, such as mapping /dev/mem. ++ */ ++ use_madv_populate_write = madv_populate_write_possible(area, hpagesize); ++ ++ if (!use_madv_populate_write) { ++ if (g_once_init_enter(&initialized)) { ++ qemu_mutex_init(&sigbus_mutex); ++ g_once_init_leave(&initialized, 1); ++ } ++ ++ qemu_mutex_lock(&sigbus_mutex); ++ memset(&act, 0, sizeof(act)); ++#ifdef CONFIG_LINUX ++ act.sa_sigaction = &sigbus_handler; ++ act.sa_flags = SA_SIGINFO; ++#else /* CONFIG_LINUX */ ++ act.sa_handler = &sigbus_handler; ++ act.sa_flags = 0; ++#endif /* CONFIG_LINUX */ ++ ++ ret = sigaction(SIGBUS, &act, &sigbus_oldact); ++ if (ret) { ++ qemu_mutex_unlock(&sigbus_mutex); ++ error_setg_errno(errp, errno, ++ "qemu_prealloc_mem: failed to install signal handler"); ++ return false; ++ } ++ } ++ ++ /* touch pages simultaneously */ ++ ret = touch_all_pages(area, hpagesize, numpages, max_threads, tc, async, ++ use_madv_populate_write); ++ if (ret) { ++ error_setg_errno(errp, -ret, ++ "qemu_prealloc_mem: preallocating memory failed"); ++ rv = false; ++ } ++ ++ if (!use_madv_populate_write) { ++ ret = sigaction(SIGBUS, &sigbus_oldact, NULL); ++ if (ret) { ++ /* Terminate QEMU since it can't recover from error */ ++ perror("qemu_prealloc_mem: failed to reinstall signal handler"); ++ exit(1); ++ } ++ qemu_mutex_unlock(&sigbus_mutex); ++ } ++ return rv; ++} ++ ++char *qemu_get_pid_name(pid_t pid) ++{ ++ char *name = NULL; ++ ++#if defined(__FreeBSD__) ++ /* BSDs don't have /proc, but they provide a nice substitute */ ++ struct kinfo_proc *proc = kinfo_getproc(pid); ++ ++ if (proc) { ++ name = g_strdup(proc->ki_comm); ++ free(proc); ++ } ++#else ++ /* Assume a system with reasonable procfs */ ++ char *pid_path; ++ size_t len; ++ ++ pid_path = g_strdup_printf("/proc/%d/cmdline", pid); ++ g_file_get_contents(pid_path, &name, &len, NULL); ++ g_free(pid_path); ++#endif ++ ++ return name; ++} ++ ++ ++void *qemu_alloc_stack(size_t *sz) ++{ ++ void *ptr; ++ int flags; ++#ifdef CONFIG_DEBUG_STACK_USAGE ++ void *ptr2; ++#endif ++ size_t pagesz = qemu_real_host_page_size(); ++#ifdef _SC_THREAD_STACK_MIN ++ /* avoid stacks smaller than _SC_THREAD_STACK_MIN */ ++ long min_stack_sz = sysconf(_SC_THREAD_STACK_MIN); ++ *sz = MAX(MAX(min_stack_sz, 0), *sz); ++#endif ++ /* adjust stack size to a multiple of the page size */ ++ *sz = ROUND_UP(*sz, pagesz); ++ /* allocate one extra page for the guard page */ ++ *sz += pagesz; ++ ++ flags = MAP_PRIVATE | MAP_ANONYMOUS; ++#if defined(MAP_STACK) && defined(__OpenBSD__) ++ /* Only enable MAP_STACK on OpenBSD. Other OS's such as ++ * Linux/FreeBSD/NetBSD have a flag with the same name ++ * but have differing functionality. OpenBSD will SEGV ++ * if it spots execution with a stack pointer pointing ++ * at memory that was not allocated with MAP_STACK. ++ */ ++ flags |= MAP_STACK; ++#endif ++ ++ ptr = mmap(NULL, *sz, PROT_READ | PROT_WRITE, flags, -1, 0); ++ if (ptr == MAP_FAILED) { ++ perror("failed to allocate memory for stack"); ++ abort(); ++ } ++ ++ /* Stack grows down -- guard page at the bottom. */ ++ if (mprotect(ptr, pagesz, PROT_NONE) != 0) { ++ perror("failed to set up stack guard page"); ++ abort(); ++ } ++ ++#ifdef CONFIG_DEBUG_STACK_USAGE ++ for (ptr2 = ptr + pagesz; ptr2 < ptr + *sz; ptr2 += sizeof(uint32_t)) { ++ *(uint32_t *)ptr2 = 0xdeadbeaf; ++ } ++#endif ++ ++ return ptr; ++} ++ ++#ifdef CONFIG_DEBUG_STACK_USAGE ++static __thread unsigned int max_stack_usage; ++#endif ++ ++void qemu_free_stack(void *stack, size_t sz) ++{ ++#ifdef CONFIG_DEBUG_STACK_USAGE ++ unsigned int usage; ++ void *ptr; ++ ++ for (ptr = stack + qemu_real_host_page_size(); ptr < stack + sz; ++ ptr += sizeof(uint32_t)) { ++ if (*(uint32_t *)ptr != 0xdeadbeaf) { ++ break; ++ } ++ } ++ usage = sz - (uintptr_t) (ptr - stack); ++ if (usage > max_stack_usage) { ++ error_report("thread %d max stack usage increased from %u to %u", ++ qemu_get_thread_id(), max_stack_usage, usage); ++ max_stack_usage = usage; ++ } ++#endif ++ ++ munmap(stack, sz); ++} ++ ++/* ++ * Disable CFI checks. ++ * We are going to call a signal handler directly. Such handler may or may not ++ * have been defined in our binary, so there's no guarantee that the pointer ++ * used to set the handler is a cfi-valid pointer. Since the handlers are ++ * stored in kernel memory, changing the handler to an attacker-defined ++ * function requires being able to call a sigaction() syscall, ++ * which is not as easy as overwriting a pointer in memory. ++ */ ++QEMU_DISABLE_CFI ++void sigaction_invoke(struct sigaction *action, ++ struct qemu_signalfd_siginfo *info) ++{ ++ siginfo_t si = {}; ++ si.si_signo = info->ssi_signo; ++ si.si_errno = info->ssi_errno; ++ si.si_code = info->ssi_code; ++ ++ /* Convert the minimal set of fields defined by POSIX. ++ * Positive si_code values are reserved for kernel-generated ++ * signals, where the valid siginfo fields are determined by ++ * the signal number. But according to POSIX, it is unspecified ++ * whether SI_USER and SI_QUEUE have values less than or equal to ++ * zero. ++ */ ++ if (info->ssi_code == SI_USER || info->ssi_code == SI_QUEUE || ++ info->ssi_code <= 0) { ++ /* SIGTERM, etc. */ ++ si.si_pid = info->ssi_pid; ++ si.si_uid = info->ssi_uid; ++ } else if (info->ssi_signo == SIGILL || info->ssi_signo == SIGFPE || ++ info->ssi_signo == SIGSEGV || info->ssi_signo == SIGBUS) { ++ si.si_addr = (void *)(uintptr_t)info->ssi_addr; ++ } else if (info->ssi_signo == SIGCHLD) { ++ si.si_pid = info->ssi_pid; ++ si.si_status = info->ssi_status; ++ si.si_uid = info->ssi_uid; ++ } ++ action->sa_sigaction(info->ssi_signo, &si, NULL); ++} ++ ++size_t qemu_get_host_physmem(void) ++{ ++#ifdef _SC_PHYS_PAGES ++ long pages = sysconf(_SC_PHYS_PAGES); ++ if (pages > 0) { ++ if (pages > SIZE_MAX / qemu_real_host_page_size()) { ++ return SIZE_MAX; ++ } else { ++ return pages * qemu_real_host_page_size(); ++ } ++ } ++#endif ++ return 0; ++} ++ ++int qemu_msync(void *addr, size_t length, int fd) ++{ ++ size_t align_mask = ~(qemu_real_host_page_size() - 1); ++ ++ /** ++ * There are no strict reqs as per the length of mapping ++ * to be synced. Still the length needs to follow the address ++ * alignment changes. Additionally - round the size to the multiple ++ * of PAGE_SIZE ++ */ ++ length += ((uintptr_t)addr & (qemu_real_host_page_size() - 1)); ++ length = (length + ~align_mask) & align_mask; ++ ++ addr = (void *)((uintptr_t)addr & align_mask); ++ ++ return msync(addr, length, MS_SYNC); ++} ++ ++static bool qemu_close_all_open_fd_proc(const int *skip, unsigned int nskip) ++{ ++ struct dirent *de; ++ int fd, dfd; ++ DIR *dir; ++ unsigned int skip_start = 0, skip_end = nskip; ++ ++ dir = opendir("/proc/self/fd"); ++ if (!dir) { ++ /* If /proc is not mounted, there is nothing that can be done. */ ++ return false; ++ } ++ /* Avoid closing the directory. */ ++ dfd = dirfd(dir); ++ ++ for (de = readdir(dir); de; de = readdir(dir)) { ++ bool close_fd = true; ++ ++ if (de->d_name[0] == '.') { ++ continue; ++ } ++ fd = atoi(de->d_name); ++ if (fd == dfd) { ++ continue; ++ } ++ ++ for (unsigned int i = skip_start; i < skip_end; i++) { ++ if (fd < skip[i]) { ++ /* We are below the next skipped fd, break */ ++ break; ++ } else if (fd == skip[i]) { ++ close_fd = false; ++ /* Restrict the range as we found fds matching start/end */ ++ if (i == skip_start) { ++ skip_start++; ++ } else if (i == skip_end) { ++ skip_end--; ++ } ++ break; ++ } ++ } ++ ++ if (close_fd) { ++ close(fd); ++ } ++ } ++ closedir(dir); ++ ++ return true; ++} ++ ++static bool qemu_close_all_open_fd_close_range(const int *skip, ++ unsigned int nskip, ++ int open_max) ++{ ++#ifdef CONFIG_CLOSE_RANGE ++ int max_fd = open_max - 1; ++ int first = 0, last; ++ unsigned int cur_skip = 0; ++ int ret; ++ ++ do { ++ /* Find the start boundary of the range to close */ ++ while (cur_skip < nskip && first == skip[cur_skip]) { ++ cur_skip++; ++ first++; ++ } ++ ++ /* Find the upper boundary of the range to close */ ++ last = max_fd; ++ if (cur_skip < nskip) { ++ last = skip[cur_skip] - 1; ++ last = MIN(last, max_fd); ++ } ++ ++ /* With the adjustments to the range, we might be done. */ ++ if (first > last) { ++ break; ++ } ++ ++ ret = close_range(first, last, 0); ++ if (ret < 0) { ++ return false; ++ } ++ ++ first = last + 1; ++ } while (last < max_fd); ++ ++ return true; ++#else ++ return false; ++#endif ++} ++ ++static void qemu_close_all_open_fd_fallback(const int *skip, unsigned int nskip, ++ int open_max) ++{ ++ unsigned int cur_skip = 0; ++ ++ /* Fallback */ ++ for (int i = 0; i < open_max; i++) { ++ if (cur_skip < nskip && i == skip[cur_skip]) { ++ cur_skip++; ++ continue; ++ } ++ close(i); ++ } ++} ++ ++/* ++ * Close all open file descriptors. ++ */ ++void qemu_close_all_open_fd(const int *skip, unsigned int nskip) ++{ ++ int open_max = sysconf(_SC_OPEN_MAX); ++ ++ assert(skip != NULL || nskip == 0); ++ ++ if (!qemu_close_all_open_fd_close_range(skip, nskip, open_max) && ++ !qemu_close_all_open_fd_proc(skip, nskip)) { ++ qemu_close_all_open_fd_fallback(skip, nskip, open_max); ++ } ++} +diff --git a/qcow2/lib/util/qemu-coroutine-lock.c b/qcow2/lib/util/qemu-coroutine-lock.c +new file mode 100644 +index 00000000..25344353 +--- /dev/null ++++ b/qcow2/lib/util/qemu-coroutine-lock.c +@@ -0,0 +1,469 @@ ++/* ++ * coroutine queues and locks ++ * ++ * Copyright (c) 2011 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ * ++ * The lock-free mutex implementation is based on OSv ++ * (core/lfmutex.cc, include/lockfree/mutex.hh). ++ * Copyright (C) 2013 Cloudius Systems, Ltd. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/coroutine_int.h" ++#include "qemu/processor.h" ++#include "qemu/queue.h" ++#include "block/aio.h" ++#include "trace.h" ++ ++void qemu_co_queue_init(CoQueue *queue) ++{ ++ QSIMPLEQ_INIT(&queue->entries); ++} ++ ++void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock, ++ CoQueueWaitFlags flags) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ if (flags & CO_QUEUE_WAIT_FRONT) { ++ QSIMPLEQ_INSERT_HEAD(&queue->entries, self, co_queue_next); ++ } else { ++ QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next); ++ } ++ ++ if (lock) { ++ qemu_lockable_unlock(lock); ++ } ++ ++ /* There is no race condition here. Other threads will call ++ * aio_co_schedule on our AioContext, which can reenter this ++ * coroutine but only after this yield and after the main loop ++ * has gone through the next iteration. ++ */ ++ qemu_coroutine_yield(); ++ assert(qemu_in_coroutine()); ++ ++ /* TODO: OSv implements wait morphing here, where the wakeup ++ * primitive automatically places the woken coroutine on the ++ * mutex's queue. This avoids the thundering herd effect. ++ * This could be implemented for CoMutexes, but not really for ++ * other cases of QemuLockable. ++ */ ++ if (lock) { ++ qemu_lockable_lock(lock); ++ } ++} ++ ++bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock) ++{ ++ Coroutine *next; ++ ++ next = QSIMPLEQ_FIRST(&queue->entries); ++ if (!next) { ++ return false; ++ } ++ ++ QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next); ++ if (lock) { ++ qemu_lockable_unlock(lock); ++ } ++ aio_co_wake(next); ++ if (lock) { ++ qemu_lockable_lock(lock); ++ } ++ return true; ++} ++ ++bool coroutine_fn qemu_co_queue_next(CoQueue *queue) ++{ ++ /* No unlock/lock needed in coroutine context. */ ++ return qemu_co_enter_next_impl(queue, NULL); ++} ++ ++void qemu_co_enter_all_impl(CoQueue *queue, QemuLockable *lock) ++{ ++ while (qemu_co_enter_next_impl(queue, lock)) { ++ /* just loop */ ++ } ++} ++ ++void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) ++{ ++ /* No unlock/lock needed in coroutine context. */ ++ qemu_co_enter_all_impl(queue, NULL); ++} ++ ++bool qemu_co_queue_empty(CoQueue *queue) ++{ ++ return QSIMPLEQ_FIRST(&queue->entries) == NULL; ++} ++ ++/* The wait records are handled with a multiple-producer, single-consumer ++ * lock-free queue. There cannot be two concurrent pop_waiter() calls ++ * because pop_waiter() can only be called while mutex->handoff is zero. ++ * This can happen in three cases: ++ * - in qemu_co_mutex_unlock, before the hand-off protocol has started. ++ * In this case, qemu_co_mutex_lock will see mutex->handoff == 0 and ++ * not take part in the handoff. ++ * - in qemu_co_mutex_lock, if it steals the hand-off responsibility from ++ * qemu_co_mutex_unlock. In this case, qemu_co_mutex_unlock will fail ++ * the cmpxchg (it will see either 0 or the next sequence value) and ++ * exit. The next hand-off cannot begin until qemu_co_mutex_lock has ++ * woken up someone. ++ * - in qemu_co_mutex_unlock, if it takes the hand-off token itself. ++ * In this case another iteration starts with mutex->handoff == 0; ++ * a concurrent qemu_co_mutex_lock will fail the cmpxchg, and ++ * qemu_co_mutex_unlock will go back to case (1). ++ * ++ * The following functions manage this queue. ++ */ ++typedef struct CoWaitRecord { ++ Coroutine *co; ++ QSLIST_ENTRY(CoWaitRecord) next; ++} CoWaitRecord; ++ ++static void coroutine_fn push_waiter(CoMutex *mutex, CoWaitRecord *w) ++{ ++ w->co = qemu_coroutine_self(); ++ QSLIST_INSERT_HEAD_ATOMIC(&mutex->from_push, w, next); ++} ++ ++static void move_waiters(CoMutex *mutex) ++{ ++ QSLIST_HEAD(, CoWaitRecord) reversed; ++ QSLIST_MOVE_ATOMIC(&reversed, &mutex->from_push); ++ while (!QSLIST_EMPTY(&reversed)) { ++ CoWaitRecord *w = QSLIST_FIRST(&reversed); ++ QSLIST_REMOVE_HEAD(&reversed, next); ++ QSLIST_INSERT_HEAD(&mutex->to_pop, w, next); ++ } ++} ++ ++static CoWaitRecord *pop_waiter(CoMutex *mutex) ++{ ++ CoWaitRecord *w; ++ ++ if (QSLIST_EMPTY(&mutex->to_pop)) { ++ move_waiters(mutex); ++ if (QSLIST_EMPTY(&mutex->to_pop)) { ++ return NULL; ++ } ++ } ++ w = QSLIST_FIRST(&mutex->to_pop); ++ QSLIST_REMOVE_HEAD(&mutex->to_pop, next); ++ return w; ++} ++ ++static bool has_waiters(CoMutex *mutex) ++{ ++ return QSLIST_EMPTY(&mutex->to_pop) || QSLIST_EMPTY(&mutex->from_push); ++} ++ ++void qemu_co_mutex_init(CoMutex *mutex) ++{ ++ memset(mutex, 0, sizeof(*mutex)); ++} ++ ++static void coroutine_fn qemu_co_mutex_wake(CoMutex *mutex, Coroutine *co) ++{ ++ /* Read co before co->ctx; pairs with smp_wmb() in ++ * qemu_coroutine_enter(). ++ */ ++ smp_read_barrier_depends(); ++ mutex->ctx = co->ctx; ++ aio_co_wake(co); ++} ++ ++static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx, ++ CoMutex *mutex) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ CoWaitRecord w; ++ unsigned old_handoff; ++ ++ trace_qemu_co_mutex_lock_entry(mutex, self); ++ push_waiter(mutex, &w); ++ ++ /* ++ * Add waiter before reading mutex->handoff. Pairs with qatomic_set_mb ++ * in qemu_co_mutex_unlock. ++ */ ++ smp_mb__after_rmw(); ++ ++ /* This is the "Responsibility Hand-Off" protocol; a lock() picks from ++ * a concurrent unlock() the responsibility of waking somebody up. ++ */ ++ old_handoff = qatomic_read(&mutex->handoff); ++ if (old_handoff && ++ has_waiters(mutex) && ++ qatomic_cmpxchg(&mutex->handoff, old_handoff, 0) == old_handoff) { ++ /* There can be no concurrent pops, because there can be only ++ * one active handoff at a time. ++ */ ++ CoWaitRecord *to_wake = pop_waiter(mutex); ++ Coroutine *co = to_wake->co; ++ if (co == self) { ++ /* We got the lock ourselves! */ ++ assert(to_wake == &w); ++ mutex->ctx = ctx; ++ return; ++ } ++ ++ qemu_co_mutex_wake(mutex, co); ++ } ++ ++ qemu_coroutine_yield(); ++ trace_qemu_co_mutex_lock_return(mutex, self); ++} ++ ++void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) ++{ ++ AioContext *ctx = qemu_get_current_aio_context(); ++ Coroutine *self = qemu_coroutine_self(); ++ int waiters, i; ++ ++ /* Running a very small critical section on pthread_mutex_t and CoMutex ++ * shows that pthread_mutex_t is much faster because it doesn't actually ++ * go to sleep. What happens is that the critical section is shorter ++ * than the latency of entering the kernel and thus FUTEX_WAIT always ++ * fails. With CoMutex there is no such latency but you still want to ++ * avoid wait and wakeup. So introduce it artificially. ++ */ ++ i = 0; ++retry_fast_path: ++ waiters = qatomic_cmpxchg(&mutex->locked, 0, 1); ++ if (waiters != 0) { ++ while (waiters == 1 && ++i < 1000) { ++ if (qatomic_read(&mutex->ctx) == ctx) { ++ break; ++ } ++ if (qatomic_read(&mutex->locked) == 0) { ++ goto retry_fast_path; ++ } ++ cpu_relax(); ++ } ++ waiters = qatomic_fetch_inc(&mutex->locked); ++ } ++ ++ if (waiters == 0) { ++ /* Uncontended. */ ++ trace_qemu_co_mutex_lock_uncontended(mutex, self); ++ mutex->ctx = ctx; ++ } else { ++ qemu_co_mutex_lock_slowpath(ctx, mutex); ++ } ++ mutex->holder = self; ++ self->locks_held++; ++} ++ ++void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ ++ trace_qemu_co_mutex_unlock_entry(mutex, self); ++ ++ assert(mutex->locked); ++ assert(mutex->holder == self); ++ assert(qemu_in_coroutine()); ++ ++ mutex->ctx = NULL; ++ mutex->holder = NULL; ++ self->locks_held--; ++ if (qatomic_fetch_dec(&mutex->locked) == 1) { ++ /* No waiting qemu_co_mutex_lock(). Pfew, that was easy! */ ++ return; ++ } ++ ++ for (;;) { ++ CoWaitRecord *to_wake = pop_waiter(mutex); ++ unsigned our_handoff; ++ ++ if (to_wake) { ++ qemu_co_mutex_wake(mutex, to_wake->co); ++ break; ++ } ++ ++ /* Some concurrent lock() is in progress (we know this because ++ * mutex->locked was >1) but it hasn't yet put itself on the wait ++ * queue. Pick a sequence number for the handoff protocol (not 0). ++ */ ++ if (++mutex->sequence == 0) { ++ mutex->sequence = 1; ++ } ++ ++ our_handoff = mutex->sequence; ++ /* Set handoff before checking for waiters. */ ++ qatomic_set_mb(&mutex->handoff, our_handoff); ++ if (!has_waiters(mutex)) { ++ /* The concurrent lock has not added itself yet, so it ++ * will be able to pick our handoff. ++ */ ++ break; ++ } ++ ++ /* Try to do the handoff protocol ourselves; if somebody else has ++ * already taken it, however, we're done and they're responsible. ++ */ ++ if (qatomic_cmpxchg(&mutex->handoff, our_handoff, 0) != our_handoff) { ++ break; ++ } ++ } ++ ++ trace_qemu_co_mutex_unlock_return(mutex, self); ++} ++ ++struct CoRwTicket { ++ bool read; ++ Coroutine *co; ++ QSIMPLEQ_ENTRY(CoRwTicket) next; ++}; ++ ++void qemu_co_rwlock_init(CoRwlock *lock) ++{ ++ qemu_co_mutex_init(&lock->mutex); ++ lock->owners = 0; ++ QSIMPLEQ_INIT(&lock->tickets); ++} ++ ++/* Releases the internal CoMutex. */ ++static void coroutine_fn qemu_co_rwlock_maybe_wake_one(CoRwlock *lock) ++{ ++ CoRwTicket *tkt = QSIMPLEQ_FIRST(&lock->tickets); ++ Coroutine *co = NULL; ++ ++ /* ++ * Setting lock->owners here prevents rdlock and wrlock from ++ * sneaking in between unlock and wake. ++ */ ++ ++ if (tkt) { ++ if (tkt->read) { ++ if (lock->owners >= 0) { ++ lock->owners++; ++ co = tkt->co; ++ } ++ } else { ++ if (lock->owners == 0) { ++ lock->owners = -1; ++ co = tkt->co; ++ } ++ } ++ } ++ ++ if (co) { ++ QSIMPLEQ_REMOVE_HEAD(&lock->tickets, next); ++ qemu_co_mutex_unlock(&lock->mutex); ++ aio_co_wake(co); ++ } else { ++ qemu_co_mutex_unlock(&lock->mutex); ++ } ++} ++ ++void coroutine_fn qemu_co_rwlock_rdlock(CoRwlock *lock) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ ++ qemu_co_mutex_lock(&lock->mutex); ++ /* For fairness, wait if a writer is in line. */ ++ if (lock->owners == 0 || (lock->owners > 0 && QSIMPLEQ_EMPTY(&lock->tickets))) { ++ lock->owners++; ++ qemu_co_mutex_unlock(&lock->mutex); ++ } else { ++ CoRwTicket my_ticket = { true, self }; ++ ++ QSIMPLEQ_INSERT_TAIL(&lock->tickets, &my_ticket, next); ++ qemu_co_mutex_unlock(&lock->mutex); ++ qemu_coroutine_yield(); ++ assert(lock->owners >= 1); ++ ++ /* Possibly wake another reader, which will wake the next in line. */ ++ qemu_co_mutex_lock(&lock->mutex); ++ qemu_co_rwlock_maybe_wake_one(lock); ++ } ++ ++ self->locks_held++; ++} ++ ++void coroutine_fn qemu_co_rwlock_unlock(CoRwlock *lock) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ ++ assert(qemu_in_coroutine()); ++ self->locks_held--; ++ ++ qemu_co_mutex_lock(&lock->mutex); ++ if (lock->owners > 0) { ++ lock->owners--; ++ } else { ++ assert(lock->owners == -1); ++ lock->owners = 0; ++ } ++ ++ qemu_co_rwlock_maybe_wake_one(lock); ++} ++ ++void coroutine_fn qemu_co_rwlock_downgrade(CoRwlock *lock) ++{ ++ qemu_co_mutex_lock(&lock->mutex); ++ assert(lock->owners == -1); ++ lock->owners = 1; ++ ++ /* Possibly wake another reader, which will wake the next in line. */ ++ qemu_co_rwlock_maybe_wake_one(lock); ++} ++ ++void coroutine_fn qemu_co_rwlock_wrlock(CoRwlock *lock) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ ++ qemu_co_mutex_lock(&lock->mutex); ++ if (lock->owners == 0) { ++ lock->owners = -1; ++ qemu_co_mutex_unlock(&lock->mutex); ++ } else { ++ CoRwTicket my_ticket = { false, qemu_coroutine_self() }; ++ ++ QSIMPLEQ_INSERT_TAIL(&lock->tickets, &my_ticket, next); ++ qemu_co_mutex_unlock(&lock->mutex); ++ qemu_coroutine_yield(); ++ assert(lock->owners == -1); ++ } ++ ++ self->locks_held++; ++} ++ ++void coroutine_fn qemu_co_rwlock_upgrade(CoRwlock *lock) ++{ ++ qemu_co_mutex_lock(&lock->mutex); ++ assert(lock->owners > 0); ++ /* For fairness, wait if a writer is in line. */ ++ if (lock->owners == 1 && QSIMPLEQ_EMPTY(&lock->tickets)) { ++ lock->owners = -1; ++ qemu_co_mutex_unlock(&lock->mutex); ++ } else { ++ CoRwTicket my_ticket = { false, qemu_coroutine_self() }; ++ ++ lock->owners--; ++ QSIMPLEQ_INSERT_TAIL(&lock->tickets, &my_ticket, next); ++ qemu_co_rwlock_maybe_wake_one(lock); ++ qemu_coroutine_yield(); ++ assert(lock->owners == -1); ++ } ++} +diff --git a/qcow2/lib/util/qemu-coroutine.c b/qcow2/lib/util/qemu-coroutine.c +new file mode 100644 +index 00000000..eb4eebef +--- /dev/null ++++ b/qcow2/lib/util/qemu-coroutine.c +@@ -0,0 +1,401 @@ ++/* ++ * QEMU coroutines ++ * ++ * Copyright IBM, Corp. 2011 ++ * ++ * Authors: ++ * Stefan Hajnoczi ++ * Kevin Wolf ++ * ++ * This work is licensed under the terms of the GNU LGPL, version 2 or later. ++ * See the COPYING.LIB file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "trace.h" ++#include "qemu/thread.h" ++#include "qemu/atomic.h" ++#include "qemu/coroutine_int.h" ++#include "qemu/coroutine-tls.h" ++#include "qemu/cutils.h" ++#include "block/aio.h" ++ ++enum { ++ COROUTINE_POOL_BATCH_MAX_SIZE = 128, ++}; ++ ++/* ++ * Coroutine creation and deletion is expensive so a pool of unused coroutines ++ * is kept as a cache. When the pool has coroutines available, they are ++ * recycled instead of creating new ones from scratch. Coroutines are added to ++ * the pool upon termination. ++ * ++ * The pool is global but each thread maintains a small local pool to avoid ++ * global pool contention. Threads fetch and return batches of coroutines from ++ * the global pool to maintain their local pool. The local pool holds up to two ++ * batches whereas the maximum size of the global pool is controlled by the ++ * qemu_coroutine_inc_pool_size() API. ++ * ++ * .-----------------------------------. ++ * | Batch 1 | Batch 2 | Batch 3 | ... | global_pool ++ * `-----------------------------------' ++ * ++ * .-------------------. ++ * | Batch 1 | Batch 2 | per-thread local_pool (maximum 2 batches) ++ * `-------------------' ++ */ ++typedef struct CoroutinePoolBatch { ++ /* Batches are kept in a list */ ++ QSLIST_ENTRY(CoroutinePoolBatch) next; ++ ++ /* This batch holds up to @COROUTINE_POOL_BATCH_MAX_SIZE coroutines */ ++ QSLIST_HEAD(, Coroutine) list; ++ unsigned int size; ++} CoroutinePoolBatch; ++ ++typedef QSLIST_HEAD(, CoroutinePoolBatch) CoroutinePool; ++ ++/* Host operating system limit on number of pooled coroutines */ ++static unsigned int global_pool_hard_max_size; ++ ++static QemuMutex global_pool_lock; /* protects the following variables */ ++static CoroutinePool global_pool = QSLIST_HEAD_INITIALIZER(global_pool); ++static unsigned int global_pool_size; ++static unsigned int global_pool_max_size = COROUTINE_POOL_BATCH_MAX_SIZE; ++ ++QEMU_DEFINE_STATIC_CO_TLS(CoroutinePool, local_pool); ++QEMU_DEFINE_STATIC_CO_TLS(Notifier, local_pool_cleanup_notifier); ++ ++static CoroutinePoolBatch *coroutine_pool_batch_new(void) ++{ ++ CoroutinePoolBatch *batch = g_new(CoroutinePoolBatch, 1); ++ ++ QSLIST_INIT(&batch->list); ++ batch->size = 0; ++ return batch; ++} ++ ++static void coroutine_pool_batch_delete(CoroutinePoolBatch *batch) ++{ ++ Coroutine *co; ++ Coroutine *tmp; ++ ++ QSLIST_FOREACH_SAFE(co, &batch->list, pool_next, tmp) { ++ QSLIST_REMOVE_HEAD(&batch->list, pool_next); ++ qemu_coroutine_delete(co); ++ } ++ g_free(batch); ++} ++ ++static void local_pool_cleanup(Notifier *n, void *value) ++{ ++ CoroutinePool *local_pool = get_ptr_local_pool(); ++ CoroutinePoolBatch *batch; ++ CoroutinePoolBatch *tmp; ++ ++ QSLIST_FOREACH_SAFE(batch, local_pool, next, tmp) { ++ QSLIST_REMOVE_HEAD(local_pool, next); ++ coroutine_pool_batch_delete(batch); ++ } ++} ++ ++/* Ensure the atexit notifier is registered */ ++static void local_pool_cleanup_init_once(void) ++{ ++ Notifier *notifier = get_ptr_local_pool_cleanup_notifier(); ++ if (!notifier->notify) { ++ notifier->notify = local_pool_cleanup; ++ qemu_thread_atexit_add(notifier); ++ } ++} ++ ++/* Helper to get the next unused coroutine from the local pool */ ++static Coroutine *coroutine_pool_get_local(void) ++{ ++ CoroutinePool *local_pool = get_ptr_local_pool(); ++ CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool); ++ Coroutine *co; ++ ++ if (unlikely(!batch)) { ++ return NULL; ++ } ++ ++ co = QSLIST_FIRST(&batch->list); ++ QSLIST_REMOVE_HEAD(&batch->list, pool_next); ++ batch->size--; ++ ++ if (batch->size == 0) { ++ QSLIST_REMOVE_HEAD(local_pool, next); ++ coroutine_pool_batch_delete(batch); ++ } ++ return co; ++} ++ ++/* Get the next batch from the global pool */ ++static void coroutine_pool_refill_local(void) ++{ ++ CoroutinePool *local_pool = get_ptr_local_pool(); ++ CoroutinePoolBatch *batch; ++ ++ WITH_QEMU_LOCK_GUARD(&global_pool_lock) { ++ batch = QSLIST_FIRST(&global_pool); ++ ++ if (batch) { ++ QSLIST_REMOVE_HEAD(&global_pool, next); ++ global_pool_size -= batch->size; ++ } ++ } ++ ++ if (batch) { ++ QSLIST_INSERT_HEAD(local_pool, batch, next); ++ local_pool_cleanup_init_once(); ++ } ++} ++ ++/* Add a batch of coroutines to the global pool */ ++static void coroutine_pool_put_global(CoroutinePoolBatch *batch) ++{ ++ WITH_QEMU_LOCK_GUARD(&global_pool_lock) { ++ unsigned int max = MIN(global_pool_max_size, ++ global_pool_hard_max_size); ++ ++ if (global_pool_size < max) { ++ QSLIST_INSERT_HEAD(&global_pool, batch, next); ++ ++ /* Overshooting the max pool size is allowed */ ++ global_pool_size += batch->size; ++ return; ++ } ++ } ++ ++ /* The global pool was full, so throw away this batch */ ++ coroutine_pool_batch_delete(batch); ++} ++ ++/* Get the next unused coroutine from the pool or return NULL */ ++static Coroutine *coroutine_pool_get(void) ++{ ++ Coroutine *co; ++ ++ co = coroutine_pool_get_local(); ++ if (!co) { ++ coroutine_pool_refill_local(); ++ co = coroutine_pool_get_local(); ++ } ++ return co; ++} ++ ++static void coroutine_pool_put(Coroutine *co) ++{ ++ CoroutinePool *local_pool = get_ptr_local_pool(); ++ CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool); ++ ++ if (unlikely(!batch)) { ++ batch = coroutine_pool_batch_new(); ++ QSLIST_INSERT_HEAD(local_pool, batch, next); ++ local_pool_cleanup_init_once(); ++ } ++ ++ if (unlikely(batch->size >= COROUTINE_POOL_BATCH_MAX_SIZE)) { ++ CoroutinePoolBatch *next = QSLIST_NEXT(batch, next); ++ ++ /* Is the local pool full? */ ++ if (next) { ++ QSLIST_REMOVE_HEAD(local_pool, next); ++ coroutine_pool_put_global(batch); ++ } ++ ++ batch = coroutine_pool_batch_new(); ++ QSLIST_INSERT_HEAD(local_pool, batch, next); ++ } ++ ++ QSLIST_INSERT_HEAD(&batch->list, co, pool_next); ++ batch->size++; ++} ++ ++Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque) ++{ ++ Coroutine *co = NULL; ++ ++ if (IS_ENABLED(CONFIG_COROUTINE_POOL)) { ++ co = coroutine_pool_get(); ++ } ++ ++ if (!co) { ++ co = qemu_coroutine_new(); ++ } ++ ++ co->entry = entry; ++ co->entry_arg = opaque; ++ QSIMPLEQ_INIT(&co->co_queue_wakeup); ++ return co; ++} ++ ++static void coroutine_delete(Coroutine *co) ++{ ++ co->caller = NULL; ++ ++ if (IS_ENABLED(CONFIG_COROUTINE_POOL)) { ++ coroutine_pool_put(co); ++ } else { ++ qemu_coroutine_delete(co); ++ } ++} ++ ++void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) ++{ ++ QSIMPLEQ_HEAD(, Coroutine) pending = QSIMPLEQ_HEAD_INITIALIZER(pending); ++ Coroutine *from = qemu_coroutine_self(); ++ ++ QSIMPLEQ_INSERT_TAIL(&pending, co, co_queue_next); ++ ++ /* Run co and any queued coroutines */ ++ while (!QSIMPLEQ_EMPTY(&pending)) { ++ Coroutine *to = QSIMPLEQ_FIRST(&pending); ++ CoroutineAction ret; ++ ++ /* ++ * Read to before to->scheduled; pairs with qatomic_cmpxchg in ++ * qemu_co_sleep(), aio_co_schedule() etc. ++ */ ++ smp_read_barrier_depends(); ++ ++ const char *scheduled = qatomic_read(&to->scheduled); ++ ++ QSIMPLEQ_REMOVE_HEAD(&pending, co_queue_next); ++ ++ trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg); ++ ++ /* if the Coroutine has already been scheduled, entering it again will ++ * cause us to enter it twice, potentially even after the coroutine has ++ * been deleted */ ++ if (scheduled) { ++ fprintf(stderr, ++ "%s: Co-routine was already scheduled in '%s'\n", ++ __func__, scheduled); ++ abort(); ++ } ++ ++ if (to->caller) { ++ fprintf(stderr, "Co-routine re-entered recursively\n"); ++ abort(); ++ } ++ ++ to->caller = from; ++ to->ctx = ctx; ++ ++ /* Store to->ctx before anything that stores to. Matches ++ * barrier in aio_co_wake and qemu_co_mutex_wake. ++ */ ++ smp_wmb(); ++ ++ ret = qemu_coroutine_switch(from, to, COROUTINE_ENTER); ++ ++ /* Queued coroutines are run depth-first; previously pending coroutines ++ * run after those queued more recently. ++ */ ++ QSIMPLEQ_PREPEND(&pending, &to->co_queue_wakeup); ++ ++ switch (ret) { ++ case COROUTINE_YIELD: ++ break; ++ case COROUTINE_TERMINATE: ++ assert(!to->locks_held); ++ trace_qemu_coroutine_terminate(to); ++ coroutine_delete(to); ++ break; ++ default: ++ abort(); ++ } ++ } ++} ++ ++void qemu_coroutine_enter(Coroutine *co) ++{ ++ qemu_aio_coroutine_enter(qemu_get_current_aio_context(), co); ++} ++ ++void qemu_coroutine_enter_if_inactive(Coroutine *co) ++{ ++ if (!qemu_coroutine_entered(co)) { ++ qemu_coroutine_enter(co); ++ } ++} ++ ++void coroutine_fn qemu_coroutine_yield(void) ++{ ++ Coroutine *self = qemu_coroutine_self(); ++ Coroutine *to = self->caller; ++ ++ trace_qemu_coroutine_yield(self, to); ++ ++ if (!to) { ++ fprintf(stderr, "Co-routine is yielding to no one\n"); ++ abort(); ++ } ++ ++ self->caller = NULL; ++ qemu_coroutine_switch(self, to, COROUTINE_YIELD); ++} ++ ++bool qemu_coroutine_entered(Coroutine *co) ++{ ++ return co->caller; ++} ++ ++AioContext *qemu_coroutine_get_aio_context(Coroutine *co) ++{ ++ return co->ctx; ++} ++ ++void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size) ++{ ++ QEMU_LOCK_GUARD(&global_pool_lock); ++ global_pool_max_size += additional_pool_size; ++} ++ ++void qemu_coroutine_dec_pool_size(unsigned int removing_pool_size) ++{ ++ QEMU_LOCK_GUARD(&global_pool_lock); ++ global_pool_max_size -= removing_pool_size; ++} ++ ++static unsigned int get_global_pool_hard_max_size(void) ++{ ++#ifdef __linux__ ++ g_autofree char *contents = NULL; ++ int max_map_count; ++ ++ /* ++ * Linux processes can have up to max_map_count virtual memory areas ++ * (VMAs). mmap(2), mprotect(2), etc fail with ENOMEM beyond this limit. We ++ * must limit the coroutine pool to a safe size to avoid running out of ++ * VMAs. ++ */ ++ if (g_file_get_contents("/proc/sys/vm/max_map_count", &contents, NULL, ++ NULL) && ++ qemu_strtoi(contents, NULL, 10, &max_map_count) == 0) { ++ /* ++ * This is an upper bound that avoids exceeding max_map_count. Leave a ++ * fixed amount for non-coroutine users like library dependencies, ++ * vhost-user, etc. Each coroutine takes up 2 VMAs so halve the ++ * remaining amount. ++ */ ++ if (max_map_count > 5000) { ++ return (max_map_count - 5000) / 2; ++ } else { ++ /* Disable the global pool but threads still have local pools */ ++ return 0; ++ } ++ } ++#endif ++ ++ return UINT_MAX; ++} ++ ++static void __attribute__((constructor)) qemu_coroutine_init(void) ++{ ++ qemu_mutex_init(&global_pool_lock); ++ global_pool_hard_max_size = get_global_pool_hard_max_size(); ++} +diff --git a/qcow2/lib/util/qemu-option.c b/qcow2/lib/util/qemu-option.c +new file mode 100644 +index 00000000..201f7a87 +--- /dev/null ++++ b/qcow2/lib/util/qemu-option.c +@@ -0,0 +1,1226 @@ ++/* ++ * Commandline option parsing functions ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * Copyright (c) 2009 Kevin Wolf ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "qapi/error.h" ++#include "qemu/error-report.h" ++#include "qapi/qmp/qbool.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qnum.h" ++#include "qapi/qmp/qstring.h" ++#include "qapi/qmp/qerror.h" ++#include "qemu/option_int.h" ++#include "qemu/cutils.h" ++#include "qemu/id.h" ++#include "qemu/help_option.h" ++ ++/* ++ * Extracts the name of an option from the parameter string (@p points at the ++ * first byte of the option name) ++ * ++ * The option name is @len characters long and is copied into @option. The ++ * caller is responsible for free'ing @option when no longer required. ++ * ++ * The return value is the position of the delimiter/zero byte after the option ++ * name in @p. ++ */ ++static const char *get_opt_name(const char *p, char **option, size_t len) ++{ ++ *option = g_strndup(p, len); ++ return p + len; ++} ++ ++/* ++ * Extracts the value of an option from the parameter string p (p points at the ++ * first byte of the option value) ++ * ++ * This function is comparable to get_opt_name with the difference that the ++ * delimiter is fixed to be comma which starts a new option. To specify an ++ * option value that contains commas, double each comma. ++ */ ++const char *get_opt_value(const char *p, char **value) ++{ ++ size_t capacity = 0, length; ++ const char *offset; ++ ++ *value = NULL; ++ while (1) { ++ offset = qemu_strchrnul(p, ','); ++ length = offset - p; ++ if (*offset != '\0' && *(offset + 1) == ',') { ++ length++; ++ } ++ *value = g_renew(char, *value, capacity + length + 1); ++ strncpy(*value + capacity, p, length); ++ (*value)[capacity + length] = '\0'; ++ capacity += length; ++ if (*offset == '\0' || ++ *(offset + 1) != ',') { ++ break; ++ } ++ ++ p += (offset - p) + 2; ++ } ++ ++ return offset; ++} ++ ++static bool parse_option_number(const char *name, const char *value, ++ uint64_t *ret, Error **errp) ++{ ++ uint64_t number; ++ int err; ++ ++ err = qemu_strtou64(value, NULL, 0, &number); ++ if (err == -ERANGE) { ++ error_setg(errp, "Value '%s' is too large for parameter '%s'", ++ value, name); ++ return false; ++ } ++ if (err) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number"); ++ return false; ++ } ++ *ret = number; ++ return true; ++} ++ ++static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc, ++ const char *name) ++{ ++ int i; ++ ++ for (i = 0; desc[i].name != NULL; i++) { ++ if (strcmp(desc[i].name, name) == 0) { ++ return &desc[i]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static const char *find_default_by_name(QemuOpts *opts, const char *name) ++{ ++ const QemuOptDesc *desc = find_desc_by_name(opts->list->desc, name); ++ ++ return desc ? desc->def_value_str : NULL; ++} ++ ++bool parse_option_size(const char *name, const char *value, ++ uint64_t *ret, Error **errp) ++{ ++ uint64_t size; ++ int err; ++ ++ err = qemu_strtosz(value, NULL, &size); ++ if (err == -ERANGE) { ++ error_setg(errp, "Value '%s' is out of range for parameter '%s'", ++ value, name); ++ return false; ++ } ++ if (err) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, ++ "a non-negative number below 2^64"); ++ error_append_hint(errp, "Optional suffix k, M, G, T, P or E means" ++ " kilo-, mega-, giga-, tera-, peta-\n" ++ "and exabytes, respectively.\n"); ++ return false; ++ } ++ *ret = size; ++ return true; ++} ++ ++static const char *opt_type_to_string(enum QemuOptType type) ++{ ++ switch (type) { ++ case QEMU_OPT_STRING: ++ return "str"; ++ case QEMU_OPT_BOOL: ++ return "bool (on/off)"; ++ case QEMU_OPT_NUMBER: ++ return "num"; ++ case QEMU_OPT_SIZE: ++ return "size"; ++ } ++ ++ g_assert_not_reached(); ++} ++ ++/** ++ * Print the list of options available in the given list. If ++ * @print_caption is true, a caption (including the list name, if it ++ * exists) is printed. The options itself will be indented, so ++ * @print_caption should only be set to false if the caller prints its ++ * own custom caption (so that the indentation makes sense). ++ */ ++void qemu_opts_print_help(QemuOptsList *list, bool print_caption) ++{ ++ QemuOptDesc *desc; ++ int i; ++ GPtrArray *array = g_ptr_array_new(); ++ ++ assert(list); ++ desc = list->desc; ++ while (desc && desc->name) { ++ GString *str = g_string_new(NULL); ++ g_string_append_printf(str, " %s=<%s>", desc->name, ++ opt_type_to_string(desc->type)); ++ if (desc->help) { ++ if (str->len < 24) { ++ g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); ++ } ++ g_string_append_printf(str, " - %s", desc->help); ++ } ++ g_ptr_array_add(array, g_string_free(str, false)); ++ desc++; ++ } ++ ++ g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); ++ if (print_caption && array->len > 0) { ++ if (list->name) { ++ printf("%s options:\n", list->name); ++ } else { ++ printf("Options:\n"); ++ } ++ } else if (array->len == 0) { ++ if (list->name) { ++ printf("There are no options for %s.\n", list->name); ++ } else { ++ printf("No options available.\n"); ++ } ++ } ++ for (i = 0; i < array->len; i++) { ++ printf("%s\n", (char *)array->pdata[i]); ++ } ++ g_ptr_array_set_free_func(array, g_free); ++ g_ptr_array_free(array, true); ++ ++} ++/* ------------------------------------------------------------------ */ ++ ++QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt; ++ ++ QTAILQ_FOREACH_REVERSE(opt, &opts->head, next) { ++ if (strcmp(opt->name, name) != 0) ++ continue; ++ return opt; ++ } ++ return NULL; ++} ++ ++static void qemu_opt_del(QemuOpt *opt) ++{ ++ QTAILQ_REMOVE(&opt->opts->head, opt, next); ++ g_free(opt->name); ++ g_free(opt->str); ++ g_free(opt); ++} ++ ++/* qemu_opt_set allows many settings for the same option. ++ * This function deletes all settings for an option. ++ */ ++static void qemu_opt_del_all(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt, *next_opt; ++ ++ QTAILQ_FOREACH_SAFE(opt, &opts->head, next, next_opt) { ++ if (!strcmp(opt->name, name)) { ++ qemu_opt_del(opt); ++ } ++ } ++} ++ ++const char *qemu_opt_get(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt; ++ ++ if (opts == NULL) { ++ return NULL; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (!opt) { ++ return find_default_by_name(opts, name); ++ } ++ ++ return opt->str; ++} ++ ++void qemu_opt_iter_init(QemuOptsIter *iter, QemuOpts *opts, const char *name) ++{ ++ iter->opts = opts; ++ iter->opt = QTAILQ_FIRST(&opts->head); ++ iter->name = name; ++} ++ ++const char *qemu_opt_iter_next(QemuOptsIter *iter) ++{ ++ QemuOpt *ret = iter->opt; ++ if (iter->name) { ++ while (ret && !g_str_equal(iter->name, ret->name)) { ++ ret = QTAILQ_NEXT(ret, next); ++ } ++ } ++ iter->opt = ret ? QTAILQ_NEXT(ret, next) : NULL; ++ return ret ? ret->str : NULL; ++} ++ ++/* Get a known option (or its default) and remove it from the list ++ * all in one action. Return a malloced string of the option value. ++ * Result must be freed by caller with g_free(). ++ */ ++char *qemu_opt_get_del(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt; ++ char *str; ++ ++ if (opts == NULL) { ++ return NULL; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (!opt) { ++ return g_strdup(find_default_by_name(opts, name)); ++ } ++ str = opt->str; ++ opt->str = NULL; ++ qemu_opt_del_all(opts, name); ++ return str; ++} ++ ++bool qemu_opt_has_help_opt(QemuOpts *opts) ++{ ++ QemuOpt *opt; ++ ++ QTAILQ_FOREACH_REVERSE(opt, &opts->head, next) { ++ if (is_help_option(opt->name)) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++static bool qemu_opt_get_bool_helper(QemuOpts *opts, const char *name, ++ bool defval, bool del) ++{ ++ QemuOpt *opt; ++ const char *def_val; ++ bool ret = defval; ++ ++ if (opts == NULL) { ++ return ret; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (opt == NULL) { ++ def_val = find_default_by_name(opts, name); ++ if (def_val) { ++ qapi_bool_parse(name, def_val, &ret, &error_abort); ++ } ++ return ret; ++ } ++ assert(opt->desc && opt->desc->type == QEMU_OPT_BOOL); ++ ret = opt->value.boolean; ++ if (del) { ++ qemu_opt_del_all(opts, name); ++ } ++ return ret; ++} ++ ++bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval) ++{ ++ return qemu_opt_get_bool_helper(opts, name, defval, false); ++} ++ ++bool qemu_opt_get_bool_del(QemuOpts *opts, const char *name, bool defval) ++{ ++ return qemu_opt_get_bool_helper(opts, name, defval, true); ++} ++ ++static uint64_t qemu_opt_get_number_helper(QemuOpts *opts, const char *name, ++ uint64_t defval, bool del) ++{ ++ QemuOpt *opt; ++ const char *def_val; ++ uint64_t ret = defval; ++ ++ if (opts == NULL) { ++ return ret; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (opt == NULL) { ++ def_val = find_default_by_name(opts, name); ++ if (def_val) { ++ parse_option_number(name, def_val, &ret, &error_abort); ++ } ++ return ret; ++ } ++ assert(opt->desc && opt->desc->type == QEMU_OPT_NUMBER); ++ ret = opt->value.uint; ++ if (del) { ++ qemu_opt_del_all(opts, name); ++ } ++ return ret; ++} ++ ++uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval) ++{ ++ return qemu_opt_get_number_helper(opts, name, defval, false); ++} ++ ++uint64_t qemu_opt_get_number_del(QemuOpts *opts, const char *name, ++ uint64_t defval) ++{ ++ return qemu_opt_get_number_helper(opts, name, defval, true); ++} ++ ++static uint64_t qemu_opt_get_size_helper(QemuOpts *opts, const char *name, ++ uint64_t defval, bool del) ++{ ++ QemuOpt *opt; ++ const char *def_val; ++ uint64_t ret = defval; ++ ++ if (opts == NULL) { ++ return ret; ++ } ++ ++ opt = qemu_opt_find(opts, name); ++ if (opt == NULL) { ++ def_val = find_default_by_name(opts, name); ++ if (def_val) { ++ parse_option_size(name, def_val, &ret, &error_abort); ++ } ++ return ret; ++ } ++ assert(opt->desc && opt->desc->type == QEMU_OPT_SIZE); ++ ret = opt->value.uint; ++ if (del) { ++ qemu_opt_del_all(opts, name); ++ } ++ return ret; ++} ++ ++uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval) ++{ ++ return qemu_opt_get_size_helper(opts, name, defval, false); ++} ++ ++uint64_t qemu_opt_get_size_del(QemuOpts *opts, const char *name, ++ uint64_t defval) ++{ ++ return qemu_opt_get_size_helper(opts, name, defval, true); ++} ++ ++static bool qemu_opt_parse(QemuOpt *opt, Error **errp) ++{ ++ if (opt->desc == NULL) ++ return true; ++ ++ switch (opt->desc->type) { ++ case QEMU_OPT_STRING: ++ /* nothing */ ++ return true; ++ case QEMU_OPT_BOOL: ++ return qapi_bool_parse(opt->name, opt->str, &opt->value.boolean, errp); ++ case QEMU_OPT_NUMBER: ++ return parse_option_number(opt->name, opt->str, &opt->value.uint, ++ errp); ++ case QEMU_OPT_SIZE: ++ return parse_option_size(opt->name, opt->str, &opt->value.uint, ++ errp); ++ default: ++ abort(); ++ } ++} ++ ++static bool opts_accepts_any(const QemuOptsList *list) ++{ ++ return list->desc[0].name == NULL; ++} ++ ++int qemu_opt_unset(QemuOpts *opts, const char *name) ++{ ++ QemuOpt *opt = qemu_opt_find(opts, name); ++ ++ assert(opts_accepts_any(opts->list)); ++ ++ if (opt == NULL) { ++ return -1; ++ } else { ++ qemu_opt_del(opt); ++ return 0; ++ } ++} ++ ++static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value) ++{ ++ QemuOpt *opt = g_malloc0(sizeof(*opt)); ++ ++ opt->name = g_strdup(name); ++ opt->str = value; ++ opt->opts = opts; ++ QTAILQ_INSERT_TAIL(&opts->head, opt, next); ++ ++ return opt; ++} ++ ++static bool opt_validate(QemuOpt *opt, Error **errp) ++{ ++ const QemuOptDesc *desc; ++ const QemuOptsList *list = opt->opts->list; ++ ++ desc = find_desc_by_name(list->desc, opt->name); ++ if (!desc && !opts_accepts_any(list)) { ++ error_setg(errp, "Invalid parameter '%s'", opt->name); ++ return false; ++ } ++ ++ opt->desc = desc; ++ if (!qemu_opt_parse(opt, errp)) { ++ return false; ++ } ++ ++ return true; ++} ++ ++bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value, ++ Error **errp) ++{ ++ QemuOpt *opt = opt_create(opts, name, g_strdup(value)); ++ ++ if (!opt_validate(opt, errp)) { ++ qemu_opt_del(opt); ++ return false; ++ } ++ return true; ++} ++ ++bool qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, ++ Error **errp) ++{ ++ QemuOpt *opt; ++ const QemuOptDesc *desc; ++ const QemuOptsList *list = opts->list; ++ ++ desc = find_desc_by_name(list->desc, name); ++ if (!desc && !opts_accepts_any(list)) { ++ error_setg(errp, "Invalid parameter '%s'", name); ++ return false; ++ } ++ ++ opt = g_malloc0(sizeof(*opt)); ++ opt->name = g_strdup(name); ++ opt->opts = opts; ++ opt->desc = desc; ++ opt->value.boolean = !!val; ++ opt->str = g_strdup(val ? "on" : "off"); ++ QTAILQ_INSERT_TAIL(&opts->head, opt, next); ++ return true; ++} ++ ++bool qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val, ++ Error **errp) ++{ ++ QemuOpt *opt; ++ const QemuOptDesc *desc; ++ const QemuOptsList *list = opts->list; ++ ++ desc = find_desc_by_name(list->desc, name); ++ if (!desc && !opts_accepts_any(list)) { ++ error_setg(errp, "Invalid parameter '%s'", name); ++ return false; ++ } ++ ++ opt = g_malloc0(sizeof(*opt)); ++ opt->name = g_strdup(name); ++ opt->opts = opts; ++ opt->desc = desc; ++ opt->value.uint = val; ++ opt->str = g_strdup_printf("%" PRId64, val); ++ QTAILQ_INSERT_TAIL(&opts->head, opt, next); ++ return true; ++} ++ ++/** ++ * For each member of @opts, call @func(@opaque, name, value, @errp). ++ * @func() may store an Error through @errp, but must return non-zero then. ++ * When @func() returns non-zero, break the loop and return that value. ++ * Return zero when the loop completes. ++ */ ++int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, ++ Error **errp) ++{ ++ QemuOpt *opt; ++ int rc; ++ ++ QTAILQ_FOREACH(opt, &opts->head, next) { ++ rc = func(opaque, opt->name, opt->str, errp); ++ if (rc) { ++ return rc; ++ } ++ assert(!errp || !*errp); ++ } ++ return 0; ++} ++ ++QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id) ++{ ++ QemuOpts *opts; ++ ++ QTAILQ_FOREACH(opts, &list->head, next) { ++ if (!opts->id && !id) { ++ return opts; ++ } ++ if (opts->id && id && !strcmp(opts->id, id)) { ++ return opts; ++ } ++ } ++ return NULL; ++} ++ ++QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, ++ int fail_if_exists, Error **errp) ++{ ++ QemuOpts *opts = NULL; ++ ++ if (list->merge_lists) { ++ if (id) { ++ error_setg(errp, "Invalid parameter 'id'"); ++ return NULL; ++ } ++ opts = qemu_opts_find(list, NULL); ++ if (opts) { ++ return opts; ++ } ++ } else if (id) { ++ assert(fail_if_exists); ++ if (!id_wellformed(id)) { ++ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id", ++ "an identifier"); ++ error_append_hint(errp, "Identifiers consist of letters, digits, " ++ "'-', '.', '_', starting with a letter.\n"); ++ return NULL; ++ } ++ opts = qemu_opts_find(list, id); ++ if (opts != NULL) { ++ error_setg(errp, "Duplicate ID '%s' for %s", id, list->name); ++ return NULL; ++ } ++ } ++ opts = g_malloc0(sizeof(*opts)); ++ opts->id = g_strdup(id); ++ opts->list = list; ++ loc_save(&opts->loc); ++ QTAILQ_INIT(&opts->head); ++ QTAILQ_INSERT_TAIL(&list->head, opts, next); ++ return opts; ++} ++ ++void qemu_opts_reset(QemuOptsList *list) ++{ ++ QemuOpts *opts, *next_opts; ++ ++ QTAILQ_FOREACH_SAFE(opts, &list->head, next, next_opts) { ++ qemu_opts_del(opts); ++ } ++} ++ ++void qemu_opts_loc_restore(QemuOpts *opts) ++{ ++ loc_restore(&opts->loc); ++} ++ ++const char *qemu_opts_id(QemuOpts *opts) ++{ ++ return opts->id; ++} ++ ++/* The id string will be g_free()d by qemu_opts_del */ ++void qemu_opts_set_id(QemuOpts *opts, char *id) ++{ ++ opts->id = id; ++} ++ ++void qemu_opts_del(QemuOpts *opts) ++{ ++ QemuOpt *opt; ++ ++ if (opts == NULL) { ++ return; ++ } ++ ++ for (;;) { ++ opt = QTAILQ_FIRST(&opts->head); ++ if (opt == NULL) ++ break; ++ qemu_opt_del(opt); ++ } ++ QTAILQ_REMOVE(&opts->list->head, opts, next); ++ g_free(opts->id); ++ g_free(opts); ++} ++ ++/* print value, escaping any commas in value */ ++static void escaped_print(const char *value) ++{ ++ const char *ptr; ++ ++ for (ptr = value; *ptr; ++ptr) { ++ if (*ptr == ',') { ++ putchar(','); ++ } ++ putchar(*ptr); ++ } ++} ++ ++void qemu_opts_print(QemuOpts *opts, const char *separator) ++{ ++ QemuOpt *opt; ++ QemuOptDesc *desc = opts->list->desc; ++ const char *sep = ""; ++ ++ if (opts->id) { ++ printf("id=%s", opts->id); /* passed id_wellformed -> no commas */ ++ sep = separator; ++ } ++ ++ if (desc[0].name == NULL) { ++ QTAILQ_FOREACH(opt, &opts->head, next) { ++ printf("%s%s=", sep, opt->name); ++ escaped_print(opt->str); ++ sep = separator; ++ } ++ return; ++ } ++ for (; desc && desc->name; desc++) { ++ const char *value; ++ opt = qemu_opt_find(opts, desc->name); ++ ++ value = opt ? opt->str : desc->def_value_str; ++ if (!value) { ++ continue; ++ } ++ if (desc->type == QEMU_OPT_STRING) { ++ printf("%s%s=", sep, desc->name); ++ escaped_print(value); ++ } else if ((desc->type == QEMU_OPT_SIZE || ++ desc->type == QEMU_OPT_NUMBER) && opt) { ++ printf("%s%s=%" PRId64, sep, desc->name, opt->value.uint); ++ } else { ++ printf("%s%s=%s", sep, desc->name, value); ++ } ++ sep = separator; ++ } ++} ++ ++static const char *get_opt_name_value(const char *params, ++ const char *firstname, ++ bool warn_on_flag, ++ bool *help_wanted, ++ char **name, char **value) ++{ ++ const char *p; ++ const char *prefix = ""; ++ size_t len; ++ bool is_help = false; ++ ++ len = strcspn(params, "=,"); ++ if (params[len] != '=') { ++ /* found "foo,more" */ ++ if (firstname) { ++ /* implicitly named first option */ ++ *name = g_strdup(firstname); ++ p = get_opt_value(params, value); ++ } else { ++ /* option without value, must be a flag */ ++ p = get_opt_name(params, name, len); ++ if (strncmp(*name, "no", 2) == 0) { ++ memmove(*name, *name + 2, strlen(*name + 2) + 1); ++ *value = g_strdup("off"); ++ prefix = "no"; ++ } else { ++ *value = g_strdup("on"); ++ is_help = is_help_option(*name); ++ } ++ if (!is_help && warn_on_flag) { ++ warn_report("short-form boolean option '%s%s' deprecated", prefix, *name); ++ if (g_str_equal(*name, "delay")) { ++ error_printf("Please use nodelay=%s instead\n", prefix[0] ? "on" : "off"); ++ } else { ++ error_printf("Please use %s=%s instead\n", *name, *value); ++ } ++ } ++ } ++ } else { ++ /* found "foo=bar,more" */ ++ p = get_opt_name(params, name, len); ++ assert(*p == '='); ++ p++; ++ p = get_opt_value(p, value); ++ } ++ ++ assert(!*p || *p == ','); ++ if (help_wanted && is_help) { ++ *help_wanted = true; ++ } ++ if (*p == ',') { ++ p++; ++ } ++ return p; ++} ++ ++static bool opts_do_parse(QemuOpts *opts, const char *params, ++ const char *firstname, ++ bool warn_on_flag, bool *help_wanted, Error **errp) ++{ ++ char *option, *value; ++ const char *p; ++ QemuOpt *opt; ++ ++ for (p = params; *p;) { ++ p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value); ++ if (help_wanted && *help_wanted) { ++ g_free(option); ++ g_free(value); ++ return false; ++ } ++ firstname = NULL; ++ ++ if (!strcmp(option, "id")) { ++ g_free(option); ++ g_free(value); ++ continue; ++ } ++ ++ opt = opt_create(opts, option, value); ++ g_free(option); ++ if (!opt_validate(opt, errp)) { ++ qemu_opt_del(opt); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static char *opts_parse_id(const char *params) ++{ ++ const char *p; ++ char *name, *value; ++ ++ for (p = params; *p;) { ++ p = get_opt_name_value(p, NULL, false, NULL, &name, &value); ++ if (!strcmp(name, "id")) { ++ g_free(name); ++ return value; ++ } ++ g_free(name); ++ g_free(value); ++ } ++ ++ return NULL; ++} ++ ++bool has_help_option(const char *params) ++{ ++ const char *p; ++ char *name, *value; ++ bool ret = false; ++ ++ for (p = params; *p;) { ++ p = get_opt_name_value(p, NULL, false, &ret, &name, &value); ++ g_free(name); ++ g_free(value); ++ if (ret) { ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/** ++ * Store options parsed from @params into @opts. ++ * If @firstname is non-null, the first key=value in @params may omit ++ * key=, and is treated as if key was @firstname. ++ * On error, store an error object through @errp if non-null. ++ */ ++bool qemu_opts_do_parse(QemuOpts *opts, const char *params, ++ const char *firstname, Error **errp) ++{ ++ return opts_do_parse(opts, params, firstname, false, NULL, errp); ++} ++ ++static QemuOpts *opts_parse(QemuOptsList *list, const char *params, ++ bool permit_abbrev, ++ bool warn_on_flag, bool *help_wanted, Error **errp) ++{ ++ const char *firstname; ++ char *id = opts_parse_id(params); ++ QemuOpts *opts; ++ ++ assert(!permit_abbrev || list->implied_opt_name); ++ firstname = permit_abbrev ? list->implied_opt_name : NULL; ++ ++ opts = qemu_opts_create(list, id, !list->merge_lists, errp); ++ g_free(id); ++ if (opts == NULL) { ++ return NULL; ++ } ++ ++ if (!opts_do_parse(opts, params, firstname, ++ warn_on_flag, help_wanted, errp)) { ++ qemu_opts_del(opts); ++ return NULL; ++ } ++ ++ return opts; ++} ++ ++/** ++ * Create a QemuOpts in @list and with options parsed from @params. ++ * If @permit_abbrev, the first key=value in @params may omit key=, ++ * and is treated as if key was @list->implied_opt_name. ++ * On error, store an error object through @errp if non-null. ++ * Return the new QemuOpts on success, null pointer on error. ++ */ ++QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, ++ bool permit_abbrev, Error **errp) ++{ ++ return opts_parse(list, params, permit_abbrev, false, NULL, errp); ++} ++ ++/** ++ * Create a QemuOpts in @list and with options parsed from @params. ++ * If @permit_abbrev, the first key=value in @params may omit key=, ++ * and is treated as if key was @list->implied_opt_name. ++ * Report errors with error_report_err(). This is inappropriate in ++ * QMP context. Do not use this function there! ++ * Return the new QemuOpts on success, null pointer on error. ++ */ ++QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params, ++ bool permit_abbrev) ++{ ++ Error *err = NULL; ++ QemuOpts *opts; ++ bool help_wanted = false; ++ ++ opts = opts_parse(list, params, permit_abbrev, true, ++ opts_accepts_any(list) ? NULL : &help_wanted, ++ &err); ++ if (!opts) { ++ assert(!!err + !!help_wanted == 1); ++ if (help_wanted) { ++ qemu_opts_print_help(list, true); ++ } else { ++ error_report_err(err); ++ } ++ } ++ return opts; ++} ++ ++static bool qemu_opts_from_qdict_entry(QemuOpts *opts, ++ const QDictEntry *entry, ++ Error **errp) ++{ ++ const char *key = qdict_entry_key(entry); ++ QObject *obj = qdict_entry_value(entry); ++ char buf[32]; ++ g_autofree char *tmp = NULL; ++ const char *value; ++ ++ if (!strcmp(key, "id")) { ++ return true; ++ } ++ ++ switch (qobject_type(obj)) { ++ case QTYPE_QSTRING: ++ value = qstring_get_str(qobject_to(QString, obj)); ++ break; ++ case QTYPE_QNUM: ++ tmp = qnum_to_string(qobject_to(QNum, obj)); ++ value = tmp; ++ break; ++ case QTYPE_QBOOL: ++ pstrcpy(buf, sizeof(buf), ++ qbool_get_bool(qobject_to(QBool, obj)) ? "on" : "off"); ++ value = buf; ++ break; ++ default: ++ return true; ++ } ++ ++ return qemu_opt_set(opts, key, value, errp); ++} ++ ++/* ++ * Create QemuOpts from a QDict. ++ * Use value of key "id" as ID if it exists and is a QString. Only ++ * QStrings, QNums and QBools are copied. Entries with other types ++ * are silently ignored. ++ */ ++QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict, ++ Error **errp) ++{ ++ QemuOpts *opts; ++ const QDictEntry *entry; ++ ++ opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1, errp); ++ if (!opts) { ++ return NULL; ++ } ++ ++ for (entry = qdict_first(qdict); ++ entry; ++ entry = qdict_next(qdict, entry)) { ++ if (!qemu_opts_from_qdict_entry(opts, entry, errp)) { ++ qemu_opts_del(opts); ++ return NULL; ++ } ++ } ++ ++ return opts; ++} ++ ++/* ++ * Adds all QDict entries to the QemuOpts that can be added and removes them ++ * from the QDict. When this function returns, the QDict contains only those ++ * entries that couldn't be added to the QemuOpts. ++ */ ++bool qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp) ++{ ++ const QDictEntry *entry, *next; ++ ++ entry = qdict_first(qdict); ++ ++ while (entry != NULL) { ++ next = qdict_next(qdict, entry); ++ ++ if (opts_accepts_any(opts->list) || ++ find_desc_by_name(opts->list->desc, entry->key)) { ++ if (!qemu_opts_from_qdict_entry(opts, entry, errp)) { ++ return false; ++ } ++ qdict_del(qdict, entry->key); ++ } ++ ++ entry = next; ++ } ++ ++ return true; ++} ++ ++/* ++ * Convert from QemuOpts to QDict. The QDict values are of type QString. ++ * ++ * If @list is given, only add those options to the QDict that are contained in ++ * the list. If @del is true, any options added to the QDict are removed from ++ * the QemuOpts, otherwise they remain there. ++ * ++ * If two options in @opts have the same name, they are processed in order ++ * so that the last one wins (consistent with the reverse iteration in ++ * qemu_opt_find()), but all of them are deleted if @del is true. ++ * ++ * TODO We'll want to use types appropriate for opt->desc->type, but ++ * this is enough for now. ++ */ ++QDict *qemu_opts_to_qdict_filtered(QemuOpts *opts, QDict *qdict, ++ QemuOptsList *list, bool del) ++{ ++ QemuOpt *opt, *next; ++ ++ if (!qdict) { ++ qdict = qdict_new(); ++ } ++ if (opts->id) { ++ qdict_put_str(qdict, "id", opts->id); ++ } ++ QTAILQ_FOREACH_SAFE(opt, &opts->head, next, next) { ++ if (list) { ++ QemuOptDesc *desc; ++ bool found = false; ++ for (desc = list->desc; desc->name; desc++) { ++ if (!strcmp(desc->name, opt->name)) { ++ found = true; ++ break; ++ } ++ } ++ if (!found) { ++ continue; ++ } ++ } ++ qdict_put_str(qdict, opt->name, opt->str); ++ if (del) { ++ qemu_opt_del(opt); ++ } ++ } ++ return qdict; ++} ++ ++/* Copy all options in a QemuOpts to the given QDict. See ++ * qemu_opts_to_qdict_filtered() for details. */ ++QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict) ++{ ++ return qemu_opts_to_qdict_filtered(opts, qdict, NULL, false); ++} ++ ++/* Validate parsed opts against descriptions where no ++ * descriptions were provided in the QemuOptsList. ++ */ ++bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp) ++{ ++ QemuOpt *opt; ++ ++ assert(opts_accepts_any(opts->list)); ++ ++ QTAILQ_FOREACH(opt, &opts->head, next) { ++ opt->desc = find_desc_by_name(desc, opt->name); ++ if (!opt->desc) { ++ error_setg(errp, "Invalid parameter '%s'", opt->name); ++ return false; ++ } ++ ++ if (!qemu_opt_parse(opt, errp)) { ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/** ++ * For each member of @list, call @func(@opaque, member, @errp). ++ * Call it with the current location temporarily set to the member's. ++ * @func() may store an Error through @errp, but must return non-zero then. ++ * When @func() returns non-zero, break the loop and return that value. ++ * Return zero when the loop completes. ++ */ ++int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, ++ void *opaque, Error **errp) ++{ ++ Location loc; ++ QemuOpts *opts, *next; ++ int rc = 0; ++ ++ loc_push_none(&loc); ++ QTAILQ_FOREACH_SAFE(opts, &list->head, next, next) { ++ loc_restore(&opts->loc); ++ rc = func(opaque, opts, errp); ++ if (rc) { ++ break; ++ } ++ assert(!errp || !*errp); ++ } ++ loc_pop(&loc); ++ return rc; ++} ++ ++static size_t count_opts_list(QemuOptsList *list) ++{ ++ QemuOptDesc *desc = NULL; ++ size_t num_opts = 0; ++ ++ if (!list) { ++ return 0; ++ } ++ ++ desc = list->desc; ++ while (desc && desc->name) { ++ num_opts++; ++ desc++; ++ } ++ ++ return num_opts; ++} ++ ++void qemu_opts_free(QemuOptsList *list) ++{ ++ g_free(list); ++} ++ ++/* Realloc dst option list and append options from an option list (list) ++ * to it. dst could be NULL or a malloced list. ++ * The lifetime of dst must be shorter than the input list because the ++ * QemuOptDesc->name, ->help, and ->def_value_str strings are shared. ++ */ ++QemuOptsList *qemu_opts_append(QemuOptsList *dst, ++ QemuOptsList *list) ++{ ++ size_t num_opts, num_dst_opts; ++ QemuOptDesc *desc; ++ bool need_init = false; ++ bool need_head_update; ++ ++ if (!list) { ++ return dst; ++ } ++ ++ /* If dst is NULL, after realloc, some area of dst should be initialized ++ * before adding options to it. ++ */ ++ if (!dst) { ++ need_init = true; ++ need_head_update = true; ++ } else { ++ /* Moreover, even if dst is not NULL, the realloc may move it to a ++ * different address in which case we may get a stale tail pointer ++ * in dst->head. */ ++ need_head_update = QTAILQ_EMPTY(&dst->head); ++ } ++ ++ num_opts = count_opts_list(dst); ++ num_dst_opts = num_opts; ++ num_opts += count_opts_list(list); ++ dst = g_realloc(dst, sizeof(QemuOptsList) + ++ (num_opts + 1) * sizeof(QemuOptDesc)); ++ if (need_init) { ++ dst->name = NULL; ++ dst->implied_opt_name = NULL; ++ dst->merge_lists = false; ++ } ++ if (need_head_update) { ++ QTAILQ_INIT(&dst->head); ++ } ++ dst->desc[num_dst_opts].name = NULL; ++ ++ /* append list->desc to dst->desc */ ++ if (list) { ++ desc = list->desc; ++ while (desc && desc->name) { ++ if (find_desc_by_name(dst->desc, desc->name) == NULL) { ++ dst->desc[num_dst_opts++] = *desc; ++ dst->desc[num_dst_opts].name = NULL; ++ } ++ desc++; ++ } ++ } ++ ++ return dst; ++} +diff --git a/qcow2/lib/util/qemu-print.c b/qcow2/lib/util/qemu-print.c +new file mode 100644 +index 00000000..69ba612f +--- /dev/null ++++ b/qcow2/lib/util/qemu-print.c +@@ -0,0 +1,70 @@ ++/* ++ * Print to stream or current monitor ++ * ++ * Copyright (C) 2019 Red Hat Inc. ++ * ++ * Authors: ++ * Markus Armbruster , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "monitor/monitor.h" ++#include "qemu/qemu-print.h" ++ ++/* ++ * Print like vprintf(). ++ * Print to current monitor if we have one, else to stdout. ++ */ ++int qemu_vprintf(const char *fmt, va_list ap) ++{ ++ Monitor *cur_mon = monitor_cur(); ++ if (cur_mon) { ++ return monitor_vprintf(cur_mon, fmt, ap); ++ } ++ return vprintf(fmt, ap); ++} ++ ++/* ++ * Print like printf(). ++ * Print to current monitor if we have one, else to stdout. ++ */ ++int qemu_printf(const char *fmt, ...) ++{ ++ va_list ap; ++ int ret; ++ ++ va_start(ap, fmt); ++ ret = qemu_vprintf(fmt, ap); ++ va_end(ap); ++ return ret; ++} ++ ++/* ++ * Print like vfprintf() ++ * Print to @stream if non-null, else to current monitor. ++ */ ++int qemu_vfprintf(FILE *stream, const char *fmt, va_list ap) ++{ ++ if (!stream) { ++ return monitor_vprintf(monitor_cur(), fmt, ap); ++ } ++ return vfprintf(stream, fmt, ap); ++} ++ ++/* ++ * Print like fprintf(). ++ * Print to @stream if non-null, else to current monitor. ++ */ ++int qemu_fprintf(FILE *stream, const char *fmt, ...) ++{ ++ va_list ap; ++ int ret; ++ ++ va_start(ap, fmt); ++ ret = qemu_vfprintf(stream, fmt, ap); ++ va_end(ap); ++ return ret; ++} +diff --git a/qcow2/lib/util/qemu-sockets.c b/qcow2/lib/util/qemu-sockets.c +new file mode 100644 +index 00000000..60c44b2b +--- /dev/null ++++ b/qcow2/lib/util/qemu-sockets.c +@@ -0,0 +1,1475 @@ ++/* ++ * inet and unix socket functions for qemu ++ * ++ * (c) 2008 Gerd Hoffmann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; under version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++#include "qemu/osdep.h" ++ ++#ifdef CONFIG_AF_VSOCK ++#include ++#endif /* CONFIG_AF_VSOCK */ ++ ++#include "monitor/monitor.h" ++#include "qapi/clone-visitor.h" ++#include "qapi/error.h" ++#include "qapi/qapi-visit-sockets.h" ++#include "qemu/sockets.h" ++#include "qemu/main-loop.h" ++#include "qapi/qobject-input-visitor.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qemu/cutils.h" ++#include "trace.h" ++ ++#ifndef AI_ADDRCONFIG ++# define AI_ADDRCONFIG 0 ++#endif ++ ++#ifndef AI_V4MAPPED ++# define AI_V4MAPPED 0 ++#endif ++ ++#ifndef AI_NUMERICSERV ++# define AI_NUMERICSERV 0 ++#endif ++ ++ ++static int inet_getport(struct addrinfo *e) ++{ ++ struct sockaddr_in *i4; ++ struct sockaddr_in6 *i6; ++ ++ switch (e->ai_family) { ++ case PF_INET6: ++ i6 = (void*)e->ai_addr; ++ return ntohs(i6->sin6_port); ++ case PF_INET: ++ i4 = (void*)e->ai_addr; ++ return ntohs(i4->sin_port); ++ default: ++ return 0; ++ } ++} ++ ++static void inet_setport(struct addrinfo *e, int port) ++{ ++ struct sockaddr_in *i4; ++ struct sockaddr_in6 *i6; ++ ++ switch (e->ai_family) { ++ case PF_INET6: ++ i6 = (void*)e->ai_addr; ++ i6->sin6_port = htons(port); ++ break; ++ case PF_INET: ++ i4 = (void*)e->ai_addr; ++ i4->sin_port = htons(port); ++ break; ++ } ++} ++ ++NetworkAddressFamily inet_netfamily(int family) ++{ ++ switch (family) { ++ case PF_INET6: return NETWORK_ADDRESS_FAMILY_IPV6; ++ case PF_INET: return NETWORK_ADDRESS_FAMILY_IPV4; ++ case PF_UNIX: return NETWORK_ADDRESS_FAMILY_UNIX; ++#ifdef CONFIG_AF_VSOCK ++ case PF_VSOCK: return NETWORK_ADDRESS_FAMILY_VSOCK; ++#endif /* CONFIG_AF_VSOCK */ ++ } ++ return NETWORK_ADDRESS_FAMILY_UNKNOWN; ++} ++ ++bool fd_is_socket(int fd) ++{ ++ int optval; ++ socklen_t optlen = sizeof(optval); ++ return !getsockopt(fd, SOL_SOCKET, SO_TYPE, &optval, &optlen); ++} ++ ++ ++/* ++ * Matrix we're trying to apply ++ * ++ * ipv4 ipv6 family ++ * - - PF_UNSPEC ++ * - f PF_INET ++ * - t PF_INET6 ++ * f - PF_INET6 ++ * f f ++ * f t PF_INET6 ++ * t - PF_INET ++ * t f PF_INET ++ * t t PF_INET6/PF_UNSPEC ++ * ++ * NB, this matrix is only about getting the necessary results ++ * from getaddrinfo(). Some of the cases require further work ++ * after reading results from getaddrinfo in order to fully ++ * apply the logic the end user wants. ++ * ++ * In the first and last cases, we must set IPV6_V6ONLY=0 ++ * when binding, to allow a single listener to potentially ++ * accept both IPv4+6 addresses. ++ */ ++int inet_ai_family_from_address(InetSocketAddress *addr, ++ Error **errp) ++{ ++ if (addr->has_ipv6 && addr->has_ipv4 && ++ !addr->ipv6 && !addr->ipv4) { ++ error_setg(errp, "Cannot disable IPv4 and IPv6 at same time"); ++ return PF_UNSPEC; ++ } ++ if ((addr->has_ipv6 && addr->ipv6) && (addr->has_ipv4 && addr->ipv4)) { ++ /* ++ * Some backends can only do a single listener. In that case ++ * we want empty hostname to resolve to "::" and then use the ++ * flag IPV6_V6ONLY==0 to get both protocols on 1 socket. This ++ * doesn't work for addresses other than "", so they're just ++ * inevitably broken until multiple listeners can be used, ++ * and thus we honour getaddrinfo automatic protocol detection ++ * Once all backends do multi-listener, remove the PF_INET6 ++ * branch entirely. ++ */ ++ if (!addr->host || g_str_equal(addr->host, "")) { ++ return PF_INET6; ++ } else { ++ return PF_UNSPEC; ++ } ++ } ++ if ((addr->has_ipv6 && addr->ipv6) || (addr->has_ipv4 && !addr->ipv4)) { ++ return PF_INET6; ++ } ++ if ((addr->has_ipv4 && addr->ipv4) || (addr->has_ipv6 && !addr->ipv6)) { ++ return PF_INET; ++ } ++ return PF_UNSPEC; ++} ++ ++static int create_fast_reuse_socket(struct addrinfo *e) ++{ ++ int slisten = qemu_socket(e->ai_family, e->ai_socktype, e->ai_protocol); ++ if (slisten < 0) { ++ return -1; ++ } ++ socket_set_fast_reuse(slisten); ++ return slisten; ++} ++ ++static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) ++{ ++#ifndef IPV6_V6ONLY ++ return bind(socket, e->ai_addr, e->ai_addrlen); ++#else ++ /* ++ * Deals with first & last cases in matrix in comment ++ * for inet_ai_family_from_address(). ++ */ ++ int v6only = ++ ((!saddr->has_ipv4 && !saddr->has_ipv6) || ++ (saddr->has_ipv4 && saddr->ipv4 && ++ saddr->has_ipv6 && saddr->ipv6)) ? 0 : 1; ++ int stat; ++ ++ rebind: ++ if (e->ai_family == PF_INET6) { ++ setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, ++ sizeof(v6only)); ++ } ++ ++ stat = bind(socket, e->ai_addr, e->ai_addrlen); ++ if (!stat) { ++ return 0; ++ } ++ ++ /* If we got EADDRINUSE from an IPv6 bind & v6only is unset, ++ * it could be that the IPv4 port is already claimed, so retry ++ * with v6only set ++ */ ++ if (e->ai_family == PF_INET6 && errno == EADDRINUSE && !v6only) { ++ v6only = 1; ++ goto rebind; ++ } ++ return stat; ++#endif ++} ++ ++static int inet_listen_saddr(InetSocketAddress *saddr, ++ int port_offset, ++ int num, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ struct addrinfo ai, *res, *e; ++ char port[33]; ++ char uaddr[INET6_ADDRSTRLEN+1]; ++ char uport[33]; ++ int rc, port_min, port_max, p; ++ int slisten = -1; ++ int saved_errno = 0; ++ bool socket_created = false; ++ ++ if (saddr->keep_alive) { ++ error_setg(errp, "keep-alive option is not supported for passive " ++ "sockets"); ++ return -1; ++ } ++ ++ memset(&ai,0, sizeof(ai)); ++ ai.ai_flags = AI_PASSIVE; ++ if (saddr->has_numeric && saddr->numeric) { ++ ai.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; ++ } ++ ai.ai_socktype = SOCK_STREAM; ++ ai.ai_family = inet_ai_family_from_address(saddr, errp); ++ if (*errp) { ++ return -1; ++ } ++ ++ if (saddr->host == NULL) { ++ error_setg(errp, "host not specified"); ++ return -1; ++ } ++ if (saddr->port != NULL) { ++ pstrcpy(port, sizeof(port), saddr->port); ++ } else { ++ port[0] = '\0'; ++ } ++ ++ /* lookup */ ++ if (port_offset) { ++ uint64_t baseport; ++ if (strlen(port) == 0) { ++ error_setg(errp, "port not specified"); ++ return -1; ++ } ++ if (parse_uint_full(port, 10, &baseport) < 0) { ++ error_setg(errp, "can't convert to a number: %s", port); ++ return -1; ++ } ++ if (baseport > 65535 || ++ baseport + port_offset > 65535) { ++ error_setg(errp, "port %s out of range", port); ++ return -1; ++ } ++ snprintf(port, sizeof(port), "%d", (int)baseport + port_offset); ++ } ++ rc = getaddrinfo(strlen(saddr->host) ? saddr->host : NULL, ++ strlen(port) ? port : NULL, &ai, &res); ++ if (rc != 0) { ++ error_setg(errp, "address resolution failed for %s:%s: %s", ++ saddr->host, port, gai_strerror(rc)); ++ return -1; ++ } ++ ++ /* create socket + bind/listen */ ++ for (e = res; e != NULL; e = e->ai_next) { ++#ifdef HAVE_IPPROTO_MPTCP ++ if (saddr->has_mptcp && saddr->mptcp) { ++ e->ai_protocol = IPPROTO_MPTCP; ++ } ++#endif ++ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, ++ uaddr,INET6_ADDRSTRLEN,uport,32, ++ NI_NUMERICHOST | NI_NUMERICSERV); ++ ++ port_min = inet_getport(e); ++ port_max = saddr->has_to ? saddr->to + port_offset : port_min; ++ for (p = port_min; p <= port_max; p++) { ++ inet_setport(e, p); ++ ++ slisten = create_fast_reuse_socket(e); ++ if (slisten < 0) { ++ /* First time we expect we might fail to create the socket ++ * eg if 'e' has AF_INET6 but ipv6 kmod is not loaded. ++ * Later iterations should always succeed if first iteration ++ * worked though, so treat that as fatal. ++ */ ++ if (p == port_min) { ++ continue; ++ } else { ++ error_setg_errno(errp, errno, ++ "Failed to recreate failed listening socket"); ++ goto listen_failed; ++ } ++ } ++ socket_created = true; ++ ++ rc = try_bind(slisten, saddr, e); ++ if (rc < 0) { ++ if (errno != EADDRINUSE) { ++ error_setg_errno(errp, errno, "Failed to bind socket"); ++ goto listen_failed; ++ } ++ } else { ++ if (!listen(slisten, num)) { ++ goto listen_ok; ++ } ++ if (errno != EADDRINUSE) { ++ error_setg_errno(errp, errno, "Failed to listen on socket"); ++ goto listen_failed; ++ } ++ } ++ /* Someone else managed to bind to the same port and beat us ++ * to listen on it! Socket semantics does not allow us to ++ * recover from this situation, so we need to recreate the ++ * socket to allow bind attempts for subsequent ports: ++ */ ++ close(slisten); ++ slisten = -1; ++ } ++ } ++ error_setg_errno(errp, errno, ++ socket_created ? ++ "Failed to find an available port" : ++ "Failed to create a socket"); ++listen_failed: ++ saved_errno = errno; ++ if (slisten >= 0) { ++ close(slisten); ++ } ++ freeaddrinfo(res); ++ errno = saved_errno; ++ return -1; ++ ++listen_ok: ++ freeaddrinfo(res); ++ return slisten; ++} ++ ++#ifdef _WIN32 ++#define QEMU_SOCKET_RC_INPROGRESS(rc) \ ++ ((rc) == -EINPROGRESS || (rc) == -EWOULDBLOCK || (rc) == -WSAEALREADY) ++#else ++#define QEMU_SOCKET_RC_INPROGRESS(rc) \ ++ ((rc) == -EINPROGRESS) ++#endif ++ ++static int inet_connect_addr(const InetSocketAddress *saddr, ++ struct addrinfo *addr, Error **errp) ++{ ++ int sock, rc; ++ ++ sock = qemu_socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket family %d", ++ addr->ai_family); ++ return -1; ++ } ++ socket_set_fast_reuse(sock); ++ ++ /* connect to peer */ ++ do { ++ rc = 0; ++ if (connect(sock, addr->ai_addr, addr->ai_addrlen) < 0) { ++ rc = -errno; ++ } ++ } while (rc == -EINTR); ++ ++ if (rc < 0) { ++ error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", ++ saddr->host, saddr->port); ++ close(sock); ++ return -1; ++ } ++ ++ return sock; ++} ++ ++static struct addrinfo *inet_parse_connect_saddr(InetSocketAddress *saddr, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ struct addrinfo ai, *res; ++ int rc; ++ static int useV4Mapped = 1; ++ ++ memset(&ai, 0, sizeof(ai)); ++ ++ ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; ++ if (qatomic_read(&useV4Mapped)) { ++ ai.ai_flags |= AI_V4MAPPED; ++ } ++ ai.ai_socktype = SOCK_STREAM; ++ ai.ai_family = inet_ai_family_from_address(saddr, errp); ++ if (*errp) { ++ return NULL; ++ } ++ ++ if (saddr->host == NULL || saddr->port == NULL) { ++ error_setg(errp, "host and/or port not specified"); ++ return NULL; ++ } ++ ++ /* lookup */ ++ rc = getaddrinfo(saddr->host, saddr->port, &ai, &res); ++ ++ /* At least FreeBSD and OS-X 10.6 declare AI_V4MAPPED but ++ * then don't implement it in their getaddrinfo(). Detect ++ * this and retry without the flag since that's preferable ++ * to a fatal error ++ */ ++ if (rc == EAI_BADFLAGS && ++ (ai.ai_flags & AI_V4MAPPED)) { ++ qatomic_set(&useV4Mapped, 0); ++ ai.ai_flags &= ~AI_V4MAPPED; ++ rc = getaddrinfo(saddr->host, saddr->port, &ai, &res); ++ } ++ if (rc != 0) { ++ error_setg(errp, "address resolution failed for %s:%s: %s", ++ saddr->host, saddr->port, gai_strerror(rc)); ++ return NULL; ++ } ++ return res; ++} ++ ++/** ++ * Create a socket and connect it to an address. ++ * ++ * @saddr: Inet socket address specification ++ * @errp: set on error ++ * ++ * Returns: -1 on error, file descriptor on success. ++ */ ++int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) ++{ ++ Error *local_err = NULL; ++ struct addrinfo *res, *e; ++ int sock = -1; ++ ++ res = inet_parse_connect_saddr(saddr, errp); ++ if (!res) { ++ return -1; ++ } ++ ++ for (e = res; e != NULL; e = e->ai_next) { ++ error_free(local_err); ++ local_err = NULL; ++ ++#ifdef HAVE_IPPROTO_MPTCP ++ if (saddr->has_mptcp && saddr->mptcp) { ++ e->ai_protocol = IPPROTO_MPTCP; ++ } ++#endif ++ ++ sock = inet_connect_addr(saddr, e, &local_err); ++ if (sock >= 0) { ++ break; ++ } ++ } ++ ++ freeaddrinfo(res); ++ ++ if (sock < 0) { ++ error_propagate(errp, local_err); ++ return sock; ++ } ++ ++ if (saddr->keep_alive) { ++ int val = 1; ++ int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, ++ &val, sizeof(val)); ++ ++ if (ret < 0) { ++ error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); ++ close(sock); ++ return -1; ++ } ++ } ++ ++ return sock; ++} ++ ++static int inet_dgram_saddr(InetSocketAddress *sraddr, ++ InetSocketAddress *sladdr, ++ Error **errp) ++{ ++ ERRP_GUARD(); ++ struct addrinfo ai, *peer = NULL, *local = NULL; ++ const char *addr; ++ const char *port; ++ int sock = -1, rc; ++ ++ /* lookup peer addr */ ++ memset(&ai,0, sizeof(ai)); ++ ai.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ADDRCONFIG; ++ ai.ai_socktype = SOCK_DGRAM; ++ ai.ai_family = inet_ai_family_from_address(sraddr, errp); ++ if (*errp) { ++ goto err; ++ } ++ ++ addr = sraddr->host; ++ port = sraddr->port; ++ if (addr == NULL || strlen(addr) == 0) { ++ addr = "localhost"; ++ } ++ if (port == NULL || strlen(port) == 0) { ++ error_setg(errp, "remote port not specified"); ++ goto err; ++ } ++ ++ if ((rc = getaddrinfo(addr, port, &ai, &peer)) != 0) { ++ error_setg(errp, "address resolution failed for %s:%s: %s", addr, port, ++ gai_strerror(rc)); ++ goto err; ++ } ++ ++ /* lookup local addr */ ++ memset(&ai,0, sizeof(ai)); ++ ai.ai_flags = AI_PASSIVE; ++ ai.ai_family = peer->ai_family; ++ ai.ai_socktype = SOCK_DGRAM; ++ ++ if (sladdr) { ++ addr = sladdr->host; ++ port = sladdr->port; ++ if (addr == NULL || strlen(addr) == 0) { ++ addr = NULL; ++ } ++ if (!port || strlen(port) == 0) { ++ port = "0"; ++ } ++ } else { ++ addr = NULL; ++ port = "0"; ++ } ++ ++ if ((rc = getaddrinfo(addr, port, &ai, &local)) != 0) { ++ error_setg(errp, "address resolution failed for %s:%s: %s", addr, port, ++ gai_strerror(rc)); ++ goto err; ++ } ++ ++ /* create socket */ ++ sock = qemu_socket(peer->ai_family, peer->ai_socktype, peer->ai_protocol); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket family %d", ++ peer->ai_family); ++ goto err; ++ } ++ socket_set_fast_reuse(sock); ++ ++ /* bind socket */ ++ if (bind(sock, local->ai_addr, local->ai_addrlen) < 0) { ++ error_setg_errno(errp, errno, "Failed to bind socket"); ++ goto err; ++ } ++ ++ /* connect to peer */ ++ if (connect(sock,peer->ai_addr,peer->ai_addrlen) < 0) { ++ error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", ++ addr, port); ++ goto err; ++ } ++ ++ freeaddrinfo(local); ++ freeaddrinfo(peer); ++ return sock; ++ ++err: ++ if (sock != -1) { ++ close(sock); ++ } ++ if (local) { ++ freeaddrinfo(local); ++ } ++ if (peer) { ++ freeaddrinfo(peer); ++ } ++ ++ return -1; ++} ++ ++/* compatibility wrapper */ ++static int inet_parse_flag(const char *flagname, const char *optstr, bool *val, ++ Error **errp) ++{ ++ char *end; ++ size_t len; ++ ++ end = strstr(optstr, ","); ++ if (end) { ++ if (end[1] == ',') { /* Reject 'ipv6=on,,foo' */ ++ error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); ++ return -1; ++ } ++ len = end - optstr; ++ } else { ++ len = strlen(optstr); ++ } ++ if (len == 0 || (len == 3 && strncmp(optstr, "=on", len) == 0)) { ++ *val = true; ++ } else if (len == 4 && strncmp(optstr, "=off", len) == 0) { ++ *val = false; ++ } else { ++ error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); ++ return -1; ++ } ++ return 0; ++} ++ ++int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) ++{ ++ const char *optstr, *h; ++ char host[65]; ++ char port[33]; ++ int to; ++ int pos; ++ char *begin; ++ ++ memset(addr, 0, sizeof(*addr)); ++ ++ /* parse address */ ++ if (str[0] == ':') { ++ /* no host given */ ++ host[0] = '\0'; ++ if (sscanf(str, ":%32[^,]%n", port, &pos) != 1) { ++ error_setg(errp, "error parsing port in address '%s'", str); ++ return -1; ++ } ++ } else if (str[0] == '[') { ++ /* IPv6 addr */ ++ if (sscanf(str, "[%64[^]]]:%32[^,]%n", host, port, &pos) != 2) { ++ error_setg(errp, "error parsing IPv6 address '%s'", str); ++ return -1; ++ } ++ } else { ++ /* hostname or IPv4 addr */ ++ if (sscanf(str, "%64[^:]:%32[^,]%n", host, port, &pos) != 2) { ++ error_setg(errp, "error parsing address '%s'", str); ++ return -1; ++ } ++ } ++ ++ addr->host = g_strdup(host); ++ addr->port = g_strdup(port); ++ ++ /* parse options */ ++ optstr = str + pos; ++ h = strstr(optstr, ",to="); ++ if (h) { ++ h += 4; ++ if (sscanf(h, "%d%n", &to, &pos) != 1 || ++ (h[pos] != '\0' && h[pos] != ',')) { ++ error_setg(errp, "error parsing to= argument"); ++ return -1; ++ } ++ addr->has_to = true; ++ addr->to = to; ++ } ++ begin = strstr(optstr, ",ipv4"); ++ if (begin) { ++ if (inet_parse_flag("ipv4", begin + 5, &addr->ipv4, errp) < 0) { ++ return -1; ++ } ++ addr->has_ipv4 = true; ++ } ++ begin = strstr(optstr, ",ipv6"); ++ if (begin) { ++ if (inet_parse_flag("ipv6", begin + 5, &addr->ipv6, errp) < 0) { ++ return -1; ++ } ++ addr->has_ipv6 = true; ++ } ++ begin = strstr(optstr, ",keep-alive"); ++ if (begin) { ++ if (inet_parse_flag("keep-alive", begin + strlen(",keep-alive"), ++ &addr->keep_alive, errp) < 0) ++ { ++ return -1; ++ } ++ addr->has_keep_alive = true; ++ } ++#ifdef HAVE_IPPROTO_MPTCP ++ begin = strstr(optstr, ",mptcp"); ++ if (begin) { ++ if (inet_parse_flag("mptcp", begin + strlen(",mptcp"), ++ &addr->mptcp, errp) < 0) ++ { ++ return -1; ++ } ++ addr->has_mptcp = true; ++ } ++#endif ++ return 0; ++} ++ ++ ++/** ++ * Create a blocking socket and connect it to an address. ++ * ++ * @str: address string ++ * @errp: set in case of an error ++ * ++ * Returns -1 in case of error, file descriptor on success ++ **/ ++int inet_connect(const char *str, Error **errp) ++{ ++ int sock = -1; ++ InetSocketAddress *addr = g_new(InetSocketAddress, 1); ++ ++ if (!inet_parse(addr, str, errp)) { ++ sock = inet_connect_saddr(addr, errp); ++ } ++ qapi_free_InetSocketAddress(addr); ++ return sock; ++} ++ ++#ifdef CONFIG_AF_VSOCK ++static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr, ++ struct sockaddr_vm *svm, ++ Error **errp) ++{ ++ uint64_t val; ++ ++ memset(svm, 0, sizeof(*svm)); ++ svm->svm_family = AF_VSOCK; ++ ++ if (parse_uint_full(vaddr->cid, 10, &val) < 0 || ++ val > UINT32_MAX) { ++ error_setg(errp, "Failed to parse cid '%s'", vaddr->cid); ++ return false; ++ } ++ svm->svm_cid = val; ++ ++ if (parse_uint_full(vaddr->port, 10, &val) < 0 || ++ val > UINT32_MAX) { ++ error_setg(errp, "Failed to parse port '%s'", vaddr->port); ++ return false; ++ } ++ svm->svm_port = val; ++ ++ return true; ++} ++ ++static int vsock_connect_addr(const VsockSocketAddress *vaddr, ++ const struct sockaddr_vm *svm, Error **errp) ++{ ++ int sock, rc; ++ ++ sock = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket family %d", ++ AF_VSOCK); ++ return -1; ++ } ++ ++ /* connect to peer */ ++ do { ++ rc = 0; ++ if (connect(sock, (const struct sockaddr *)svm, sizeof(*svm)) < 0) { ++ rc = -errno; ++ } ++ } while (rc == -EINTR); ++ ++ if (rc < 0) { ++ error_setg_errno(errp, errno, "Failed to connect to '%s:%s'", ++ vaddr->cid, vaddr->port); ++ close(sock); ++ return -1; ++ } ++ ++ return sock; ++} ++ ++static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp) ++{ ++ struct sockaddr_vm svm; ++ ++ if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) { ++ return -1; ++ } ++ ++ return vsock_connect_addr(vaddr, &svm, errp); ++} ++ ++static int vsock_listen_saddr(VsockSocketAddress *vaddr, ++ int num, ++ Error **errp) ++{ ++ struct sockaddr_vm svm; ++ int slisten; ++ ++ if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) { ++ return -1; ++ } ++ ++ slisten = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); ++ if (slisten < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket"); ++ return -1; ++ } ++ ++ if (bind(slisten, (const struct sockaddr *)&svm, sizeof(svm)) != 0) { ++ error_setg_errno(errp, errno, "Failed to bind socket"); ++ close(slisten); ++ return -1; ++ } ++ ++ if (listen(slisten, num) != 0) { ++ error_setg_errno(errp, errno, "Failed to listen on socket"); ++ close(slisten); ++ return -1; ++ } ++ return slisten; ++} ++ ++static int vsock_parse(VsockSocketAddress *addr, const char *str, ++ Error **errp) ++{ ++ char cid[33]; ++ char port[33]; ++ int n; ++ ++ if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) { ++ error_setg(errp, "error parsing address '%s'", str); ++ return -1; ++ } ++ if (str[n] != '\0') { ++ error_setg(errp, "trailing characters in address '%s'", str); ++ return -1; ++ } ++ ++ addr->cid = g_strdup(cid); ++ addr->port = g_strdup(port); ++ return 0; ++} ++#else ++static void vsock_unsupported(Error **errp) ++{ ++ error_setg(errp, "socket family AF_VSOCK unsupported"); ++} ++ ++static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp) ++{ ++ vsock_unsupported(errp); ++ return -1; ++} ++ ++static int vsock_listen_saddr(VsockSocketAddress *vaddr, ++ int num, ++ Error **errp) ++{ ++ vsock_unsupported(errp); ++ return -1; ++} ++ ++static int vsock_parse(VsockSocketAddress *addr, const char *str, ++ Error **errp) ++{ ++ vsock_unsupported(errp); ++ return -1; ++} ++#endif /* CONFIG_AF_VSOCK */ ++ ++static bool saddr_is_abstract(UnixSocketAddress *saddr) ++{ ++#ifdef CONFIG_LINUX ++ return saddr->abstract; ++#else ++ return false; ++#endif ++} ++ ++static bool saddr_is_tight(UnixSocketAddress *saddr) ++{ ++#ifdef CONFIG_LINUX ++ return !saddr->has_tight || saddr->tight; ++#else ++ return false; ++#endif ++} ++ ++static int unix_listen_saddr(UnixSocketAddress *saddr, ++ int num, ++ Error **errp) ++{ ++ bool abstract = saddr_is_abstract(saddr); ++ struct sockaddr_un un; ++ int sock, fd; ++ char *pathbuf = NULL; ++ const char *path; ++ size_t pathlen; ++ size_t addrlen; ++ ++ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create Unix socket"); ++ return -1; ++ } ++ ++ if (saddr->path[0] || abstract) { ++ path = saddr->path; ++ } else { ++ path = pathbuf = g_strdup_printf("%s/qemu-socket-XXXXXX", ++ g_get_tmp_dir()); ++ } ++ ++ pathlen = strlen(path); ++ if (pathlen > sizeof(un.sun_path) || ++ (abstract && pathlen > (sizeof(un.sun_path) - 1))) { ++ error_setg(errp, "UNIX socket path '%s' is too long", path); ++ error_append_hint(errp, "Path must be less than %zu bytes\n", ++ abstract ? sizeof(un.sun_path) - 1 : ++ sizeof(un.sun_path)); ++ goto err; ++ } ++ ++ if (pathbuf != NULL) { ++ /* ++ * This dummy fd usage silences the mktemp() insecure warning. ++ * Using mkstemp() doesn't make things more secure here ++ * though. bind() complains about existing files, so we have ++ * to unlink first and thus re-open the race window. The ++ * worst case possible is bind() failing, i.e. a DoS attack. ++ */ ++ fd = mkstemp(pathbuf); ++ if (fd < 0) { ++ error_setg_errno(errp, errno, ++ "Failed to make a temporary socket %s", pathbuf); ++ goto err; ++ } ++ close(fd); ++ } ++ ++ if (!abstract && unlink(path) < 0 && errno != ENOENT) { ++ error_setg_errno(errp, errno, ++ "Failed to unlink socket %s", path); ++ goto err; ++ } ++ ++ memset(&un, 0, sizeof(un)); ++ un.sun_family = AF_UNIX; ++ addrlen = sizeof(un); ++ ++ if (abstract) { ++ un.sun_path[0] = '\0'; ++ memcpy(&un.sun_path[1], path, pathlen); ++ if (saddr_is_tight(saddr)) { ++ addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + pathlen; ++ } ++ } else { ++ memcpy(un.sun_path, path, pathlen); ++ } ++ ++ if (bind(sock, (struct sockaddr *) &un, addrlen) < 0) { ++ error_setg_errno(errp, errno, "Failed to bind socket to %s", path); ++ goto err; ++ } ++ if (listen(sock, num) < 0) { ++ error_setg_errno(errp, errno, "Failed to listen on socket"); ++ goto err; ++ } ++ ++ g_free(pathbuf); ++ return sock; ++ ++err: ++ g_free(pathbuf); ++ close(sock); ++ return -1; ++} ++ ++static int unix_connect_saddr(UnixSocketAddress *saddr, Error **errp) ++{ ++ bool abstract = saddr_is_abstract(saddr); ++ struct sockaddr_un un; ++ int sock, rc; ++ size_t pathlen; ++ size_t addrlen; ++ ++ if (saddr->path == NULL) { ++ error_setg(errp, "unix connect: no path specified"); ++ return -1; ++ } ++ ++ sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); ++ if (sock < 0) { ++ error_setg_errno(errp, errno, "Failed to create socket"); ++ return -1; ++ } ++ ++ pathlen = strlen(saddr->path); ++ if (pathlen > sizeof(un.sun_path) || ++ (abstract && pathlen > (sizeof(un.sun_path) - 1))) { ++ error_setg(errp, "UNIX socket path '%s' is too long", saddr->path); ++ error_append_hint(errp, "Path must be less than %zu bytes\n", ++ abstract ? sizeof(un.sun_path) - 1 : ++ sizeof(un.sun_path)); ++ goto err; ++ } ++ ++ memset(&un, 0, sizeof(un)); ++ un.sun_family = AF_UNIX; ++ addrlen = sizeof(un); ++ ++ if (abstract) { ++ un.sun_path[0] = '\0'; ++ memcpy(&un.sun_path[1], saddr->path, pathlen); ++ if (saddr_is_tight(saddr)) { ++ addrlen = offsetof(struct sockaddr_un, sun_path) + 1 + pathlen; ++ } ++ } else { ++ memcpy(un.sun_path, saddr->path, pathlen); ++ } ++ /* connect to peer */ ++ do { ++ rc = 0; ++ if (connect(sock, (struct sockaddr *) &un, addrlen) < 0) { ++ rc = -errno; ++ } ++ } while (rc == -EINTR); ++ ++ if (rc < 0) { ++ error_setg_errno(errp, -rc, "Failed to connect to '%s'", ++ saddr->path); ++ goto err; ++ } ++ ++ return sock; ++ ++ err: ++ close(sock); ++ return -1; ++} ++ ++/* compatibility wrapper */ ++int unix_listen(const char *str, Error **errp) ++{ ++ UnixSocketAddress *saddr; ++ int sock; ++ ++ saddr = g_new0(UnixSocketAddress, 1); ++ saddr->path = g_strdup(str); ++ sock = unix_listen_saddr(saddr, 1, errp); ++ qapi_free_UnixSocketAddress(saddr); ++ return sock; ++} ++ ++int unix_connect(const char *path, Error **errp) ++{ ++ UnixSocketAddress *saddr; ++ int sock; ++ ++ saddr = g_new0(UnixSocketAddress, 1); ++ saddr->path = g_strdup(path); ++ sock = unix_connect_saddr(saddr, errp); ++ qapi_free_UnixSocketAddress(saddr); ++ return sock; ++} ++ ++char *socket_uri(SocketAddress *addr) ++{ ++ switch (addr->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ return g_strdup_printf("tcp:%s:%s", ++ addr->u.inet.host, ++ addr->u.inet.port); ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ return g_strdup_printf("unix:%s", ++ addr->u.q_unix.path); ++ case SOCKET_ADDRESS_TYPE_FD: ++ return g_strdup_printf("fd:%s", addr->u.fd.str); ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ return g_strdup_printf("vsock:%s:%s", ++ addr->u.vsock.cid, ++ addr->u.vsock.port); ++ default: ++ return g_strdup("unknown address type"); ++ } ++} ++ ++SocketAddress *socket_parse(const char *str, Error **errp) ++{ ++ SocketAddress *addr; ++ ++ addr = g_new0(SocketAddress, 1); ++ if (strstart(str, "unix:", NULL)) { ++ if (str[5] == '\0') { ++ error_setg(errp, "invalid Unix socket address"); ++ goto fail; ++ } else { ++ addr->type = SOCKET_ADDRESS_TYPE_UNIX; ++ addr->u.q_unix.path = g_strdup(str + 5); ++ } ++ } else if (strstart(str, "fd:", NULL)) { ++ if (str[3] == '\0') { ++ error_setg(errp, "invalid file descriptor address"); ++ goto fail; ++ } else { ++ addr->type = SOCKET_ADDRESS_TYPE_FD; ++ addr->u.fd.str = g_strdup(str + 3); ++ } ++ } else if (strstart(str, "vsock:", NULL)) { ++ addr->type = SOCKET_ADDRESS_TYPE_VSOCK; ++ if (vsock_parse(&addr->u.vsock, str + strlen("vsock:"), errp)) { ++ goto fail; ++ } ++ } else if (strstart(str, "tcp:", NULL)) { ++ addr->type = SOCKET_ADDRESS_TYPE_INET; ++ if (inet_parse(&addr->u.inet, str + strlen("tcp:"), errp)) { ++ goto fail; ++ } ++ } else { ++ addr->type = SOCKET_ADDRESS_TYPE_INET; ++ if (inet_parse(&addr->u.inet, str, errp)) { ++ goto fail; ++ } ++ } ++ return addr; ++ ++fail: ++ qapi_free_SocketAddress(addr); ++ return NULL; ++} ++ ++static int socket_get_fd(const char *fdstr, Error **errp) ++{ ++ Monitor *cur_mon = monitor_cur(); ++ int fd; ++ if (cur_mon) { ++ fd = monitor_get_fd(cur_mon, fdstr, errp); ++ if (fd < 0) { ++ return -1; ++ } ++ } else { ++ if (qemu_strtoi(fdstr, NULL, 10, &fd) < 0) { ++ error_setg_errno(errp, errno, ++ "Unable to parse FD number %s", ++ fdstr); ++ return -1; ++ } ++ } ++ if (!fd_is_socket(fd)) { ++ error_setg(errp, "File descriptor '%s' is not a socket", fdstr); ++ close(fd); ++ return -1; ++ } ++ return fd; ++} ++ ++int socket_address_parse_named_fd(SocketAddress *addr, Error **errp) ++{ ++ int fd; ++ ++ if (addr->type != SOCKET_ADDRESS_TYPE_FD) { ++ return 0; ++ } ++ ++ fd = socket_get_fd(addr->u.fd.str, errp); ++ if (fd < 0) { ++ return fd; ++ } ++ ++ g_free(addr->u.fd.str); ++ addr->u.fd.str = g_strdup_printf("%d", fd); ++ ++ return 0; ++} ++ ++int socket_connect(SocketAddress *addr, Error **errp) ++{ ++ int fd; ++ ++ switch (addr->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ fd = inet_connect_saddr(&addr->u.inet, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ fd = unix_connect_saddr(&addr->u.q_unix, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_FD: ++ fd = socket_get_fd(addr->u.fd.str, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ fd = vsock_connect_saddr(&addr->u.vsock, errp); ++ break; ++ ++ default: ++ abort(); ++ } ++ return fd; ++} ++ ++int socket_listen(SocketAddress *addr, int num, Error **errp) ++{ ++ int fd; ++ ++ trace_socket_listen(num); ++ switch (addr->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ fd = inet_listen_saddr(&addr->u.inet, 0, num, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ fd = unix_listen_saddr(&addr->u.q_unix, num, errp); ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_FD: ++ fd = socket_get_fd(addr->u.fd.str, errp); ++ if (fd < 0) { ++ return -1; ++ } ++ ++ /* ++ * If the socket is not yet in the listen state, then transition it to ++ * the listen state now. ++ * ++ * If it's already listening then this updates the backlog value as ++ * requested. ++ * ++ * If this socket cannot listen because it's already in another state ++ * (e.g. unbound or connected) then we'll catch the error here. ++ */ ++ if (listen(fd, num) != 0) { ++ error_setg_errno(errp, errno, "Failed to listen on fd socket"); ++ close(fd); ++ return -1; ++ } ++ break; ++ ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ fd = vsock_listen_saddr(&addr->u.vsock, num, errp); ++ break; ++ ++ default: ++ abort(); ++ } ++ return fd; ++} ++ ++void socket_listen_cleanup(int fd, Error **errp) ++{ ++ SocketAddress *addr; ++ ++ addr = socket_local_address(fd, errp); ++ if (!addr) { ++ return; ++ } ++ ++ if (addr->type == SOCKET_ADDRESS_TYPE_UNIX ++ && addr->u.q_unix.path) { ++ if (unlink(addr->u.q_unix.path) < 0 && errno != ENOENT) { ++ error_setg_errno(errp, errno, ++ "Failed to unlink socket %s", ++ addr->u.q_unix.path); ++ } ++ } ++ ++ qapi_free_SocketAddress(addr); ++} ++ ++int socket_dgram(SocketAddress *remote, SocketAddress *local, Error **errp) ++{ ++ int fd; ++ ++ /* ++ * TODO SOCKET_ADDRESS_TYPE_FD when fd is AF_INET or AF_INET6 ++ * (although other address families can do SOCK_DGRAM, too) ++ */ ++ switch (remote->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ fd = inet_dgram_saddr(&remote->u.inet, ++ local ? &local->u.inet : NULL, errp); ++ break; ++ ++ default: ++ error_setg(errp, "socket type unsupported for datagram"); ++ fd = -1; ++ } ++ return fd; ++} ++ ++ ++static SocketAddress * ++socket_sockaddr_to_address_inet(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp) ++{ ++ char host[NI_MAXHOST]; ++ char serv[NI_MAXSERV]; ++ SocketAddress *addr; ++ InetSocketAddress *inet; ++ int ret; ++ ++ ret = getnameinfo((struct sockaddr *)sa, salen, ++ host, sizeof(host), ++ serv, sizeof(serv), ++ NI_NUMERICHOST | NI_NUMERICSERV); ++ if (ret != 0) { ++ error_setg(errp, "Cannot format numeric socket address: %s", ++ gai_strerror(ret)); ++ return NULL; ++ } ++ ++ addr = g_new0(SocketAddress, 1); ++ addr->type = SOCKET_ADDRESS_TYPE_INET; ++ inet = &addr->u.inet; ++ inet->host = g_strdup(host); ++ inet->port = g_strdup(serv); ++ if (sa->ss_family == AF_INET) { ++ inet->has_ipv4 = inet->ipv4 = true; ++ } else { ++ inet->has_ipv6 = inet->ipv6 = true; ++ } ++ ++ return addr; ++} ++ ++ ++static SocketAddress * ++socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp) ++{ ++ SocketAddress *addr; ++ struct sockaddr_un *su = (struct sockaddr_un *)sa; ++ ++ addr = g_new0(SocketAddress, 1); ++ addr->type = SOCKET_ADDRESS_TYPE_UNIX; ++ salen -= offsetof(struct sockaddr_un, sun_path); ++#ifdef CONFIG_LINUX ++ if (salen > 0 && !su->sun_path[0]) { ++ /* Linux abstract socket */ ++ addr->u.q_unix.path = g_strndup(su->sun_path + 1, salen - 1); ++ addr->u.q_unix.has_abstract = true; ++ addr->u.q_unix.abstract = true; ++ addr->u.q_unix.has_tight = true; ++ addr->u.q_unix.tight = salen < sizeof(su->sun_path); ++ return addr; ++ } ++#endif ++ ++ addr->u.q_unix.path = g_strndup(su->sun_path, salen); ++ return addr; ++} ++ ++#ifdef CONFIG_AF_VSOCK ++static SocketAddress * ++socket_sockaddr_to_address_vsock(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp) ++{ ++ SocketAddress *addr; ++ VsockSocketAddress *vaddr; ++ struct sockaddr_vm *svm = (struct sockaddr_vm *)sa; ++ ++ addr = g_new0(SocketAddress, 1); ++ addr->type = SOCKET_ADDRESS_TYPE_VSOCK; ++ vaddr = &addr->u.vsock; ++ vaddr->cid = g_strdup_printf("%u", svm->svm_cid); ++ vaddr->port = g_strdup_printf("%u", svm->svm_port); ++ ++ return addr; ++} ++#endif /* CONFIG_AF_VSOCK */ ++ ++SocketAddress * ++socket_sockaddr_to_address(struct sockaddr_storage *sa, ++ socklen_t salen, ++ Error **errp) ++{ ++ switch (sa->ss_family) { ++ case AF_INET: ++ case AF_INET6: ++ return socket_sockaddr_to_address_inet(sa, salen, errp); ++ ++ case AF_UNIX: ++ return socket_sockaddr_to_address_unix(sa, salen, errp); ++ ++#ifdef CONFIG_AF_VSOCK ++ case AF_VSOCK: ++ return socket_sockaddr_to_address_vsock(sa, salen, errp); ++#endif ++ ++ default: ++ error_setg(errp, "socket family %d unsupported", ++ sa->ss_family); ++ return NULL; ++ } ++ return 0; ++} ++ ++ ++SocketAddress *socket_local_address(int fd, Error **errp) ++{ ++ struct sockaddr_storage ss; ++ socklen_t sslen = sizeof(ss); ++ ++ if (getsockname(fd, (struct sockaddr *)&ss, &sslen) < 0) { ++ error_setg_errno(errp, errno, "%s", ++ "Unable to query local socket address"); ++ return NULL; ++ } ++ ++ return socket_sockaddr_to_address(&ss, sslen, errp); ++} ++ ++ ++SocketAddress *socket_remote_address(int fd, Error **errp) ++{ ++ struct sockaddr_storage ss; ++ socklen_t sslen = sizeof(ss); ++ ++ if (getpeername(fd, (struct sockaddr *)&ss, &sslen) < 0) { ++ error_setg_errno(errp, errno, "%s", ++ "Unable to query remote socket address"); ++ return NULL; ++ } ++ ++ return socket_sockaddr_to_address(&ss, sslen, errp); ++} ++ ++ ++SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) ++{ ++ SocketAddress *addr; ++ ++ if (!addr_legacy) { ++ return NULL; ++ } ++ ++ addr = g_new(SocketAddress, 1); ++ ++ switch (addr_legacy->type) { ++ case SOCKET_ADDRESS_TYPE_INET: ++ addr->type = SOCKET_ADDRESS_TYPE_INET; ++ QAPI_CLONE_MEMBERS(InetSocketAddress, &addr->u.inet, ++ addr_legacy->u.inet.data); ++ break; ++ case SOCKET_ADDRESS_TYPE_UNIX: ++ addr->type = SOCKET_ADDRESS_TYPE_UNIX; ++ QAPI_CLONE_MEMBERS(UnixSocketAddress, &addr->u.q_unix, ++ addr_legacy->u.q_unix.data); ++ break; ++ case SOCKET_ADDRESS_TYPE_VSOCK: ++ addr->type = SOCKET_ADDRESS_TYPE_VSOCK; ++ QAPI_CLONE_MEMBERS(VsockSocketAddress, &addr->u.vsock, ++ addr_legacy->u.vsock.data); ++ break; ++ case SOCKET_ADDRESS_TYPE_FD: ++ addr->type = SOCKET_ADDRESS_TYPE_FD; ++ QAPI_CLONE_MEMBERS(FdSocketAddress, &addr->u.fd, ++ addr_legacy->u.fd.data); ++ break; ++ default: ++ abort(); ++ } ++ ++ return addr; ++} +diff --git a/qcow2/lib/util/qemu-thread-common.h b/qcow2/lib/util/qemu-thread-common.h +new file mode 100644 +index 00000000..2af6b120 +--- /dev/null ++++ b/qcow2/lib/util/qemu-thread-common.h +@@ -0,0 +1,54 @@ ++/* ++ * Common qemu-thread implementation header file. ++ * ++ * Copyright Red Hat, Inc. 2018 ++ * ++ * Authors: ++ * Peter Xu , ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#ifndef QEMU_THREAD_COMMON_H ++#define QEMU_THREAD_COMMON_H ++ ++#include "qemu/thread.h" ++#include "trace.h" ++ ++static inline void qemu_mutex_post_init(QemuMutex *mutex) ++{ ++#ifdef CONFIG_DEBUG_MUTEX ++ mutex->file = NULL; ++ mutex->line = 0; ++#endif ++ mutex->initialized = true; ++} ++ ++static inline void qemu_mutex_pre_lock(QemuMutex *mutex, ++ const char *file, int line) ++{ ++ trace_qemu_mutex_lock(mutex, file, line); ++} ++ ++static inline void qemu_mutex_post_lock(QemuMutex *mutex, ++ const char *file, int line) ++{ ++#ifdef CONFIG_DEBUG_MUTEX ++ mutex->file = file; ++ mutex->line = line; ++#endif ++ trace_qemu_mutex_locked(mutex, file, line); ++} ++ ++static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, ++ const char *file, int line) ++{ ++#ifdef CONFIG_DEBUG_MUTEX ++ mutex->file = NULL; ++ mutex->line = 0; ++#endif ++ trace_qemu_mutex_unlock(mutex, file, line); ++} ++ ++#endif +diff --git a/qcow2/lib/util/qemu-thread-posix.c b/qcow2/lib/util/qemu-thread-posix.c +new file mode 100644 +index 00000000..b2e26e21 +--- /dev/null ++++ b/qcow2/lib/util/qemu-thread-posix.c +@@ -0,0 +1,686 @@ ++/* ++ * Wrappers around mutex/cond/thread functions ++ * ++ * Copyright Red Hat, Inc. 2009 ++ * ++ * Author: ++ * Marcelo Tosatti ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++#include "qemu/osdep.h" ++#include "qemu/thread.h" ++#include "qemu/atomic.h" ++#include "qemu/notify.h" ++#include "qemu-thread-common.h" ++#include "qemu/tsan.h" ++#include "qemu/bitmap.h" ++ ++#ifdef CONFIG_PTHREAD_SET_NAME_NP ++#include ++#endif ++ ++static bool name_threads; ++ ++void qemu_thread_naming(bool enable) ++{ ++ name_threads = enable; ++ ++#if !defined CONFIG_PTHREAD_SETNAME_NP_W_TID && \ ++ !defined CONFIG_PTHREAD_SETNAME_NP_WO_TID && \ ++ !defined CONFIG_PTHREAD_SET_NAME_NP ++ /* This is a debugging option, not fatal */ ++ if (enable) { ++ fprintf(stderr, "qemu: thread naming not supported on this host\n"); ++ } ++#endif ++} ++ ++static void error_exit(int err, const char *msg) ++{ ++ fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err)); ++ abort(); ++} ++ ++static inline clockid_t qemu_timedwait_clockid(void) ++{ ++#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK ++ return CLOCK_MONOTONIC; ++#else ++ return CLOCK_REALTIME; ++#endif ++} ++ ++static void compute_abs_deadline(struct timespec *ts, int ms) ++{ ++ clock_gettime(qemu_timedwait_clockid(), ts); ++ ts->tv_nsec += (ms % 1000) * 1000000; ++ ts->tv_sec += ms / 1000; ++ if (ts->tv_nsec >= 1000000000) { ++ ts->tv_sec++; ++ ts->tv_nsec -= 1000000000; ++ } ++} ++ ++void qemu_mutex_init(QemuMutex *mutex) ++{ ++ int err; ++ ++ err = pthread_mutex_init(&mutex->lock, NULL); ++ if (err) ++ error_exit(err, __func__); ++ qemu_mutex_post_init(mutex); ++} ++ ++void qemu_mutex_destroy(QemuMutex *mutex) ++{ ++ int err; ++ ++ assert(mutex->initialized); ++ mutex->initialized = false; ++ err = pthread_mutex_destroy(&mutex->lock); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line) ++{ ++ int err; ++ ++ assert(mutex->initialized); ++ qemu_mutex_pre_lock(mutex, file, line); ++ err = pthread_mutex_lock(&mutex->lock); ++ if (err) ++ error_exit(err, __func__); ++ qemu_mutex_post_lock(mutex, file, line); ++} ++ ++int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) ++{ ++ int err; ++ ++ assert(mutex->initialized); ++ err = pthread_mutex_trylock(&mutex->lock); ++ if (err == 0) { ++ qemu_mutex_post_lock(mutex, file, line); ++ return 0; ++ } ++ if (err != EBUSY) { ++ error_exit(err, __func__); ++ } ++ return -EBUSY; ++} ++ ++void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line) ++{ ++ int err; ++ ++ assert(mutex->initialized); ++ qemu_mutex_pre_unlock(mutex, file, line); ++ err = pthread_mutex_unlock(&mutex->lock); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_rec_mutex_init(QemuRecMutex *mutex) ++{ ++ int err; ++ pthread_mutexattr_t attr; ++ ++ pthread_mutexattr_init(&attr); ++ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); ++ err = pthread_mutex_init(&mutex->m.lock, &attr); ++ pthread_mutexattr_destroy(&attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ mutex->m.initialized = true; ++} ++ ++void qemu_rec_mutex_destroy(QemuRecMutex *mutex) ++{ ++ qemu_mutex_destroy(&mutex->m); ++} ++ ++void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line) ++{ ++ qemu_mutex_lock_impl(&mutex->m, file, line); ++} ++ ++int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line) ++{ ++ return qemu_mutex_trylock_impl(&mutex->m, file, line); ++} ++ ++void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line) ++{ ++ qemu_mutex_unlock_impl(&mutex->m, file, line); ++} ++ ++void qemu_cond_init(QemuCond *cond) ++{ ++ pthread_condattr_t attr; ++ int err; ++ ++ err = pthread_condattr_init(&attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++#ifdef CONFIG_PTHREAD_CONDATTR_SETCLOCK ++ err = pthread_condattr_setclock(&attr, qemu_timedwait_clockid()); ++ if (err) { ++ error_exit(err, __func__); ++ } ++#endif ++ err = pthread_cond_init(&cond->cond, &attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ err = pthread_condattr_destroy(&attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ cond->initialized = true; ++} ++ ++void qemu_cond_destroy(QemuCond *cond) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ cond->initialized = false; ++ err = pthread_cond_destroy(&cond->cond); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_cond_signal(QemuCond *cond) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ err = pthread_cond_signal(&cond->cond); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_cond_broadcast(QemuCond *cond) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ err = pthread_cond_broadcast(&cond->cond); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ qemu_mutex_pre_unlock(mutex, file, line); ++ err = pthread_cond_wait(&cond->cond, &mutex->lock); ++ qemu_mutex_post_lock(mutex, file, line); ++ if (err) ++ error_exit(err, __func__); ++} ++ ++static bool TSA_NO_TSA ++qemu_cond_timedwait_ts(QemuCond *cond, QemuMutex *mutex, struct timespec *ts, ++ const char *file, const int line) ++{ ++ int err; ++ ++ assert(cond->initialized); ++ trace_qemu_mutex_unlock(mutex, file, line); ++ err = pthread_cond_timedwait(&cond->cond, &mutex->lock, ts); ++ trace_qemu_mutex_locked(mutex, file, line); ++ if (err && err != ETIMEDOUT) { ++ error_exit(err, __func__); ++ } ++ return err != ETIMEDOUT; ++} ++ ++bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms, ++ const char *file, const int line) ++{ ++ struct timespec ts; ++ ++ compute_abs_deadline(&ts, ms); ++ return qemu_cond_timedwait_ts(cond, mutex, &ts, file, line); ++} ++ ++void qemu_sem_init(QemuSemaphore *sem, int init) ++{ ++ qemu_mutex_init(&sem->mutex); ++ qemu_cond_init(&sem->cond); ++ ++ if (init < 0) { ++ error_exit(EINVAL, __func__); ++ } ++ sem->count = init; ++} ++ ++void qemu_sem_destroy(QemuSemaphore *sem) ++{ ++ qemu_cond_destroy(&sem->cond); ++ qemu_mutex_destroy(&sem->mutex); ++} ++ ++void qemu_sem_post(QemuSemaphore *sem) ++{ ++ qemu_mutex_lock(&sem->mutex); ++ if (sem->count == UINT_MAX) { ++ error_exit(EINVAL, __func__); ++ } else { ++ sem->count++; ++ qemu_cond_signal(&sem->cond); ++ } ++ qemu_mutex_unlock(&sem->mutex); ++} ++ ++int qemu_sem_timedwait(QemuSemaphore *sem, int ms) ++{ ++ bool rc = true; ++ struct timespec ts; ++ ++ compute_abs_deadline(&ts, ms); ++ qemu_mutex_lock(&sem->mutex); ++ while (sem->count == 0) { ++ if (ms == 0) { ++ rc = false; ++ } else { ++ rc = qemu_cond_timedwait_ts(&sem->cond, &sem->mutex, &ts, ++ __FILE__, __LINE__); ++ } ++ if (!rc) { /* timeout */ ++ break; ++ } ++ } ++ if (rc) { ++ --sem->count; ++ } ++ qemu_mutex_unlock(&sem->mutex); ++ return (rc ? 0 : -1); ++} ++ ++void qemu_sem_wait(QemuSemaphore *sem) ++{ ++ qemu_mutex_lock(&sem->mutex); ++ while (sem->count == 0) { ++ qemu_cond_wait(&sem->cond, &sem->mutex); ++ } ++ --sem->count; ++ qemu_mutex_unlock(&sem->mutex); ++} ++ ++#ifdef __linux__ ++#include "qemu/futex.h" ++#else ++static inline void qemu_futex_wake(QemuEvent *ev, int n) ++{ ++ assert(ev->initialized); ++ pthread_mutex_lock(&ev->lock); ++ if (n == 1) { ++ pthread_cond_signal(&ev->cond); ++ } else { ++ pthread_cond_broadcast(&ev->cond); ++ } ++ pthread_mutex_unlock(&ev->lock); ++} ++ ++static inline void qemu_futex_wait(QemuEvent *ev, unsigned val) ++{ ++ assert(ev->initialized); ++ pthread_mutex_lock(&ev->lock); ++ if (ev->value == val) { ++ pthread_cond_wait(&ev->cond, &ev->lock); ++ } ++ pthread_mutex_unlock(&ev->lock); ++} ++#endif ++ ++/* Valid transitions: ++ * - free->set, when setting the event ++ * - busy->set, when setting the event, followed by qemu_futex_wake ++ * - set->free, when resetting the event ++ * - free->busy, when waiting ++ * ++ * set->busy does not happen (it can be observed from the outside but ++ * it really is set->free->busy). ++ * ++ * busy->free provably cannot happen; to enforce it, the set->free transition ++ * is done with an OR, which becomes a no-op if the event has concurrently ++ * transitioned to free or busy. ++ */ ++ ++#define EV_SET 0 ++#define EV_FREE 1 ++#define EV_BUSY -1 ++ ++void qemu_event_init(QemuEvent *ev, bool init) ++{ ++#ifndef __linux__ ++ pthread_mutex_init(&ev->lock, NULL); ++ pthread_cond_init(&ev->cond, NULL); ++#endif ++ ++ ev->value = (init ? EV_SET : EV_FREE); ++ ev->initialized = true; ++} ++ ++void qemu_event_destroy(QemuEvent *ev) ++{ ++ assert(ev->initialized); ++ ev->initialized = false; ++#ifndef __linux__ ++ pthread_mutex_destroy(&ev->lock); ++ pthread_cond_destroy(&ev->cond); ++#endif ++} ++ ++void qemu_event_set(QemuEvent *ev) ++{ ++ assert(ev->initialized); ++ ++ /* ++ * Pairs with both qemu_event_reset() and qemu_event_wait(). ++ * ++ * qemu_event_set has release semantics, but because it *loads* ++ * ev->value we need a full memory barrier here. ++ */ ++ smp_mb(); ++ if (qatomic_read(&ev->value) != EV_SET) { ++ int old = qatomic_xchg(&ev->value, EV_SET); ++ ++ /* Pairs with memory barrier in kernel futex_wait system call. */ ++ smp_mb__after_rmw(); ++ if (old == EV_BUSY) { ++ /* There were waiters, wake them up. */ ++ qemu_futex_wake(ev, INT_MAX); ++ } ++ } ++} ++ ++void qemu_event_reset(QemuEvent *ev) ++{ ++ assert(ev->initialized); ++ ++ /* ++ * If there was a concurrent reset (or even reset+wait), ++ * do nothing. Otherwise change EV_SET->EV_FREE. ++ */ ++ qatomic_or(&ev->value, EV_FREE); ++ ++ /* ++ * Order reset before checking the condition in the caller. ++ * Pairs with the first memory barrier in qemu_event_set(). ++ */ ++ smp_mb__after_rmw(); ++} ++ ++void qemu_event_wait(QemuEvent *ev) ++{ ++ unsigned value; ++ ++ assert(ev->initialized); ++ ++ /* ++ * qemu_event_wait must synchronize with qemu_event_set even if it does ++ * not go down the slow path, so this load-acquire is needed that ++ * synchronizes with the first memory barrier in qemu_event_set(). ++ * ++ * If we do go down the slow path, there is no requirement at all: we ++ * might miss a qemu_event_set() here but ultimately the memory barrier in ++ * qemu_futex_wait() will ensure the check is done correctly. ++ */ ++ value = qatomic_load_acquire(&ev->value); ++ if (value != EV_SET) { ++ if (value == EV_FREE) { ++ /* ++ * Leave the event reset and tell qemu_event_set that there are ++ * waiters. No need to retry, because there cannot be a concurrent ++ * busy->free transition. After the CAS, the event will be either ++ * set or busy. ++ * ++ * This cmpxchg doesn't have particular ordering requirements if it ++ * succeeds (moving the store earlier can only cause qemu_event_set() ++ * to issue _more_ wakeups), the failing case needs acquire semantics ++ * like the load above. ++ */ ++ if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { ++ return; ++ } ++ } ++ ++ /* ++ * This is the final check for a concurrent set, so it does need ++ * a smp_mb() pairing with the second barrier of qemu_event_set(). ++ * The barrier is inside the FUTEX_WAIT system call. ++ */ ++ qemu_futex_wait(ev, EV_BUSY); ++ } ++} ++ ++static __thread NotifierList thread_exit; ++ ++/* ++ * Note that in this implementation you can register a thread-exit ++ * notifier for the main thread, but it will never be called. ++ * This is OK because main thread exit can only happen when the ++ * entire process is exiting, and the API allows notifiers to not ++ * be called on process exit. ++ */ ++void qemu_thread_atexit_add(Notifier *notifier) ++{ ++ notifier_list_add(&thread_exit, notifier); ++} ++ ++void qemu_thread_atexit_remove(Notifier *notifier) ++{ ++ notifier_remove(notifier); ++} ++ ++static void qemu_thread_atexit_notify(void *arg) ++{ ++ /* ++ * Called when non-main thread exits (via qemu_thread_exit() ++ * or by returning from its start routine.) ++ */ ++ notifier_list_notify(&thread_exit, NULL); ++} ++ ++typedef struct { ++ void *(*start_routine)(void *); ++ void *arg; ++ char *name; ++} QemuThreadArgs; ++ ++static void *qemu_thread_start(void *args) ++{ ++ QemuThreadArgs *qemu_thread_args = args; ++ void *(*start_routine)(void *) = qemu_thread_args->start_routine; ++ void *arg = qemu_thread_args->arg; ++ void *r; ++ ++ /* Attempt to set the threads name; note that this is for debug, so ++ * we're not going to fail if we can't set it. ++ */ ++ if (name_threads && qemu_thread_args->name) { ++# if defined(CONFIG_PTHREAD_SETNAME_NP_W_TID) ++ pthread_setname_np(pthread_self(), qemu_thread_args->name); ++# elif defined(CONFIG_PTHREAD_SETNAME_NP_WO_TID) ++ pthread_setname_np(qemu_thread_args->name); ++# elif defined(CONFIG_PTHREAD_SET_NAME_NP) ++ pthread_set_name_np(pthread_self(), qemu_thread_args->name); ++# endif ++ } ++ QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name); ++ g_free(qemu_thread_args->name); ++ g_free(qemu_thread_args); ++ ++ /* ++ * GCC 11 with glibc 2.17 on PowerPC reports ++ * ++ * qemu-thread-posix.c:540:5: error: ā€˜__sigsetjmp’ accessing 656 bytes ++ * in a region of size 528 [-Werror=stringop-overflow=] ++ * 540 | pthread_cleanup_push(qemu_thread_atexit_notify, NULL); ++ * | ^~~~~~~~~~~~~~~~~~~~ ++ * ++ * which is clearly nonsense. ++ */ ++#pragma GCC diagnostic push ++#ifndef __clang__ ++#pragma GCC diagnostic ignored "-Wstringop-overflow" ++#endif ++ ++ pthread_cleanup_push(qemu_thread_atexit_notify, NULL); ++ r = start_routine(arg); ++ pthread_cleanup_pop(1); ++ ++#pragma GCC diagnostic pop ++ ++ return r; ++} ++ ++void qemu_thread_create(QemuThread *thread, const char *name, ++ void *(*start_routine)(void*), ++ void *arg, int mode) ++{ ++ sigset_t set, oldset; ++ int err; ++ pthread_attr_t attr; ++ QemuThreadArgs *qemu_thread_args; ++ ++ err = pthread_attr_init(&attr); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ ++ if (mode == QEMU_THREAD_DETACHED) { ++ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ++ } ++ ++ /* Leave signal handling to the iothread. */ ++ sigfillset(&set); ++ /* Blocking the signals can result in undefined behaviour. */ ++ sigdelset(&set, SIGSEGV); ++ sigdelset(&set, SIGFPE); ++ sigdelset(&set, SIGILL); ++ /* TODO avoid SIGBUS loss on macOS */ ++ pthread_sigmask(SIG_SETMASK, &set, &oldset); ++ ++ qemu_thread_args = g_new0(QemuThreadArgs, 1); ++ qemu_thread_args->name = g_strdup(name); ++ qemu_thread_args->start_routine = start_routine; ++ qemu_thread_args->arg = arg; ++ ++ err = pthread_create(&thread->thread, &attr, ++ qemu_thread_start, qemu_thread_args); ++ ++ if (err) ++ error_exit(err, __func__); ++ ++ pthread_sigmask(SIG_SETMASK, &oldset, NULL); ++ ++ pthread_attr_destroy(&attr); ++} ++ ++int qemu_thread_set_affinity(QemuThread *thread, unsigned long *host_cpus, ++ unsigned long nbits) ++{ ++#if defined(CONFIG_PTHREAD_AFFINITY_NP) ++ const size_t setsize = CPU_ALLOC_SIZE(nbits); ++ unsigned long value; ++ cpu_set_t *cpuset; ++ int err; ++ ++ cpuset = CPU_ALLOC(nbits); ++ g_assert(cpuset); ++ ++ CPU_ZERO_S(setsize, cpuset); ++ value = find_first_bit(host_cpus, nbits); ++ while (value < nbits) { ++ CPU_SET_S(value, setsize, cpuset); ++ value = find_next_bit(host_cpus, nbits, value + 1); ++ } ++ ++ err = pthread_setaffinity_np(thread->thread, setsize, cpuset); ++ CPU_FREE(cpuset); ++ return err; ++#else ++ return -ENOSYS; ++#endif ++} ++ ++int qemu_thread_get_affinity(QemuThread *thread, unsigned long **host_cpus, ++ unsigned long *nbits) ++{ ++#if defined(CONFIG_PTHREAD_AFFINITY_NP) ++ unsigned long tmpbits; ++ cpu_set_t *cpuset; ++ size_t setsize; ++ int i, err; ++ ++ tmpbits = CPU_SETSIZE; ++ while (true) { ++ setsize = CPU_ALLOC_SIZE(tmpbits); ++ cpuset = CPU_ALLOC(tmpbits); ++ g_assert(cpuset); ++ ++ err = pthread_getaffinity_np(thread->thread, setsize, cpuset); ++ if (err) { ++ CPU_FREE(cpuset); ++ if (err != -EINVAL) { ++ return err; ++ } ++ tmpbits *= 2; ++ } else { ++ break; ++ } ++ } ++ ++ /* Convert the result into a proper bitmap. */ ++ *nbits = tmpbits; ++ *host_cpus = bitmap_new(tmpbits); ++ for (i = 0; i < tmpbits; i++) { ++ if (CPU_ISSET(i, cpuset)) { ++ set_bit(i, *host_cpus); ++ } ++ } ++ CPU_FREE(cpuset); ++ return 0; ++#else ++ return -ENOSYS; ++#endif ++} ++ ++void qemu_thread_get_self(QemuThread *thread) ++{ ++ thread->thread = pthread_self(); ++} ++ ++bool qemu_thread_is_self(QemuThread *thread) ++{ ++ return pthread_equal(pthread_self(), thread->thread); ++} ++ ++void qemu_thread_exit(void *retval) ++{ ++ pthread_exit(retval); ++} ++ ++void *qemu_thread_join(QemuThread *thread) ++{ ++ int err; ++ void *ret; ++ ++ err = pthread_join(thread->thread, &ret); ++ if (err) { ++ error_exit(err, __func__); ++ } ++ return ret; ++} +diff --git a/qcow2/lib/util/qemu-timer.c b/qcow2/lib/util/qemu-timer.c +new file mode 100644 +index 00000000..6b1533bc +--- /dev/null ++++ b/qcow2/lib/util/qemu-timer.c +@@ -0,0 +1,713 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/main-loop.h" ++#include "qemu/timer.h" ++#include "qemu/lockable.h" ++#include "sysemu/cpu-timers.h" ++#include "sysemu/replay.h" ++#include "sysemu/cpus.h" ++ ++#ifdef CONFIG_POSIX ++#include ++#endif ++ ++#ifdef CONFIG_PPOLL ++#include ++#endif ++ ++#ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK ++#include ++#endif ++ ++/***********************************************************/ ++/* timers */ ++ ++typedef struct QEMUClock { ++ /* We rely on BQL to protect the timerlists */ ++ QLIST_HEAD(, QEMUTimerList) timerlists; ++ ++ QEMUClockType type; ++ bool enabled; ++} QEMUClock; ++ ++QEMUTimerListGroup main_loop_tlg; ++static QEMUClock qemu_clocks[QEMU_CLOCK_MAX]; ++ ++/* A QEMUTimerList is a list of timers attached to a clock. More ++ * than one QEMUTimerList can be attached to each clock, for instance ++ * used by different AioContexts / threads. Each clock also has ++ * a list of the QEMUTimerLists associated with it, in order that ++ * reenabling the clock can call all the notifiers. ++ */ ++ ++struct QEMUTimerList { ++ QEMUClock *clock; ++ QemuMutex active_timers_lock; ++ QEMUTimer *active_timers; ++ QLIST_ENTRY(QEMUTimerList) list; ++ QEMUTimerListNotifyCB *notify_cb; ++ void *notify_opaque; ++ ++ /* lightweight method to mark the end of timerlist's running */ ++ QemuEvent timers_done_ev; ++}; ++ ++/** ++ * qemu_clock_ptr: ++ * @type: type of clock ++ * ++ * Translate a clock type into a pointer to QEMUClock object. ++ * ++ * Returns: a pointer to the QEMUClock object ++ */ ++static inline QEMUClock *qemu_clock_ptr(QEMUClockType type) ++{ ++ return &qemu_clocks[type]; ++} ++ ++static bool timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) ++{ ++ return timer_head && (timer_head->expire_time <= current_time); ++} ++ ++QEMUTimerList *timerlist_new(QEMUClockType type, ++ QEMUTimerListNotifyCB *cb, ++ void *opaque) ++{ ++ QEMUTimerList *timer_list; ++ QEMUClock *clock = qemu_clock_ptr(type); ++ ++ timer_list = g_new0(QEMUTimerList, 1); ++ qemu_event_init(&timer_list->timers_done_ev, true); ++ timer_list->clock = clock; ++ timer_list->notify_cb = cb; ++ timer_list->notify_opaque = opaque; ++ qemu_mutex_init(&timer_list->active_timers_lock); ++ QLIST_INSERT_HEAD(&clock->timerlists, timer_list, list); ++ return timer_list; ++} ++ ++void timerlist_free(QEMUTimerList *timer_list) ++{ ++ assert(!timerlist_has_timers(timer_list)); ++ if (timer_list->clock) { ++ QLIST_REMOVE(timer_list, list); ++ } ++ qemu_mutex_destroy(&timer_list->active_timers_lock); ++ g_free(timer_list); ++} ++ ++static void qemu_clock_init(QEMUClockType type, QEMUTimerListNotifyCB *notify_cb) ++{ ++ QEMUClock *clock = qemu_clock_ptr(type); ++ ++ /* Assert that the clock of type TYPE has not been initialized yet. */ ++ assert(main_loop_tlg.tl[type] == NULL); ++ ++ clock->type = type; ++ clock->enabled = (type == QEMU_CLOCK_VIRTUAL ? false : true); ++ QLIST_INIT(&clock->timerlists); ++ main_loop_tlg.tl[type] = timerlist_new(type, notify_cb, NULL); ++} ++ ++bool qemu_clock_use_for_deadline(QEMUClockType type) ++{ ++ return !(icount_enabled() && (type == QEMU_CLOCK_VIRTUAL)); ++} ++ ++void qemu_clock_notify(QEMUClockType type) ++{ ++ QEMUTimerList *timer_list; ++ QEMUClock *clock = qemu_clock_ptr(type); ++ QLIST_FOREACH(timer_list, &clock->timerlists, list) { ++ timerlist_notify(timer_list); ++ } ++} ++ ++/* Disabling the clock will wait for related timerlists to stop ++ * executing qemu_run_timers. Thus, this functions should not ++ * be used from the callback of a timer that is based on @clock. ++ * Doing so would cause a deadlock. ++ * ++ * Caller should hold BQL. ++ */ ++void qemu_clock_enable(QEMUClockType type, bool enabled) ++{ ++ QEMUClock *clock = qemu_clock_ptr(type); ++ QEMUTimerList *tl; ++ bool old = clock->enabled; ++ clock->enabled = enabled; ++ if (enabled && !old) { ++ qemu_clock_notify(type); ++ } else if (!enabled && old) { ++ QLIST_FOREACH(tl, &clock->timerlists, list) { ++ qemu_event_wait(&tl->timers_done_ev); ++ } ++ } ++} ++ ++bool timerlist_has_timers(QEMUTimerList *timer_list) ++{ ++ return !!qatomic_read(&timer_list->active_timers); ++} ++ ++bool qemu_clock_has_timers(QEMUClockType type) ++{ ++ return timerlist_has_timers( ++ main_loop_tlg.tl[type]); ++} ++ ++bool timerlist_expired(QEMUTimerList *timer_list) ++{ ++ int64_t expire_time; ++ ++ if (!qatomic_read(&timer_list->active_timers)) { ++ return false; ++ } ++ ++ WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { ++ if (!timer_list->active_timers) { ++ return false; ++ } ++ expire_time = timer_list->active_timers->expire_time; ++ } ++ ++ return expire_time <= qemu_clock_get_ns(timer_list->clock->type); ++} ++ ++bool qemu_clock_expired(QEMUClockType type) ++{ ++ return timerlist_expired( ++ main_loop_tlg.tl[type]); ++} ++ ++/* ++ * As above, but return -1 for no deadline, and do not cap to 2^32 ++ * as we know the result is always positive. ++ */ ++ ++int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) ++{ ++ int64_t delta; ++ int64_t expire_time; ++ ++ if (!qatomic_read(&timer_list->active_timers)) { ++ return -1; ++ } ++ ++ if (!timer_list->clock->enabled) { ++ return -1; ++ } ++ ++ /* The active timers list may be modified before the caller uses our return ++ * value but ->notify_cb() is called when the deadline changes. Therefore ++ * the caller should notice the change and there is no race condition. ++ */ ++ WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { ++ if (!timer_list->active_timers) { ++ return -1; ++ } ++ expire_time = timer_list->active_timers->expire_time; ++ } ++ ++ delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); ++ ++ if (delta <= 0) { ++ return 0; ++ } ++ ++ return delta; ++} ++ ++/* Calculate the soonest deadline across all timerlists attached ++ * to the clock. This is used for the icount timeout so we ++ * ignore whether or not the clock should be used in deadline ++ * calculations. ++ */ ++int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) ++{ ++ int64_t deadline = -1; ++ int64_t delta; ++ int64_t expire_time; ++ QEMUTimer *ts; ++ QEMUTimerList *timer_list; ++ QEMUClock *clock = qemu_clock_ptr(type); ++ ++ if (!clock->enabled) { ++ return -1; ++ } ++ ++ QLIST_FOREACH(timer_list, &clock->timerlists, list) { ++ if (!qatomic_read(&timer_list->active_timers)) { ++ continue; ++ } ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ ts = timer_list->active_timers; ++ /* Skip all external timers */ ++ while (ts && (ts->attributes & ~attr_mask)) { ++ ts = ts->next; ++ } ++ if (!ts) { ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ continue; ++ } ++ expire_time = ts->expire_time; ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ ++ delta = expire_time - qemu_clock_get_ns(type); ++ if (delta <= 0) { ++ delta = 0; ++ } ++ deadline = qemu_soonest_timeout(deadline, delta); ++ } ++ return deadline; ++} ++ ++QEMUClockType timerlist_get_clock(QEMUTimerList *timer_list) ++{ ++ return timer_list->clock->type; ++} ++ ++QEMUTimerList *qemu_clock_get_main_loop_timerlist(QEMUClockType type) ++{ ++ return main_loop_tlg.tl[type]; ++} ++ ++void timerlist_notify(QEMUTimerList *timer_list) ++{ ++ if (timer_list->notify_cb) { ++ timer_list->notify_cb(timer_list->notify_opaque, timer_list->clock->type); ++ } else { ++ qemu_notify_event(); ++ } ++} ++ ++/* Transition function to convert a nanosecond timeout to ms ++ * This is used where a system does not support ppoll ++ */ ++int qemu_timeout_ns_to_ms(int64_t ns) ++{ ++ int64_t ms; ++ if (ns < 0) { ++ return -1; ++ } ++ ++ if (!ns) { ++ return 0; ++ } ++ ++ /* Always round up, because it's better to wait too long than to wait too ++ * little and effectively busy-wait ++ */ ++ ms = DIV_ROUND_UP(ns, SCALE_MS); ++ ++ /* To avoid overflow problems, limit this to 2^31, i.e. approx 25 days */ ++ return MIN(ms, INT32_MAX); ++} ++ ++ ++/* qemu implementation of g_poll which uses a nanosecond timeout but is ++ * otherwise identical to g_poll ++ */ ++int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout) ++{ ++#ifdef CONFIG_PPOLL ++ if (timeout < 0) { ++ return ppoll((struct pollfd *)fds, nfds, NULL, NULL); ++ } else { ++ struct timespec ts; ++ int64_t tvsec = timeout / 1000000000LL; ++ /* Avoid possibly overflowing and specifying a negative number of ++ * seconds, which would turn a very long timeout into a busy-wait. ++ */ ++ if (tvsec > (int64_t)INT32_MAX) { ++ tvsec = INT32_MAX; ++ } ++ ts.tv_sec = tvsec; ++ ts.tv_nsec = timeout % 1000000000LL; ++ return ppoll((struct pollfd *)fds, nfds, &ts, NULL); ++ } ++#else ++ return g_poll(fds, nfds, qemu_timeout_ns_to_ms(timeout)); ++#endif ++} ++ ++ ++void timer_init_full(QEMUTimer *ts, ++ QEMUTimerListGroup *timer_list_group, QEMUClockType type, ++ int scale, int attributes, ++ QEMUTimerCB *cb, void *opaque) ++{ ++ if (!timer_list_group) { ++ timer_list_group = &main_loop_tlg; ++ } ++ ts->timer_list = timer_list_group->tl[type]; ++ ts->cb = cb; ++ ts->opaque = opaque; ++ ts->scale = scale; ++ ts->attributes = attributes; ++ ts->expire_time = -1; ++} ++ ++void timer_deinit(QEMUTimer *ts) ++{ ++ assert(ts->expire_time == -1); ++ ts->timer_list = NULL; ++} ++ ++static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts) ++{ ++ QEMUTimer **pt, *t; ++ ++ ts->expire_time = -1; ++ pt = &timer_list->active_timers; ++ for(;;) { ++ t = *pt; ++ if (!t) ++ break; ++ if (t == ts) { ++ qatomic_set(pt, t->next); ++ break; ++ } ++ pt = &t->next; ++ } ++} ++ ++static bool timer_mod_ns_locked(QEMUTimerList *timer_list, ++ QEMUTimer *ts, int64_t expire_time) ++{ ++ QEMUTimer **pt, *t; ++ ++ /* add the timer in the sorted list */ ++ pt = &timer_list->active_timers; ++ for (;;) { ++ t = *pt; ++ if (!timer_expired_ns(t, expire_time)) { ++ break; ++ } ++ pt = &t->next; ++ } ++ ts->expire_time = MAX(expire_time, 0); ++ ts->next = *pt; ++ qatomic_set(pt, ts); ++ ++ return pt == &timer_list->active_timers; ++} ++ ++static void timerlist_rearm(QEMUTimerList *timer_list) ++{ ++ /* Interrupt execution to force deadline recalculation. */ ++ if (icount_enabled() && timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { ++ icount_start_warp_timer(); ++ } ++ timerlist_notify(timer_list); ++} ++ ++/* stop a timer, but do not dealloc it */ ++void timer_del(QEMUTimer *ts) ++{ ++ QEMUTimerList *timer_list = ts->timer_list; ++ ++ if (timer_list) { ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ timer_del_locked(timer_list, ts); ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ } ++} ++ ++/* modify the current timer so that it will be fired when current_time ++ >= expire_time. The corresponding callback will be called. */ ++void timer_mod_ns(QEMUTimer *ts, int64_t expire_time) ++{ ++ QEMUTimerList *timer_list = ts->timer_list; ++ bool rearm; ++ ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ timer_del_locked(timer_list, ts); ++ rearm = timer_mod_ns_locked(timer_list, ts, expire_time); ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ ++ if (rearm) { ++ timerlist_rearm(timer_list); ++ } ++} ++ ++/* modify the current timer so that it will be fired when current_time ++ >= expire_time or the current deadline, whichever comes earlier. ++ The corresponding callback will be called. */ ++void timer_mod_anticipate_ns(QEMUTimer *ts, int64_t expire_time) ++{ ++ QEMUTimerList *timer_list = ts->timer_list; ++ bool rearm; ++ ++ WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { ++ if (ts->expire_time == -1 || ts->expire_time > expire_time) { ++ if (ts->expire_time != -1) { ++ timer_del_locked(timer_list, ts); ++ } ++ rearm = timer_mod_ns_locked(timer_list, ts, expire_time); ++ } else { ++ rearm = false; ++ } ++ } ++ if (rearm) { ++ timerlist_rearm(timer_list); ++ } ++} ++ ++void timer_mod(QEMUTimer *ts, int64_t expire_time) ++{ ++ timer_mod_ns(ts, expire_time * ts->scale); ++} ++ ++void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time) ++{ ++ timer_mod_anticipate_ns(ts, expire_time * ts->scale); ++} ++ ++bool timer_pending(QEMUTimer *ts) ++{ ++ return ts->expire_time >= 0; ++} ++ ++bool timer_expired(QEMUTimer *timer_head, int64_t current_time) ++{ ++ return timer_expired_ns(timer_head, current_time * timer_head->scale); ++} ++ ++bool timerlist_run_timers(QEMUTimerList *timer_list) ++{ ++ QEMUTimer *ts; ++ int64_t current_time; ++ bool progress = false; ++ QEMUTimerCB *cb; ++ void *opaque; ++ ++ if (!qatomic_read(&timer_list->active_timers)) { ++ return false; ++ } ++ ++ qemu_event_reset(&timer_list->timers_done_ev); ++ if (!timer_list->clock->enabled) { ++ goto out; ++ } ++ ++ switch (timer_list->clock->type) { ++ case QEMU_CLOCK_REALTIME: ++ break; ++ default: ++ case QEMU_CLOCK_VIRTUAL: ++ break; ++ case QEMU_CLOCK_HOST: ++ if (!replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { ++ goto out; ++ } ++ break; ++ case QEMU_CLOCK_VIRTUAL_RT: ++ if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL_RT)) { ++ goto out; ++ } ++ break; ++ } ++ ++ /* ++ * Extract expired timers from active timers list and process them. ++ * ++ * In rr mode we need "filtered" checkpointing for virtual clock. The ++ * checkpoint must be recorded/replayed before processing any non-EXTERNAL timer, ++ * and that must only be done once since the clock value stays the same. Because ++ * non-EXTERNAL timers may appear in the timers list while it being processed, ++ * the checkpoint can be issued at a time until no timers are left and we are ++ * done". ++ */ ++ current_time = qemu_clock_get_ns(timer_list->clock->type); ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ while ((ts = timer_list->active_timers)) { ++ if (!timer_expired_ns(ts, current_time)) { ++ /* No expired timers left. The checkpoint can be skipped ++ * if no timers fired or they were all external. ++ */ ++ break; ++ } ++ /* Checkpoint for virtual clock is redundant in cases where ++ * it's being triggered with only non-EXTERNAL timers, because ++ * these timers don't change guest state directly. ++ */ ++ if (replay_mode != REPLAY_MODE_NONE ++ && timer_list->clock->type == QEMU_CLOCK_VIRTUAL ++ && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL) ++ && !replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ goto out; ++ } ++ ++ /* remove timer from the list before calling the callback */ ++ timer_list->active_timers = ts->next; ++ ts->next = NULL; ++ ts->expire_time = -1; ++ cb = ts->cb; ++ opaque = ts->opaque; ++ ++ /* run the callback (the timer list can be modified) */ ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ cb(opaque); ++ qemu_mutex_lock(&timer_list->active_timers_lock); ++ ++ progress = true; ++ } ++ qemu_mutex_unlock(&timer_list->active_timers_lock); ++ ++out: ++ qemu_event_set(&timer_list->timers_done_ev); ++ return progress; ++} ++ ++bool qemu_clock_run_timers(QEMUClockType type) ++{ ++ return timerlist_run_timers(main_loop_tlg.tl[type]); ++} ++ ++void timerlistgroup_init(QEMUTimerListGroup *tlg, ++ QEMUTimerListNotifyCB *cb, void *opaque) ++{ ++ QEMUClockType type; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ tlg->tl[type] = timerlist_new(type, cb, opaque); ++ } ++} ++ ++void timerlistgroup_deinit(QEMUTimerListGroup *tlg) ++{ ++ QEMUClockType type; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ timerlist_free(tlg->tl[type]); ++ } ++} ++ ++bool timerlistgroup_run_timers(QEMUTimerListGroup *tlg) ++{ ++ QEMUClockType type; ++ bool progress = false; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ progress |= timerlist_run_timers(tlg->tl[type]); ++ } ++ return progress; ++} ++ ++int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) ++{ ++ int64_t deadline = -1; ++ QEMUClockType type; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ if (qemu_clock_use_for_deadline(type)) { ++ deadline = qemu_soonest_timeout(deadline, ++ timerlist_deadline_ns(tlg->tl[type])); ++ } ++ } ++ return deadline; ++} ++ ++int64_t qemu_clock_get_ns(QEMUClockType type) ++{ ++ switch (type) { ++ case QEMU_CLOCK_REALTIME: ++ return get_clock(); ++ default: ++ case QEMU_CLOCK_VIRTUAL: ++ return cpus_get_virtual_clock(); ++ case QEMU_CLOCK_HOST: ++ return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); ++ case QEMU_CLOCK_VIRTUAL_RT: ++ return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock()); ++ } ++} ++ ++static void qemu_virtual_clock_set_ns(int64_t time) ++{ ++ return cpus_set_virtual_clock(time); ++} ++ ++void init_clocks(QEMUTimerListNotifyCB *notify_cb) ++{ ++ QEMUClockType type; ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ qemu_clock_init(type, notify_cb); ++ } ++ ++#ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK ++ prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); ++#endif ++} ++ ++uint64_t timer_expire_time_ns(QEMUTimer *ts) ++{ ++ return timer_pending(ts) ? ts->expire_time : -1; ++} ++ ++bool qemu_clock_run_all_timers(void) ++{ ++ bool progress = false; ++ QEMUClockType type; ++ ++ for (type = 0; type < QEMU_CLOCK_MAX; type++) { ++ if (qemu_clock_use_for_deadline(type)) { ++ progress |= qemu_clock_run_timers(type); ++ } ++ } ++ ++ return progress; ++} ++ ++int64_t qemu_clock_advance_virtual_time(int64_t dest) ++{ ++ int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ AioContext *aio_context; ++ int64_t deadline; ++ ++ aio_context = qemu_get_aio_context(); ++ ++ deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, ++ QEMU_TIMER_ATTR_ALL); ++ /* ++ * A deadline of < 0 indicates this timer is not enabled, so we ++ * won't get far trying to run it forward. ++ */ ++ while (deadline >= 0 && clock < dest) { ++ int64_t warp = qemu_soonest_timeout(dest - clock, deadline); ++ ++ qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp); ++ ++ qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); ++ timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); ++ clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ++ ++ deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, ++ QEMU_TIMER_ATTR_ALL); ++ } ++ qemu_clock_notify(QEMU_CLOCK_VIRTUAL); ++ ++ return clock; ++} +diff --git a/qcow2/lib/util/qsp.c b/qcow2/lib/util/qsp.c +new file mode 100644 +index 00000000..6b783e2e +--- /dev/null ++++ b/qcow2/lib/util/qsp.c +@@ -0,0 +1,813 @@ ++/* ++ * qsp.c - QEMU Synchronization Profiler ++ * ++ * Copyright (C) 2018, Emilio G. Cota ++ * ++ * License: GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ * QSP profiles the time spent in synchronization primitives, which can ++ * help diagnose performance problems, e.g. scalability issues when ++ * contention is high. ++ * ++ * The primitives currently supported are mutexes, recursive mutexes and ++ * condition variables. Note that not all related functions are intercepted; ++ * instead we profile only those functions that can have a performance impact, ++ * either due to blocking (e.g. cond_wait, mutex_lock) or cache line ++ * contention (e.g. mutex_lock, mutex_trylock). ++ * ++ * QSP's design focuses on speed and scalability. This is achieved ++ * by having threads do their profiling entirely on thread-local data. ++ * The appropriate thread-local data is found via a QHT, i.e. a concurrent hash ++ * table. To aggregate data in order to generate a report, we iterate over ++ * all entries in the hash table. Depending on the number of threads and ++ * synchronization objects this might be expensive, but note that it is ++ * very rarely called -- reports are generated only when requested by users. ++ * ++ * Reports are generated as a table where each row represents a call site. A ++ * call site is the triplet formed by the __file__ and __LINE__ of the caller ++ * as well as the address of the "object" (i.e. mutex, rec. mutex or condvar) ++ * being operated on. Optionally, call sites that operate on different objects ++ * of the same type can be coalesced, which can be particularly useful when ++ * profiling dynamically-allocated objects. ++ * ++ * Alternative designs considered: ++ * ++ * - Use an off-the-shelf profiler such as mutrace. This is not a viable option ++ * for us because QEMU has __malloc_hook set (by one of the libraries it ++ * uses); leaving this hook unset is required to avoid deadlock in mutrace. ++ * ++ * - Use a glib HT for each thread, protecting each HT with its own lock. ++ * This isn't simpler than the current design, and is 10% slower in the ++ * atomic_add-bench microbenchmark (-m option). ++ * ++ * - For reports, just use a binary tree as we aggregate data, instead of having ++ * an intermediate hash table. This would simplify the code only slightly, but ++ * would perform badly if there were many threads and objects to track. ++ * ++ * - Wrap operations on qsp entries with RCU read-side critical sections, so ++ * that qsp_reset() can delete entries. Unfortunately, the overhead of calling ++ * rcu_read_lock/unlock slows down atomic_add-bench -m by 24%. Having ++ * a snapshot that is updated on qsp_reset() avoids this overhead. ++ * ++ * Related Work: ++ * - Lennart Poettering's mutrace: http://0pointer.de/blog/projects/mutrace.html ++ * - Lozi, David, Thomas, Lawall and Muller. "Remote Core Locking: Migrating ++ * Critical-Section Execution to Improve the Performance of Multithreaded ++ * Applications", USENIX ATC'12. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/qemu-print.h" ++#include "qemu/thread.h" ++#include "qemu/timer.h" ++#include "qemu/qht.h" ++#include "qemu/rcu.h" ++#include "qemu/xxhash.h" ++ ++enum QSPType { ++ QSP_MUTEX, ++ QSP_BQL_MUTEX, ++ QSP_REC_MUTEX, ++ QSP_CONDVAR, ++}; ++ ++struct QSPCallSite { ++ const void *obj; ++ const char *file; /* i.e. __FILE__; shortened later */ ++ int line; ++ enum QSPType type; ++}; ++typedef struct QSPCallSite QSPCallSite; ++ ++struct QSPEntry { ++ void *thread_ptr; ++ const QSPCallSite *callsite; ++ aligned_uint64_t n_acqs; ++ aligned_uint64_t ns; ++ unsigned int n_objs; /* count of coalesced objs; only used for reporting */ ++}; ++typedef struct QSPEntry QSPEntry; ++ ++struct QSPSnapshot { ++ struct rcu_head rcu; ++ struct qht ht; ++}; ++typedef struct QSPSnapshot QSPSnapshot; ++ ++/* initial sizing for hash tables */ ++#define QSP_INITIAL_SIZE 64 ++ ++/* If this file is moved, QSP_REL_PATH should be updated accordingly */ ++#define QSP_REL_PATH "util/qsp.c" ++ ++/* this file's full path. Used to present all call sites with relative paths */ ++static size_t qsp_qemu_path_len; ++ ++/* the address of qsp_thread gives us a unique 'thread ID' */ ++static __thread int qsp_thread; ++ ++/* ++ * Call sites are the same for all threads, so we track them in a separate hash ++ * table to save memory. ++ */ ++static struct qht qsp_callsite_ht; ++ ++static struct qht qsp_ht; ++static QSPSnapshot *qsp_snapshot; ++static bool qsp_initialized, qsp_initializing; ++ ++static const char * const qsp_typenames[] = { ++ [QSP_MUTEX] = "mutex", ++ [QSP_BQL_MUTEX] = "BQL mutex", ++ [QSP_REC_MUTEX] = "rec_mutex", ++ [QSP_CONDVAR] = "condvar", ++}; ++ ++QemuMutexLockFunc bql_mutex_lock_func = qemu_mutex_lock_impl; ++QemuMutexLockFunc qemu_mutex_lock_func = qemu_mutex_lock_impl; ++QemuMutexTrylockFunc qemu_mutex_trylock_func = qemu_mutex_trylock_impl; ++QemuRecMutexLockFunc qemu_rec_mutex_lock_func = qemu_rec_mutex_lock_impl; ++QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func = ++ qemu_rec_mutex_trylock_impl; ++QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl; ++QemuCondTimedWaitFunc qemu_cond_timedwait_func = qemu_cond_timedwait_impl; ++ ++/* ++ * It pays off to _not_ hash callsite->file; hashing a string is slow, and ++ * without it we still get a pretty unique hash. ++ */ ++static inline ++uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t ab) ++{ ++ uint64_t cd = (uint64_t)(uintptr_t)callsite->obj; ++ uint32_t e = callsite->line; ++ uint32_t f = callsite->type; ++ ++ return qemu_xxhash8(ab, cd, 0, e, f); ++} ++ ++static inline ++uint32_t qsp_callsite_hash(const QSPCallSite *callsite) ++{ ++ return do_qsp_callsite_hash(callsite, 0); ++} ++ ++static inline uint32_t do_qsp_entry_hash(const QSPEntry *entry, uint64_t a) ++{ ++ return do_qsp_callsite_hash(entry->callsite, a); ++} ++ ++static uint32_t qsp_entry_hash(const QSPEntry *entry) ++{ ++ return do_qsp_entry_hash(entry, (uint64_t)(uintptr_t)entry->thread_ptr); ++} ++ ++static uint32_t qsp_entry_no_thread_hash(const QSPEntry *entry) ++{ ++ return do_qsp_entry_hash(entry, 0); ++} ++ ++/* without the objects we need to hash the file name to get a decent hash */ ++static uint32_t qsp_entry_no_thread_obj_hash(const QSPEntry *entry) ++{ ++ const QSPCallSite *callsite = entry->callsite; ++ uint64_t ab = g_str_hash(callsite->file); ++ uint64_t cd = callsite->line; ++ uint32_t e = callsite->type; ++ ++ return qemu_xxhash5(ab, cd, e); ++} ++ ++static bool qsp_callsite_cmp(const void *ap, const void *bp) ++{ ++ const QSPCallSite *a = ap; ++ const QSPCallSite *b = bp; ++ ++ return a == b || ++ (a->obj == b->obj && ++ a->line == b->line && ++ a->type == b->type && ++ (a->file == b->file || !strcmp(a->file, b->file))); ++} ++ ++static bool qsp_callsite_no_obj_cmp(const void *ap, const void *bp) ++{ ++ const QSPCallSite *a = ap; ++ const QSPCallSite *b = bp; ++ ++ return a == b || ++ (a->line == b->line && ++ a->type == b->type && ++ (a->file == b->file || !strcmp(a->file, b->file))); ++} ++ ++static bool qsp_entry_no_thread_cmp(const void *ap, const void *bp) ++{ ++ const QSPEntry *a = ap; ++ const QSPEntry *b = bp; ++ ++ return qsp_callsite_cmp(a->callsite, b->callsite); ++} ++ ++static bool qsp_entry_no_thread_obj_cmp(const void *ap, const void *bp) ++{ ++ const QSPEntry *a = ap; ++ const QSPEntry *b = bp; ++ ++ return qsp_callsite_no_obj_cmp(a->callsite, b->callsite); ++} ++ ++static bool qsp_entry_cmp(const void *ap, const void *bp) ++{ ++ const QSPEntry *a = ap; ++ const QSPEntry *b = bp; ++ ++ return a->thread_ptr == b->thread_ptr && ++ qsp_callsite_cmp(a->callsite, b->callsite); ++} ++ ++/* ++ * Normally we'd call this from a constructor function, but we want it to work ++ * via libutil as well. ++ */ ++static void qsp_do_init(void) ++{ ++ /* make sure this file's path in the tree is up to date with QSP_REL_PATH */ ++ g_assert(strstr(__FILE__, QSP_REL_PATH)); ++ qsp_qemu_path_len = strlen(__FILE__) - strlen(QSP_REL_PATH); ++ ++ qht_init(&qsp_ht, qsp_entry_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++ qht_init(&qsp_callsite_ht, qsp_callsite_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++} ++ ++static __attribute__((noinline)) void qsp_init__slowpath(void) ++{ ++ if (qatomic_cmpxchg(&qsp_initializing, false, true) == false) { ++ qsp_do_init(); ++ qatomic_set(&qsp_initialized, true); ++ } else { ++ while (!qatomic_read(&qsp_initialized)) { ++ cpu_relax(); ++ } ++ } ++} ++ ++/* qsp_init() must be called from _all_ exported functions */ ++static inline void qsp_init(void) ++{ ++ if (likely(qatomic_read(&qsp_initialized))) { ++ return; ++ } ++ qsp_init__slowpath(); ++} ++ ++static QSPCallSite *qsp_callsite_find(const QSPCallSite *orig) ++{ ++ QSPCallSite *callsite; ++ uint32_t hash; ++ ++ hash = qsp_callsite_hash(orig); ++ callsite = qht_lookup(&qsp_callsite_ht, orig, hash); ++ if (callsite == NULL) { ++ void *existing = NULL; ++ ++ callsite = g_new(QSPCallSite, 1); ++ memcpy(callsite, orig, sizeof(*callsite)); ++ qht_insert(&qsp_callsite_ht, callsite, hash, &existing); ++ if (unlikely(existing)) { ++ g_free(callsite); ++ callsite = existing; ++ } ++ } ++ return callsite; ++} ++ ++static QSPEntry * ++qsp_entry_create(struct qht *ht, const QSPEntry *entry, uint32_t hash) ++{ ++ QSPEntry *e; ++ void *existing = NULL; ++ ++ e = g_new0(QSPEntry, 1); ++ e->thread_ptr = entry->thread_ptr; ++ e->callsite = qsp_callsite_find(entry->callsite); ++ ++ qht_insert(ht, e, hash, &existing); ++ if (unlikely(existing)) { ++ g_free(e); ++ e = existing; ++ } ++ return e; ++} ++ ++static QSPEntry * ++qsp_entry_find(struct qht *ht, const QSPEntry *entry, uint32_t hash) ++{ ++ QSPEntry *e; ++ ++ e = qht_lookup(ht, entry, hash); ++ if (e == NULL) { ++ e = qsp_entry_create(ht, entry, hash); ++ } ++ return e; ++} ++ ++/* ++ * Note: Entries are never removed, so callers do not have to be in an RCU ++ * read-side critical section. ++ */ ++static QSPEntry *qsp_entry_get(const void *obj, const char *file, int line, ++ enum QSPType type) ++{ ++ QSPCallSite callsite = { ++ .obj = obj, ++ .file = file, ++ .line = line, ++ .type = type, ++ }; ++ QSPEntry orig; ++ uint32_t hash; ++ ++ qsp_init(); ++ ++ orig.thread_ptr = &qsp_thread; ++ orig.callsite = &callsite; ++ ++ hash = qsp_entry_hash(&orig); ++ return qsp_entry_find(&qsp_ht, &orig, hash); ++} ++ ++/* ++ * @e is in the global hash table; it is only written to by the current thread, ++ * so we write to it atomically (as in "write once") to prevent torn reads. ++ */ ++static inline void do_qsp_entry_record(QSPEntry *e, int64_t delta, bool acq) ++{ ++ qatomic_set_u64(&e->ns, e->ns + delta); ++ if (acq) { ++ qatomic_set_u64(&e->n_acqs, e->n_acqs + 1); ++ } ++} ++ ++static inline void qsp_entry_record(QSPEntry *e, int64_t delta) ++{ ++ do_qsp_entry_record(e, delta, true); ++} ++ ++#define QSP_GEN_VOID(type_, qsp_t_, func_, impl_) \ ++ static void func_(type_ *obj, const char *file, int line) \ ++ { \ ++ QSPEntry *e; \ ++ int64_t t0, t1; \ ++ \ ++ t0 = get_clock(); \ ++ impl_(obj, file, line); \ ++ t1 = get_clock(); \ ++ \ ++ e = qsp_entry_get(obj, file, line, qsp_t_); \ ++ qsp_entry_record(e, t1 - t0); \ ++ } ++ ++#define QSP_GEN_RET1(type_, qsp_t_, func_, impl_) \ ++ static int func_(type_ *obj, const char *file, int line) \ ++ { \ ++ QSPEntry *e; \ ++ int64_t t0, t1; \ ++ int err; \ ++ \ ++ t0 = get_clock(); \ ++ err = impl_(obj, file, line); \ ++ t1 = get_clock(); \ ++ \ ++ e = qsp_entry_get(obj, file, line, qsp_t_); \ ++ do_qsp_entry_record(e, t1 - t0, !err); \ ++ return err; \ ++ } ++ ++QSP_GEN_VOID(QemuMutex, QSP_BQL_MUTEX, qsp_bql_mutex_lock, qemu_mutex_lock_impl) ++QSP_GEN_VOID(QemuMutex, QSP_MUTEX, qsp_mutex_lock, qemu_mutex_lock_impl) ++QSP_GEN_RET1(QemuMutex, QSP_MUTEX, qsp_mutex_trylock, qemu_mutex_trylock_impl) ++ ++QSP_GEN_VOID(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_lock, ++ qemu_rec_mutex_lock_impl) ++QSP_GEN_RET1(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_trylock, ++ qemu_rec_mutex_trylock_impl) ++ ++#undef QSP_GEN_RET1 ++#undef QSP_GEN_VOID ++ ++static void ++qsp_cond_wait(QemuCond *cond, QemuMutex *mutex, const char *file, int line) ++{ ++ QSPEntry *e; ++ int64_t t0, t1; ++ ++ t0 = get_clock(); ++ qemu_cond_wait_impl(cond, mutex, file, line); ++ t1 = get_clock(); ++ ++ e = qsp_entry_get(cond, file, line, QSP_CONDVAR); ++ qsp_entry_record(e, t1 - t0); ++} ++ ++static bool ++qsp_cond_timedwait(QemuCond *cond, QemuMutex *mutex, int ms, ++ const char *file, int line) ++{ ++ QSPEntry *e; ++ int64_t t0, t1; ++ bool ret; ++ ++ t0 = get_clock(); ++ ret = qemu_cond_timedwait_impl(cond, mutex, ms, file, line); ++ t1 = get_clock(); ++ ++ e = qsp_entry_get(cond, file, line, QSP_CONDVAR); ++ qsp_entry_record(e, t1 - t0); ++ return ret; ++} ++ ++bool qsp_is_enabled(void) ++{ ++ return qatomic_read(&qemu_mutex_lock_func) == qsp_mutex_lock; ++} ++ ++void qsp_enable(void) ++{ ++ qatomic_set(&qemu_mutex_lock_func, qsp_mutex_lock); ++ qatomic_set(&qemu_mutex_trylock_func, qsp_mutex_trylock); ++ qatomic_set(&bql_mutex_lock_func, qsp_bql_mutex_lock); ++ qatomic_set(&qemu_rec_mutex_lock_func, qsp_rec_mutex_lock); ++ qatomic_set(&qemu_rec_mutex_trylock_func, qsp_rec_mutex_trylock); ++ qatomic_set(&qemu_cond_wait_func, qsp_cond_wait); ++ qatomic_set(&qemu_cond_timedwait_func, qsp_cond_timedwait); ++} ++ ++void qsp_disable(void) ++{ ++ qatomic_set(&qemu_mutex_lock_func, qemu_mutex_lock_impl); ++ qatomic_set(&qemu_mutex_trylock_func, qemu_mutex_trylock_impl); ++ qatomic_set(&bql_mutex_lock_func, qemu_mutex_lock_impl); ++ qatomic_set(&qemu_rec_mutex_lock_func, qemu_rec_mutex_lock_impl); ++ qatomic_set(&qemu_rec_mutex_trylock_func, qemu_rec_mutex_trylock_impl); ++ qatomic_set(&qemu_cond_wait_func, qemu_cond_wait_impl); ++ qatomic_set(&qemu_cond_timedwait_func, qemu_cond_timedwait_impl); ++} ++ ++static gint qsp_tree_cmp(gconstpointer ap, gconstpointer bp, gpointer up) ++{ ++ const QSPEntry *a = ap; ++ const QSPEntry *b = bp; ++ enum QSPSortBy sort_by = *(enum QSPSortBy *)up; ++ const QSPCallSite *ca; ++ const QSPCallSite *cb; ++ ++ switch (sort_by) { ++ case QSP_SORT_BY_TOTAL_WAIT_TIME: ++ if (a->ns > b->ns) { ++ return -1; ++ } else if (a->ns < b->ns) { ++ return 1; ++ } ++ break; ++ case QSP_SORT_BY_AVG_WAIT_TIME: ++ { ++ double avg_a = a->n_acqs ? a->ns / a->n_acqs : 0; ++ double avg_b = b->n_acqs ? b->ns / b->n_acqs : 0; ++ ++ if (avg_a > avg_b) { ++ return -1; ++ } else if (avg_a < avg_b) { ++ return 1; ++ } ++ break; ++ } ++ default: ++ g_assert_not_reached(); ++ } ++ ++ ca = a->callsite; ++ cb = b->callsite; ++ /* Break the tie with the object's address */ ++ if (ca->obj < cb->obj) { ++ return -1; ++ } else if (ca->obj > cb->obj) { ++ return 1; ++ } else { ++ int cmp; ++ ++ /* same obj. Break the tie with the callsite's file */ ++ cmp = strcmp(ca->file, cb->file); ++ if (cmp) { ++ return cmp; ++ } ++ /* same callsite file. Break the tie with the callsite's line */ ++ g_assert(ca->line != cb->line); ++ if (ca->line < cb->line) { ++ return -1; ++ } else if (ca->line > cb->line) { ++ return 1; ++ } else { ++ /* break the tie with the callsite's type */ ++ return cb->type - ca->type; ++ } ++ } ++} ++ ++static void qsp_sort(void *p, uint32_t h, void *userp) ++{ ++ QSPEntry *e = p; ++ GTree *tree = userp; ++ ++ g_tree_insert(tree, e, NULL); ++} ++ ++static void qsp_aggregate(void *p, uint32_t h, void *up) ++{ ++ struct qht *ht = up; ++ const QSPEntry *e = p; ++ QSPEntry *agg; ++ uint32_t hash; ++ ++ hash = qsp_entry_no_thread_hash(e); ++ agg = qsp_entry_find(ht, e, hash); ++ /* ++ * The entry is in the global hash table; read from it atomically (as in ++ * "read once"). ++ */ ++ agg->ns += qatomic_read_u64(&e->ns); ++ agg->n_acqs += qatomic_read_u64(&e->n_acqs); ++} ++ ++static void qsp_iter_diff(void *p, uint32_t hash, void *htp) ++{ ++ struct qht *ht = htp; ++ QSPEntry *old = p; ++ QSPEntry *new; ++ ++ new = qht_lookup(ht, old, hash); ++ /* entries are never deleted, so we must have this one */ ++ g_assert(new != NULL); ++ /* our reading of the stats happened after the snapshot was taken */ ++ g_assert(new->n_acqs >= old->n_acqs); ++ g_assert(new->ns >= old->ns); ++ ++ new->n_acqs -= old->n_acqs; ++ new->ns -= old->ns; ++ ++ /* No point in reporting an empty entry */ ++ if (new->n_acqs == 0 && new->ns == 0) { ++ bool removed = qht_remove(ht, new, hash); ++ ++ g_assert(removed); ++ g_free(new); ++ } ++} ++ ++static void qsp_diff(struct qht *orig, struct qht *new) ++{ ++ qht_iter(orig, qsp_iter_diff, new); ++} ++ ++static void qsp_iter_callsite_coalesce(void *p, uint32_t h, void *htp) ++{ ++ struct qht *ht = htp; ++ QSPEntry *old = p; ++ QSPEntry *e; ++ uint32_t hash; ++ ++ hash = qsp_entry_no_thread_obj_hash(old); ++ e = qht_lookup(ht, old, hash); ++ if (e == NULL) { ++ e = qsp_entry_create(ht, old, hash); ++ e->n_objs = 1; ++ } else if (e->callsite->obj != old->callsite->obj) { ++ e->n_objs++; ++ } ++ e->ns += old->ns; ++ e->n_acqs += old->n_acqs; ++} ++ ++static void qsp_ht_delete(void *p, uint32_t h, void *htp) ++{ ++ g_free(p); ++} ++ ++static void qsp_mktree(GTree *tree, bool callsite_coalesce) ++{ ++ struct qht ht, coalesce_ht; ++ struct qht *htp; ++ ++ /* ++ * First, see if there's a prior snapshot, so that we read the global hash ++ * table _after_ the snapshot has been created, which guarantees that ++ * the entries we'll read will be a superset of the snapshot's entries. ++ * ++ * We must remain in an RCU read-side critical section until we're done ++ * with the snapshot. ++ */ ++ WITH_RCU_READ_LOCK_GUARD() { ++ QSPSnapshot *snap = qatomic_rcu_read(&qsp_snapshot); ++ ++ /* Aggregate all results from the global hash table into a local one */ ++ qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++ qht_iter(&qsp_ht, qsp_aggregate, &ht); ++ ++ /* compute the difference wrt the snapshot, if any */ ++ if (snap) { ++ qsp_diff(&snap->ht, &ht); ++ } ++ } ++ ++ htp = &ht; ++ if (callsite_coalesce) { ++ qht_init(&coalesce_ht, qsp_entry_no_thread_obj_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++ qht_iter(&ht, qsp_iter_callsite_coalesce, &coalesce_ht); ++ ++ /* free the previous hash table, and point htp to coalesce_ht */ ++ qht_iter(&ht, qsp_ht_delete, NULL); ++ qht_destroy(&ht); ++ htp = &coalesce_ht; ++ } ++ ++ /* sort the hash table elements by using a tree */ ++ qht_iter(htp, qsp_sort, tree); ++ ++ /* free the hash table, but keep the elements (those are in the tree now) */ ++ qht_destroy(htp); ++} ++ ++/* free string with g_free */ ++static char *qsp_at(const QSPCallSite *callsite) ++{ ++ GString *s = g_string_new(NULL); ++ const char *shortened; ++ ++ /* remove the absolute path to qemu */ ++ if (unlikely(strlen(callsite->file) < qsp_qemu_path_len)) { ++ shortened = callsite->file; ++ } else { ++ shortened = callsite->file + qsp_qemu_path_len; ++ } ++ g_string_append_printf(s, "%s:%u", shortened, callsite->line); ++ return g_string_free(s, FALSE); ++} ++ ++struct QSPReportEntry { ++ const void *obj; ++ char *callsite_at; ++ const char *typename; ++ double time_s; ++ double ns_avg; ++ uint64_t n_acqs; ++ unsigned int n_objs; ++}; ++typedef struct QSPReportEntry QSPReportEntry; ++ ++struct QSPReport { ++ QSPReportEntry *entries; ++ size_t n_entries; ++ size_t max_n_entries; ++}; ++typedef struct QSPReport QSPReport; ++ ++static gboolean qsp_tree_report(gpointer key, gpointer value, gpointer udata) ++{ ++ const QSPEntry *e = key; ++ QSPReport *report = udata; ++ QSPReportEntry *entry; ++ ++ if (report->n_entries == report->max_n_entries) { ++ return TRUE; ++ } ++ entry = &report->entries[report->n_entries]; ++ report->n_entries++; ++ ++ entry->obj = e->callsite->obj; ++ entry->n_objs = e->n_objs; ++ entry->callsite_at = qsp_at(e->callsite); ++ entry->typename = qsp_typenames[e->callsite->type]; ++ entry->time_s = e->ns * 1e-9; ++ entry->n_acqs = e->n_acqs; ++ entry->ns_avg = e->n_acqs ? e->ns / e->n_acqs : 0; ++ return FALSE; ++} ++ ++static void pr_report(const QSPReport *rep) ++{ ++ char *dashes; ++ size_t max_len = 0; ++ int callsite_len = 0; ++ int callsite_rspace; ++ int n_dashes; ++ size_t i; ++ ++ /* find out the maximum length of all 'callsite' fields */ ++ for (i = 0; i < rep->n_entries; i++) { ++ const QSPReportEntry *e = &rep->entries[i]; ++ size_t len = strlen(e->callsite_at); ++ ++ if (len > max_len) { ++ max_len = len; ++ } ++ } ++ ++ callsite_len = MAX(max_len, strlen("Call site")); ++ /* white space to leave to the right of "Call site" */ ++ callsite_rspace = callsite_len - strlen("Call site"); ++ ++ qemu_printf("Type Object Call site%*s Wait Time (s) " ++ " Count Average (us)\n", callsite_rspace, ""); ++ ++ /* build a horizontal rule with dashes */ ++ n_dashes = 79 + callsite_rspace; ++ dashes = g_malloc(n_dashes + 1); ++ memset(dashes, '-', n_dashes); ++ dashes[n_dashes] = '\0'; ++ qemu_printf("%s\n", dashes); ++ ++ for (i = 0; i < rep->n_entries; i++) { ++ const QSPReportEntry *e = &rep->entries[i]; ++ GString *s = g_string_new(NULL); ++ ++ g_string_append_printf(s, "%-9s ", e->typename); ++ if (e->n_objs > 1) { ++ g_string_append_printf(s, "[%12u]", e->n_objs); ++ } else { ++ g_string_append_printf(s, "%14p", e->obj); ++ } ++ g_string_append_printf(s, " %s%*s %13.5f %12" PRIu64 " %12.2f\n", ++ e->callsite_at, ++ callsite_len - (int)strlen(e->callsite_at), "", ++ e->time_s, e->n_acqs, e->ns_avg * 1e-3); ++ qemu_printf("%s", s->str); ++ g_string_free(s, TRUE); ++ } ++ ++ qemu_printf("%s\n", dashes); ++ g_free(dashes); ++} ++ ++static void report_destroy(QSPReport *rep) ++{ ++ size_t i; ++ ++ for (i = 0; i < rep->n_entries; i++) { ++ QSPReportEntry *e = &rep->entries[i]; ++ ++ g_free(e->callsite_at); ++ } ++ g_free(rep->entries); ++} ++ ++void qsp_report(size_t max, enum QSPSortBy sort_by, ++ bool callsite_coalesce) ++{ ++ GTree *tree = g_tree_new_full(qsp_tree_cmp, &sort_by, g_free, NULL); ++ QSPReport rep; ++ ++ qsp_init(); ++ ++ rep.entries = g_new0(QSPReportEntry, max); ++ rep.n_entries = 0; ++ rep.max_n_entries = max; ++ ++ qsp_mktree(tree, callsite_coalesce); ++ g_tree_foreach(tree, qsp_tree_report, &rep); ++ g_tree_destroy(tree); ++ ++ pr_report(&rep); ++ report_destroy(&rep); ++} ++ ++static void qsp_snapshot_destroy(QSPSnapshot *snap) ++{ ++ qht_iter(&snap->ht, qsp_ht_delete, NULL); ++ qht_destroy(&snap->ht); ++ g_free(snap); ++} ++ ++void qsp_reset(void) ++{ ++ QSPSnapshot *new = g_new(QSPSnapshot, 1); ++ QSPSnapshot *old; ++ ++ qsp_init(); ++ ++ qht_init(&new->ht, qsp_entry_cmp, QSP_INITIAL_SIZE, ++ QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); ++ ++ /* take a snapshot of the current state */ ++ qht_iter(&qsp_ht, qsp_aggregate, &new->ht); ++ ++ /* replace the previous snapshot, if any */ ++ old = qatomic_xchg(&qsp_snapshot, new); ++ if (old) { ++ call_rcu(old, qsp_snapshot_destroy, rcu); ++ } ++} +diff --git a/qcow2/lib/util/range.c b/qcow2/lib/util/range.c +new file mode 100644 +index 00000000..f3f40098 +--- /dev/null ++++ b/qcow2/lib/util/range.c +@@ -0,0 +1,123 @@ ++/* ++ * QEMU 64-bit address ranges ++ * ++ * Copyright (c) 2015-2016 Red Hat, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/range.h" ++ ++int range_compare(Range *a, Range *b) ++{ ++ assert(!range_is_empty(a) && !range_is_empty(b)); ++ ++ /* Careful, avoid wraparound */ ++ if (b->lob && b->lob - 1 > a->upb) { ++ return -1; ++ } ++ if (a->lob && a->lob - 1 > b->upb) { ++ return 1; ++ } ++ return 0; ++} ++ ++/* Insert @data into @list of ranges; caller no longer owns @data */ ++GList *range_list_insert(GList *list, Range *data) ++{ ++ GList *l; ++ ++ assert(!range_is_empty(data)); ++ ++ /* Skip all list elements strictly less than data */ ++ for (l = list; l && range_compare(l->data, data) < 0; l = l->next) { ++ } ++ ++ if (!l || range_compare(l->data, data) > 0) { ++ /* Rest of the list (if any) is strictly greater than @data */ ++ return g_list_insert_before(list, l, data); ++ } ++ ++ /* Current list element overlaps @data, merge the two */ ++ range_extend(l->data, data); ++ g_free(data); ++ ++ /* Merge any subsequent list elements that now also overlap */ ++ while (l->next && range_compare(l->data, l->next->data) == 0) { ++ GList *new_l; ++ ++ range_extend(l->data, l->next->data); ++ g_free(l->next->data); ++ new_l = g_list_delete_link(list, l->next); ++ assert(new_l == list); ++ } ++ ++ return list; ++} ++ ++static inline ++GList *append_new_range(GList *list, uint64_t lob, uint64_t upb) ++{ ++ Range *new = g_new0(Range, 1); ++ ++ range_set_bounds(new, lob, upb); ++ return g_list_append(list, new); ++} ++ ++ ++void range_inverse_array(GList *in, GList **rev, ++ uint64_t low, uint64_t high) ++{ ++ Range *r, *rn; ++ GList *l = in, *out = *rev; ++ ++ for (l = in; l && range_upb(l->data) < low; l = l->next) { ++ continue; ++ } ++ ++ if (!l) { ++ out = append_new_range(out, low, high); ++ goto exit; ++ } ++ r = (Range *)l->data; ++ ++ /* first range lob is greater than min, insert a first range */ ++ if (range_lob(r) > low) { ++ out = append_new_range(out, low, MIN(range_lob(r) - 1, high)); ++ } ++ ++ /* insert a range in between each original range until we reach high */ ++ for (; l->next; l = l->next) { ++ r = (Range *)l->data; ++ rn = (Range *)l->next->data; ++ if (range_lob(r) >= high) { ++ goto exit; ++ } ++ if (range_compare(r, rn)) { ++ out = append_new_range(out, range_upb(r) + 1, ++ MIN(range_lob(rn) - 1, high)); ++ } ++ } ++ ++ /* last range */ ++ r = (Range *)l->data; ++ ++ /* last range upb is less than max, insert a last range */ ++ if (range_upb(r) < high) { ++ out = append_new_range(out, range_upb(r) + 1, high); ++ } ++exit: ++ *rev = out; ++} +diff --git a/qcow2/lib/util/rcu.c b/qcow2/lib/util/rcu.c +new file mode 100644 +index 00000000..fa32c942 +--- /dev/null ++++ b/qcow2/lib/util/rcu.c +@@ -0,0 +1,472 @@ ++/* ++ * urcu-mb.c ++ * ++ * Userspace RCU library with explicit memory barriers ++ * ++ * Copyright (c) 2009 Mathieu Desnoyers ++ * Copyright (c) 2009 Paul E. McKenney, IBM Corporation. ++ * Copyright 2015 Red Hat, Inc. ++ * ++ * Ported to QEMU by Paolo Bonzini ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * IBM's contributions to this file may be relicensed under LGPLv2 or later. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/rcu.h" ++#include "qemu/atomic.h" ++#include "qemu/thread.h" ++#include "qemu/main-loop.h" ++#include "qemu/lockable.h" ++#if defined(CONFIG_MALLOC_TRIM) ++#include ++#endif ++ ++/* ++ * Global grace period counter. Bit 0 is always one in rcu_gp_ctr. ++ * Bits 1 and above are defined in synchronize_rcu. ++ */ ++#define RCU_GP_LOCKED (1UL << 0) ++#define RCU_GP_CTR (1UL << 1) ++ ++unsigned long rcu_gp_ctr = RCU_GP_LOCKED; ++ ++QemuEvent rcu_gp_event; ++static int in_drain_call_rcu; ++static QemuMutex rcu_registry_lock; ++static QemuMutex rcu_sync_lock; ++ ++/* ++ * Check whether a quiescent state was crossed between the beginning of ++ * update_counter_and_wait and now. ++ */ ++static inline int rcu_gp_ongoing(unsigned long *ctr) ++{ ++ unsigned long v; ++ ++ v = qatomic_read(ctr); ++ return v && (v != rcu_gp_ctr); ++} ++ ++/* Written to only by each individual reader. Read by both the reader and the ++ * writers. ++ */ ++QEMU_DEFINE_CO_TLS(struct rcu_reader_data, rcu_reader) ++ ++/* Protected by rcu_registry_lock. */ ++typedef QLIST_HEAD(, rcu_reader_data) ThreadList; ++static ThreadList registry = QLIST_HEAD_INITIALIZER(registry); ++ ++/* Wait for previous parity/grace period to be empty of readers. */ ++static void wait_for_readers(void) ++{ ++ ThreadList qsreaders = QLIST_HEAD_INITIALIZER(qsreaders); ++ struct rcu_reader_data *index, *tmp; ++ ++ for (;;) { ++ /* We want to be notified of changes made to rcu_gp_ongoing ++ * while we walk the list. ++ */ ++ qemu_event_reset(&rcu_gp_event); ++ ++ QLIST_FOREACH(index, ®istry, node) { ++ qatomic_set(&index->waiting, true); ++ } ++ ++ /* Here, order the stores to index->waiting before the loads of ++ * index->ctr. Pairs with smp_mb_placeholder() in rcu_read_unlock(), ++ * ensuring that the loads of index->ctr are sequentially consistent. ++ * ++ * If this is the last iteration, this barrier also prevents ++ * frees from seeping upwards, and orders the two wait phases ++ * on architectures with 32-bit longs; see synchronize_rcu(). ++ */ ++ smp_mb_global(); ++ ++ QLIST_FOREACH_SAFE(index, ®istry, node, tmp) { ++ if (!rcu_gp_ongoing(&index->ctr)) { ++ QLIST_REMOVE(index, node); ++ QLIST_INSERT_HEAD(&qsreaders, index, node); ++ ++ /* No need for memory barriers here, worst of all we ++ * get some extra futex wakeups. ++ */ ++ qatomic_set(&index->waiting, false); ++ } else if (qatomic_read(&in_drain_call_rcu)) { ++ notifier_list_notify(&index->force_rcu, NULL); ++ } ++ } ++ ++ if (QLIST_EMPTY(®istry)) { ++ break; ++ } ++ ++ /* Wait for one thread to report a quiescent state and try again. ++ * Release rcu_registry_lock, so rcu_(un)register_thread() doesn't ++ * wait too much time. ++ * ++ * rcu_register_thread() may add nodes to ®istry; it will not ++ * wake up synchronize_rcu, but that is okay because at least another ++ * thread must exit its RCU read-side critical section before ++ * synchronize_rcu is done. The next iteration of the loop will ++ * move the new thread's rcu_reader from ®istry to &qsreaders, ++ * because rcu_gp_ongoing() will return false. ++ * ++ * rcu_unregister_thread() may remove nodes from &qsreaders instead ++ * of ®istry if it runs during qemu_event_wait. That's okay; ++ * the node then will not be added back to ®istry by QLIST_SWAP ++ * below. The invariant is that the node is part of one list when ++ * rcu_registry_lock is released. ++ */ ++ qemu_mutex_unlock(&rcu_registry_lock); ++ qemu_event_wait(&rcu_gp_event); ++ qemu_mutex_lock(&rcu_registry_lock); ++ } ++ ++ /* put back the reader list in the registry */ ++ QLIST_SWAP(®istry, &qsreaders, node); ++} ++ ++void synchronize_rcu(void) ++{ ++ QEMU_LOCK_GUARD(&rcu_sync_lock); ++ ++ /* Write RCU-protected pointers before reading p_rcu_reader->ctr. ++ * Pairs with smp_mb_placeholder() in rcu_read_lock(). ++ * ++ * Also orders write to RCU-protected pointers before ++ * write to rcu_gp_ctr. ++ */ ++ smp_mb_global(); ++ ++ QEMU_LOCK_GUARD(&rcu_registry_lock); ++ if (!QLIST_EMPTY(®istry)) { ++ if (sizeof(rcu_gp_ctr) < 8) { ++ /* For architectures with 32-bit longs, a two-subphases algorithm ++ * ensures we do not encounter overflow bugs. ++ * ++ * Switch parity: 0 -> 1, 1 -> 0. ++ */ ++ qatomic_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); ++ wait_for_readers(); ++ qatomic_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR); ++ } else { ++ /* Increment current grace period. */ ++ qatomic_set(&rcu_gp_ctr, rcu_gp_ctr + RCU_GP_CTR); ++ } ++ ++ wait_for_readers(); ++ } ++} ++ ++ ++#define RCU_CALL_MIN_SIZE 30 ++ ++/* Multi-producer, single-consumer queue based on urcu/static/wfqueue.h ++ * from liburcu. Note that head is only used by the consumer. ++ */ ++static struct rcu_head dummy; ++static struct rcu_head *head = &dummy, **tail = &dummy.next; ++static int rcu_call_count; ++static QemuEvent rcu_call_ready_event; ++ ++static void enqueue(struct rcu_head *node) ++{ ++ struct rcu_head **old_tail; ++ ++ node->next = NULL; ++ ++ /* ++ * Make this node the tail of the list. The node will be ++ * used by further enqueue operations, but it will not ++ * be dequeued yet... ++ */ ++ old_tail = qatomic_xchg(&tail, &node->next); ++ ++ /* ++ * ... until it is pointed to from another item in the list. ++ * In the meantime, try_dequeue() will find a NULL next pointer ++ * and loop. ++ * ++ * Synchronizes with qatomic_load_acquire() in try_dequeue(). ++ */ ++ qatomic_store_release(old_tail, node); ++} ++ ++static struct rcu_head *try_dequeue(void) ++{ ++ struct rcu_head *node, *next; ++ ++retry: ++ /* Head is only written by this thread, so no need for barriers. */ ++ node = head; ++ ++ /* ++ * If the head node has NULL in its next pointer, the value is ++ * wrong and we need to wait until its enqueuer finishes the update. ++ */ ++ next = qatomic_load_acquire(&node->next); ++ if (!next) { ++ return NULL; ++ } ++ ++ /* ++ * Test for an empty list, which we do not expect. Note that for ++ * the consumer head and tail are always consistent. The head ++ * is consistent because only the consumer reads/writes it. ++ * The tail, because it is the first step in the enqueuing. ++ * It is only the next pointers that might be inconsistent. ++ */ ++ if (head == &dummy && qatomic_read(&tail) == &dummy.next) { ++ abort(); ++ } ++ ++ /* ++ * Since we are the sole consumer, and we excluded the empty case ++ * above, the queue will always have at least two nodes: the ++ * dummy node, and the one being removed. So we do not need to update ++ * the tail pointer. ++ */ ++ head = next; ++ ++ /* If we dequeued the dummy node, add it back at the end and retry. */ ++ if (node == &dummy) { ++ enqueue(node); ++ goto retry; ++ } ++ ++ return node; ++} ++ ++static void *call_rcu_thread(void *opaque) ++{ ++ struct rcu_head *node; ++ ++ rcu_register_thread(); ++ ++ for (;;) { ++ int tries = 0; ++ int n = qatomic_read(&rcu_call_count); ++ ++ /* Heuristically wait for a decent number of callbacks to pile up. ++ * Fetch rcu_call_count now, we only must process elements that were ++ * added before synchronize_rcu() starts. ++ */ ++ while (n == 0 || (n < RCU_CALL_MIN_SIZE && ++tries <= 5)) { ++ g_usleep(10000); ++ if (n == 0) { ++ qemu_event_reset(&rcu_call_ready_event); ++ n = qatomic_read(&rcu_call_count); ++ if (n == 0) { ++#if defined(CONFIG_MALLOC_TRIM) ++ malloc_trim(4 * 1024 * 1024); ++#endif ++ qemu_event_wait(&rcu_call_ready_event); ++ } ++ } ++ n = qatomic_read(&rcu_call_count); ++ } ++ ++ qatomic_sub(&rcu_call_count, n); ++ synchronize_rcu(); ++ bql_lock(); ++ while (n > 0) { ++ node = try_dequeue(); ++ while (!node) { ++ bql_unlock(); ++ qemu_event_reset(&rcu_call_ready_event); ++ node = try_dequeue(); ++ if (!node) { ++ qemu_event_wait(&rcu_call_ready_event); ++ node = try_dequeue(); ++ } ++ bql_lock(); ++ } ++ ++ n--; ++ node->func(node); ++ } ++ bql_unlock(); ++ } ++ abort(); ++} ++ ++void call_rcu1(struct rcu_head *node, void (*func)(struct rcu_head *node)) ++{ ++ node->func = func; ++ enqueue(node); ++ qatomic_inc(&rcu_call_count); ++ qemu_event_set(&rcu_call_ready_event); ++} ++ ++ ++struct rcu_drain { ++ struct rcu_head rcu; ++ QemuEvent drain_complete_event; ++}; ++ ++static void drain_rcu_callback(struct rcu_head *node) ++{ ++ struct rcu_drain *event = (struct rcu_drain *)node; ++ qemu_event_set(&event->drain_complete_event); ++} ++ ++/* ++ * This function ensures that all pending RCU callbacks ++ * on the current thread are done executing ++ ++ * drops big qemu lock during the wait to allow RCU thread ++ * to process the callbacks ++ * ++ */ ++ ++void drain_call_rcu(void) ++{ ++ struct rcu_drain rcu_drain; ++ bool locked = bql_locked(); ++ ++ memset(&rcu_drain, 0, sizeof(struct rcu_drain)); ++ qemu_event_init(&rcu_drain.drain_complete_event, false); ++ ++ if (locked) { ++ bql_unlock(); ++ } ++ ++ ++ /* ++ * RCU callbacks are invoked in the same order as in which they ++ * are registered, thus we can be sure that when 'drain_rcu_callback' ++ * is called, all RCU callbacks that were registered on this thread ++ * prior to calling this function are completed. ++ * ++ * Note that since we have only one global queue of the RCU callbacks, ++ * we also end up waiting for most of RCU callbacks that were registered ++ * on the other threads, but this is a side effect that shouldn't be ++ * assumed. ++ */ ++ ++ qatomic_inc(&in_drain_call_rcu); ++ call_rcu1(&rcu_drain.rcu, drain_rcu_callback); ++ qemu_event_wait(&rcu_drain.drain_complete_event); ++ qatomic_dec(&in_drain_call_rcu); ++ ++ if (locked) { ++ bql_lock(); ++ } ++ ++} ++ ++void rcu_register_thread(void) ++{ ++ assert(get_ptr_rcu_reader()->ctr == 0); ++ qemu_mutex_lock(&rcu_registry_lock); ++ QLIST_INSERT_HEAD(®istry, get_ptr_rcu_reader(), node); ++ qemu_mutex_unlock(&rcu_registry_lock); ++} ++ ++void rcu_unregister_thread(void) ++{ ++ qemu_mutex_lock(&rcu_registry_lock); ++ QLIST_REMOVE(get_ptr_rcu_reader(), node); ++ qemu_mutex_unlock(&rcu_registry_lock); ++} ++ ++void rcu_add_force_rcu_notifier(Notifier *n) ++{ ++ qemu_mutex_lock(&rcu_registry_lock); ++ notifier_list_add(&get_ptr_rcu_reader()->force_rcu, n); ++ qemu_mutex_unlock(&rcu_registry_lock); ++} ++ ++void rcu_remove_force_rcu_notifier(Notifier *n) ++{ ++ qemu_mutex_lock(&rcu_registry_lock); ++ notifier_remove(n); ++ qemu_mutex_unlock(&rcu_registry_lock); ++} ++ ++static void rcu_init_complete(void) ++{ ++ QemuThread thread; ++ ++ qemu_mutex_init(&rcu_registry_lock); ++ qemu_mutex_init(&rcu_sync_lock); ++ qemu_event_init(&rcu_gp_event, true); ++ ++ qemu_event_init(&rcu_call_ready_event, false); ++ ++ /* The caller is assumed to have BQL, so the call_rcu thread ++ * must have been quiescent even after forking, just recreate it. ++ */ ++ qemu_thread_create(&thread, "call_rcu", call_rcu_thread, ++ NULL, QEMU_THREAD_DETACHED); ++ ++ rcu_register_thread(); ++} ++ ++static int atfork_depth = 1; ++ ++void rcu_enable_atfork(void) ++{ ++ atfork_depth++; ++} ++ ++void rcu_disable_atfork(void) ++{ ++ atfork_depth--; ++} ++ ++#ifdef CONFIG_POSIX ++static void rcu_init_lock(void) ++{ ++ if (atfork_depth < 1) { ++ return; ++ } ++ ++ qemu_mutex_lock(&rcu_sync_lock); ++ qemu_mutex_lock(&rcu_registry_lock); ++} ++ ++static void rcu_init_unlock(void) ++{ ++ if (atfork_depth < 1) { ++ return; ++ } ++ ++ qemu_mutex_unlock(&rcu_registry_lock); ++ qemu_mutex_unlock(&rcu_sync_lock); ++} ++ ++static void rcu_init_child(void) ++{ ++ if (atfork_depth < 1) { ++ return; ++ } ++ ++ memset(®istry, 0, sizeof(registry)); ++ rcu_init_complete(); ++} ++#endif ++ ++static void __attribute__((__constructor__)) rcu_init(void) ++{ ++ smp_mb_global_init(); ++#ifdef CONFIG_POSIX ++ pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child); ++#endif ++ rcu_init_complete(); ++} +diff --git a/qcow2/lib/util/stats64.c b/qcow2/lib/util/stats64.c +new file mode 100644 +index 00000000..09736014 +--- /dev/null ++++ b/qcow2/lib/util/stats64.c +@@ -0,0 +1,148 @@ ++/* ++ * Atomic operations on 64-bit quantities. ++ * ++ * Copyright (C) 2017 Red Hat, Inc. ++ * ++ * Author: Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/atomic.h" ++#include "qemu/stats64.h" ++#include "qemu/processor.h" ++ ++#ifndef CONFIG_ATOMIC64 ++static inline void stat64_rdlock(Stat64 *s) ++{ ++ /* Keep out incoming writers to avoid them starving us. */ ++ qatomic_add(&s->lock, 2); ++ ++ /* If there is a concurrent writer, wait for it. */ ++ while (qatomic_read(&s->lock) & 1) { ++ cpu_relax(); ++ } ++} ++ ++static inline void stat64_rdunlock(Stat64 *s) ++{ ++ qatomic_sub(&s->lock, 2); ++} ++ ++static inline bool stat64_wrtrylock(Stat64 *s) ++{ ++ return qatomic_cmpxchg(&s->lock, 0, 1) == 0; ++} ++ ++static inline void stat64_wrunlock(Stat64 *s) ++{ ++ qatomic_dec(&s->lock); ++} ++ ++uint64_t stat64_get(const Stat64 *s) ++{ ++ uint32_t high, low; ++ ++ stat64_rdlock((Stat64 *)s); ++ ++ /* 64-bit writes always take the lock, so we can read in ++ * any order. ++ */ ++ high = qatomic_read(&s->high); ++ low = qatomic_read(&s->low); ++ stat64_rdunlock((Stat64 *)s); ++ ++ return ((uint64_t)high << 32) | low; ++} ++ ++void stat64_set(Stat64 *s, uint64_t val) ++{ ++ while (!stat64_wrtrylock(s)) { ++ cpu_relax(); ++ } ++ ++ qatomic_set(&s->high, val >> 32); ++ qatomic_set(&s->low, val); ++ stat64_wrunlock(s); ++} ++ ++bool stat64_add32_carry(Stat64 *s, uint32_t low, uint32_t high) ++{ ++ uint32_t old; ++ ++ if (!stat64_wrtrylock(s)) { ++ cpu_relax(); ++ return false; ++ } ++ ++ /* 64-bit reads always take the lock, so they don't care about the ++ * order of our update. By updating s->low first, we can check ++ * whether we have to carry into s->high. ++ */ ++ old = qatomic_fetch_add(&s->low, low); ++ high += (old + low) < old; ++ qatomic_add(&s->high, high); ++ stat64_wrunlock(s); ++ return true; ++} ++ ++bool stat64_min_slow(Stat64 *s, uint64_t value) ++{ ++ uint32_t high, low; ++ uint64_t orig; ++ ++ if (!stat64_wrtrylock(s)) { ++ cpu_relax(); ++ return false; ++ } ++ ++ high = qatomic_read(&s->high); ++ low = qatomic_read(&s->low); ++ ++ orig = ((uint64_t)high << 32) | low; ++ if (value < orig) { ++ /* We have to set low before high, just like stat64_min reads ++ * high before low. The value may become higher temporarily, but ++ * stat64_get does not notice (it takes the lock) and the only ill ++ * effect on stat64_min is that the slow path may be triggered ++ * unnecessarily. ++ */ ++ qatomic_set(&s->low, (uint32_t)value); ++ smp_wmb(); ++ qatomic_set(&s->high, value >> 32); ++ } ++ stat64_wrunlock(s); ++ return true; ++} ++ ++bool stat64_max_slow(Stat64 *s, uint64_t value) ++{ ++ uint32_t high, low; ++ uint64_t orig; ++ ++ if (!stat64_wrtrylock(s)) { ++ cpu_relax(); ++ return false; ++ } ++ ++ high = qatomic_read(&s->high); ++ low = qatomic_read(&s->low); ++ ++ orig = ((uint64_t)high << 32) | low; ++ if (value > orig) { ++ /* We have to set low before high, just like stat64_max reads ++ * high before low. The value may become lower temporarily, but ++ * stat64_get does not notice (it takes the lock) and the only ill ++ * effect on stat64_max is that the slow path may be triggered ++ * unnecessarily. ++ */ ++ qatomic_set(&s->low, (uint32_t)value); ++ smp_wmb(); ++ qatomic_set(&s->high, value >> 32); ++ } ++ stat64_wrunlock(s); ++ return true; ++} ++#endif +diff --git a/qcow2/lib/util/thread-pool.c b/qcow2/lib/util/thread-pool.c +new file mode 100644 +index 00000000..27eb777e +--- /dev/null ++++ b/qcow2/lib/util/thread-pool.c +@@ -0,0 +1,381 @@ ++/* ++ * QEMU block layer thread pool ++ * ++ * Copyright IBM, Corp. 2008 ++ * Copyright Red Hat, Inc. 2012 ++ * ++ * Authors: ++ * Anthony Liguori ++ * Paolo Bonzini ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2. See ++ * the COPYING file in the top-level directory. ++ * ++ * Contributions after 2012-01-13 are licensed under the terms of the ++ * GNU GPL, version 2 or (at your option) any later version. ++ */ ++#include "qemu/osdep.h" ++#include "qemu/defer-call.h" ++#include "qemu/queue.h" ++#include "qemu/thread.h" ++#include "qemu/coroutine.h" ++#include "trace.h" ++#include "block/thread-pool.h" ++#include "qemu/main-loop.h" ++ ++static void do_spawn_thread(ThreadPool *pool); ++ ++typedef struct ThreadPoolElement ThreadPoolElement; ++ ++enum ThreadState { ++ THREAD_QUEUED, ++ THREAD_ACTIVE, ++ THREAD_DONE, ++}; ++ ++struct ThreadPoolElement { ++ BlockAIOCB common; ++ ThreadPool *pool; ++ ThreadPoolFunc *func; ++ void *arg; ++ ++ /* Moving state out of THREAD_QUEUED is protected by lock. After ++ * that, only the worker thread can write to it. Reads and writes ++ * of state and ret are ordered with memory barriers. ++ */ ++ enum ThreadState state; ++ int ret; ++ ++ /* Access to this list is protected by lock. */ ++ QTAILQ_ENTRY(ThreadPoolElement) reqs; ++ ++ /* This list is only written by the thread pool's mother thread. */ ++ QLIST_ENTRY(ThreadPoolElement) all; ++}; ++ ++struct ThreadPool { ++ AioContext *ctx; ++ QEMUBH *completion_bh; ++ QemuMutex lock; ++ QemuCond worker_stopped; ++ QemuCond request_cond; ++ QEMUBH *new_thread_bh; ++ ++ /* The following variables are only accessed from one AioContext. */ ++ QLIST_HEAD(, ThreadPoolElement) head; ++ ++ /* The following variables are protected by lock. */ ++ QTAILQ_HEAD(, ThreadPoolElement) request_list; ++ int cur_threads; ++ int idle_threads; ++ int new_threads; /* backlog of threads we need to create */ ++ int pending_threads; /* threads created but not running yet */ ++ int min_threads; ++ int max_threads; ++}; ++ ++static void *worker_thread(void *opaque) ++{ ++ ThreadPool *pool = opaque; ++ ++ qemu_mutex_lock(&pool->lock); ++ pool->pending_threads--; ++ do_spawn_thread(pool); ++ ++ while (pool->cur_threads <= pool->max_threads) { ++ ThreadPoolElement *req; ++ int ret; ++ ++ if (QTAILQ_EMPTY(&pool->request_list)) { ++ pool->idle_threads++; ++ ret = qemu_cond_timedwait(&pool->request_cond, &pool->lock, 10000); ++ pool->idle_threads--; ++ if (ret == 0 && ++ QTAILQ_EMPTY(&pool->request_list) && ++ pool->cur_threads > pool->min_threads) { ++ /* Timed out + no work to do + no need for warm threads = exit. */ ++ break; ++ } ++ /* ++ * Even if there was some work to do, check if there aren't ++ * too many worker threads before picking it up. ++ */ ++ continue; ++ } ++ ++ req = QTAILQ_FIRST(&pool->request_list); ++ QTAILQ_REMOVE(&pool->request_list, req, reqs); ++ req->state = THREAD_ACTIVE; ++ qemu_mutex_unlock(&pool->lock); ++ ++ ret = req->func(req->arg); ++ ++ req->ret = ret; ++ /* Write ret before state. */ ++ smp_wmb(); ++ req->state = THREAD_DONE; ++ ++ qemu_bh_schedule(pool->completion_bh); ++ qemu_mutex_lock(&pool->lock); ++ } ++ ++ pool->cur_threads--; ++ qemu_cond_signal(&pool->worker_stopped); ++ ++ /* ++ * Wake up another thread, in case we got a wakeup but decided ++ * to exit due to pool->cur_threads > pool->max_threads. ++ */ ++ qemu_cond_signal(&pool->request_cond); ++ qemu_mutex_unlock(&pool->lock); ++ return NULL; ++} ++ ++static void do_spawn_thread(ThreadPool *pool) ++{ ++ QemuThread t; ++ ++ /* Runs with lock taken. */ ++ if (!pool->new_threads) { ++ return; ++ } ++ ++ pool->new_threads--; ++ pool->pending_threads++; ++ ++ qemu_thread_create(&t, "worker", worker_thread, pool, QEMU_THREAD_DETACHED); ++} ++ ++static void spawn_thread_bh_fn(void *opaque) ++{ ++ ThreadPool *pool = opaque; ++ ++ qemu_mutex_lock(&pool->lock); ++ do_spawn_thread(pool); ++ qemu_mutex_unlock(&pool->lock); ++} ++ ++static void spawn_thread(ThreadPool *pool) ++{ ++ pool->cur_threads++; ++ pool->new_threads++; ++ /* If there are threads being created, they will spawn new workers, so ++ * we don't spend time creating many threads in a loop holding a mutex or ++ * starving the current vcpu. ++ * ++ * If there are no idle threads, ask the main thread to create one, so we ++ * inherit the correct affinity instead of the vcpu affinity. ++ */ ++ if (!pool->pending_threads) { ++ qemu_bh_schedule(pool->new_thread_bh); ++ } ++} ++ ++static void thread_pool_completion_bh(void *opaque) ++{ ++ ThreadPool *pool = opaque; ++ ThreadPoolElement *elem, *next; ++ ++ defer_call_begin(); /* cb() may use defer_call() to coalesce work */ ++ ++restart: ++ QLIST_FOREACH_SAFE(elem, &pool->head, all, next) { ++ if (elem->state != THREAD_DONE) { ++ continue; ++ } ++ ++ trace_thread_pool_complete(pool, elem, elem->common.opaque, ++ elem->ret); ++ QLIST_REMOVE(elem, all); ++ ++ if (elem->common.cb) { ++ /* Read state before ret. */ ++ smp_rmb(); ++ ++ /* Schedule ourselves in case elem->common.cb() calls aio_poll() to ++ * wait for another request that completed at the same time. ++ */ ++ qemu_bh_schedule(pool->completion_bh); ++ ++ elem->common.cb(elem->common.opaque, elem->ret); ++ ++ /* We can safely cancel the completion_bh here regardless of someone ++ * else having scheduled it meanwhile because we reenter the ++ * completion function anyway (goto restart). ++ */ ++ qemu_bh_cancel(pool->completion_bh); ++ ++ qemu_aio_unref(elem); ++ goto restart; ++ } else { ++ qemu_aio_unref(elem); ++ } ++ } ++ ++ defer_call_end(); ++} ++ ++static void thread_pool_cancel(BlockAIOCB *acb) ++{ ++ ThreadPoolElement *elem = (ThreadPoolElement *)acb; ++ ThreadPool *pool = elem->pool; ++ ++ trace_thread_pool_cancel(elem, elem->common.opaque); ++ ++ QEMU_LOCK_GUARD(&pool->lock); ++ if (elem->state == THREAD_QUEUED) { ++ QTAILQ_REMOVE(&pool->request_list, elem, reqs); ++ qemu_bh_schedule(pool->completion_bh); ++ ++ elem->state = THREAD_DONE; ++ elem->ret = -ECANCELED; ++ } ++ ++} ++ ++static const AIOCBInfo thread_pool_aiocb_info = { ++ .aiocb_size = sizeof(ThreadPoolElement), ++ .cancel_async = thread_pool_cancel, ++}; ++ ++BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, ++ BlockCompletionFunc *cb, void *opaque) ++{ ++ ThreadPoolElement *req; ++ AioContext *ctx = qemu_get_current_aio_context(); ++ ThreadPool *pool = aio_get_thread_pool(ctx); ++ ++ /* Assert that the thread submitting work is the same running the pool */ ++ assert(pool->ctx == qemu_get_current_aio_context()); ++ ++ req = qemu_aio_get(&thread_pool_aiocb_info, NULL, cb, opaque); ++ req->func = func; ++ req->arg = arg; ++ req->state = THREAD_QUEUED; ++ req->pool = pool; ++ ++ QLIST_INSERT_HEAD(&pool->head, req, all); ++ ++ trace_thread_pool_submit(pool, req, arg); ++ ++ qemu_mutex_lock(&pool->lock); ++ if (pool->idle_threads == 0 && pool->cur_threads < pool->max_threads) { ++ spawn_thread(pool); ++ } ++ QTAILQ_INSERT_TAIL(&pool->request_list, req, reqs); ++ qemu_mutex_unlock(&pool->lock); ++ qemu_cond_signal(&pool->request_cond); ++ return &req->common; ++} ++ ++typedef struct ThreadPoolCo { ++ Coroutine *co; ++ int ret; ++} ThreadPoolCo; ++ ++static void thread_pool_co_cb(void *opaque, int ret) ++{ ++ ThreadPoolCo *co = opaque; ++ ++ co->ret = ret; ++ aio_co_wake(co->co); ++} ++ ++int coroutine_fn thread_pool_submit_co(ThreadPoolFunc *func, void *arg) ++{ ++ ThreadPoolCo tpc = { .co = qemu_coroutine_self(), .ret = -EINPROGRESS }; ++ assert(qemu_in_coroutine()); ++ thread_pool_submit_aio(func, arg, thread_pool_co_cb, &tpc); ++ qemu_coroutine_yield(); ++ return tpc.ret; ++} ++ ++void thread_pool_submit(ThreadPoolFunc *func, void *arg) ++{ ++ thread_pool_submit_aio(func, arg, NULL, NULL); ++} ++ ++void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) ++{ ++ qemu_mutex_lock(&pool->lock); ++ ++ pool->min_threads = ctx->thread_pool_min; ++ pool->max_threads = ctx->thread_pool_max; ++ ++ /* ++ * We either have to: ++ * - Increase the number available of threads until over the min_threads ++ * threshold. ++ * - Bump the worker threads so that they exit, until under the max_threads ++ * threshold. ++ * - Do nothing. The current number of threads fall in between the min and ++ * max thresholds. We'll let the pool manage itself. ++ */ ++ for (int i = pool->cur_threads; i < pool->min_threads; i++) { ++ spawn_thread(pool); ++ } ++ ++ for (int i = pool->cur_threads; i > pool->max_threads; i--) { ++ qemu_cond_signal(&pool->request_cond); ++ } ++ ++ qemu_mutex_unlock(&pool->lock); ++} ++ ++static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx) ++{ ++ if (!ctx) { ++ ctx = qemu_get_aio_context(); ++ } ++ ++ memset(pool, 0, sizeof(*pool)); ++ pool->ctx = ctx; ++ pool->completion_bh = aio_bh_new(ctx, thread_pool_completion_bh, pool); ++ qemu_mutex_init(&pool->lock); ++ qemu_cond_init(&pool->worker_stopped); ++ qemu_cond_init(&pool->request_cond); ++ pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool); ++ ++ QLIST_INIT(&pool->head); ++ QTAILQ_INIT(&pool->request_list); ++ ++ thread_pool_update_params(pool, ctx); ++} ++ ++ThreadPool *thread_pool_new(AioContext *ctx) ++{ ++ ThreadPool *pool = g_new(ThreadPool, 1); ++ thread_pool_init_one(pool, ctx); ++ return pool; ++} ++ ++void thread_pool_free(ThreadPool *pool) ++{ ++ if (!pool) { ++ return; ++ } ++ ++ assert(QLIST_EMPTY(&pool->head)); ++ ++ qemu_mutex_lock(&pool->lock); ++ ++ /* Stop new threads from spawning */ ++ qemu_bh_delete(pool->new_thread_bh); ++ pool->cur_threads -= pool->new_threads; ++ pool->new_threads = 0; ++ ++ /* Wait for worker threads to terminate */ ++ pool->max_threads = 0; ++ qemu_cond_broadcast(&pool->request_cond); ++ while (pool->cur_threads > 0) { ++ qemu_cond_wait(&pool->worker_stopped, &pool->lock); ++ } ++ ++ qemu_mutex_unlock(&pool->lock); ++ ++ qemu_bh_delete(pool->completion_bh); ++ qemu_cond_destroy(&pool->request_cond); ++ qemu_cond_destroy(&pool->worker_stopped); ++ qemu_mutex_destroy(&pool->lock); ++ g_free(pool); ++} +diff --git a/qcow2/lib/util/timed-average.c b/qcow2/lib/util/timed-average.c +new file mode 100644 +index 00000000..2b49d532 +--- /dev/null ++++ b/qcow2/lib/util/timed-average.c +@@ -0,0 +1,231 @@ ++/* ++ * QEMU timed average computation ++ * ++ * Copyright (C) Nodalink, EURL. 2014 ++ * Copyright (C) Igalia, S.L. 2015 ++ * ++ * Authors: ++ * BenoĆ®t Canet ++ * Alberto Garcia ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation, either version 2 of the License, or ++ * (at your option) version 3 or any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "qemu/timed-average.h" ++ ++/* This module computes an average of a set of values within a time ++ * window. ++ * ++ * Algorithm: ++ * ++ * - Create two windows with a certain expiration period, and ++ * offsetted by period / 2. ++ * - Each time you want to account a new value, do it in both windows. ++ * - The minimum / maximum / average values are always returned from ++ * the oldest window. ++ * ++ * Example: ++ * ++ * t=0 |t=0.5 |t=1 |t=1.5 |t=2 ++ * wnd0: [0,0.5)|wnd0: [0.5,1.5) | |wnd0: [1.5,2.5) | ++ * wnd1: [0,1) | |wnd1: [1,2) | | ++ * ++ * Values are returned from: ++ * ++ * wnd0---------|wnd1------------|wnd0---------|wnd1-------------| ++ */ ++ ++/* Update the expiration of a time window ++ * ++ * @w: the window used ++ * @now: the current time in nanoseconds ++ * @period: the expiration period in nanoseconds ++ */ ++static void update_expiration(TimedAverageWindow *w, int64_t now, ++ int64_t period) ++{ ++ /* time elapsed since the last theoretical expiration */ ++ int64_t elapsed = (now - w->expiration) % period; ++ /* time remaininging until the next expiration */ ++ int64_t remaining = period - elapsed; ++ /* compute expiration */ ++ w->expiration = now + remaining; ++} ++ ++/* Reset a window ++ * ++ * @w: the window to reset ++ */ ++static void window_reset(TimedAverageWindow *w) ++{ ++ w->min = UINT64_MAX; ++ w->max = 0; ++ w->sum = 0; ++ w->count = 0; ++} ++ ++/* Get the current window (that is, the one with the earliest ++ * expiration time). ++ * ++ * @ta: the TimedAverage structure ++ * @ret: a pointer to the current window ++ */ ++static TimedAverageWindow *current_window(TimedAverage *ta) ++{ ++ return &ta->windows[ta->current]; ++} ++ ++/* Initialize a TimedAverage structure ++ * ++ * @ta: the TimedAverage structure ++ * @clock_type: the type of clock to use ++ * @period: the time window period in nanoseconds ++ */ ++void timed_average_init(TimedAverage *ta, QEMUClockType clock_type, ++ uint64_t period) ++{ ++ int64_t now = qemu_clock_get_ns(clock_type); ++ ++ /* Returned values are from the oldest window, so they belong to ++ * the interval [ta->period/2,ta->period). By adjusting the ++ * requested period by 4/3, we guarantee that they're in the ++ * interval [2/3 period,4/3 period), closer to the requested ++ * period on average */ ++ ta->period = (uint64_t) period * 4 / 3; ++ ta->clock_type = clock_type; ++ ta->current = 0; ++ ++ window_reset(&ta->windows[0]); ++ window_reset(&ta->windows[1]); ++ ++ /* Both windows are offsetted by half a period */ ++ ta->windows[0].expiration = now + ta->period / 2; ++ ta->windows[1].expiration = now + ta->period; ++} ++ ++/* Check if the time windows have expired, updating their counters and ++ * expiration time if that's the case. ++ * ++ * @ta: the TimedAverage structure ++ * @elapsed: if non-NULL, the elapsed time (in ns) within the current ++ * window will be stored here ++ */ ++static void check_expirations(TimedAverage *ta, uint64_t *elapsed) ++{ ++ int64_t now = qemu_clock_get_ns(ta->clock_type); ++ int i; ++ ++ assert(ta->period != 0); ++ ++ /* Check if the windows have expired */ ++ for (i = 0; i < 2; i++) { ++ TimedAverageWindow *w = &ta->windows[i]; ++ if (w->expiration <= now) { ++ window_reset(w); ++ update_expiration(w, now, ta->period); ++ } ++ } ++ ++ /* Make ta->current point to the oldest window */ ++ if (ta->windows[0].expiration < ta->windows[1].expiration) { ++ ta->current = 0; ++ } else { ++ ta->current = 1; ++ } ++ ++ /* Calculate the elapsed time within the current window */ ++ if (elapsed) { ++ int64_t remaining = ta->windows[ta->current].expiration - now; ++ *elapsed = ta->period - remaining; ++ } ++} ++ ++/* Account a value ++ * ++ * @ta: the TimedAverage structure ++ * @value: the value to account ++ */ ++void timed_average_account(TimedAverage *ta, uint64_t value) ++{ ++ int i; ++ check_expirations(ta, NULL); ++ ++ /* Do the accounting in both windows at the same time */ ++ for (i = 0; i < 2; i++) { ++ TimedAverageWindow *w = &ta->windows[i]; ++ ++ w->sum += value; ++ w->count++; ++ ++ if (value < w->min) { ++ w->min = value; ++ } ++ ++ if (value > w->max) { ++ w->max = value; ++ } ++ } ++} ++ ++/* Get the minimum value ++ * ++ * @ta: the TimedAverage structure ++ * @ret: the minimum value ++ */ ++uint64_t timed_average_min(TimedAverage *ta) ++{ ++ TimedAverageWindow *w; ++ check_expirations(ta, NULL); ++ w = current_window(ta); ++ return w->min < UINT64_MAX ? w->min : 0; ++} ++ ++/* Get the average value ++ * ++ * @ta: the TimedAverage structure ++ * @ret: the average value ++ */ ++uint64_t timed_average_avg(TimedAverage *ta) ++{ ++ TimedAverageWindow *w; ++ check_expirations(ta, NULL); ++ w = current_window(ta); ++ return w->count > 0 ? w->sum / w->count : 0; ++} ++ ++/* Get the maximum value ++ * ++ * @ta: the TimedAverage structure ++ * @ret: the maximum value ++ */ ++uint64_t timed_average_max(TimedAverage *ta) ++{ ++ check_expirations(ta, NULL); ++ return current_window(ta)->max; ++} ++ ++/* Get the sum of all accounted values ++ * @ta: the TimedAverage structure ++ * @elapsed: if non-NULL, the elapsed time (in ns) will be stored here ++ * @ret: the sum of all accounted values ++ */ ++uint64_t timed_average_sum(TimedAverage *ta, uint64_t *elapsed) ++{ ++ TimedAverageWindow *w; ++ check_expirations(ta, elapsed); ++ w = current_window(ta); ++ return w->sum; ++} +diff --git a/qcow2/lib/util/transactions.c b/qcow2/lib/util/transactions.c +new file mode 100644 +index 00000000..2dbdedce +--- /dev/null ++++ b/qcow2/lib/util/transactions.c +@@ -0,0 +1,100 @@ ++/* ++ * Simple transactions API ++ * ++ * Copyright (c) 2021 Virtuozzo International GmbH. ++ * ++ * Author: ++ * Sementsov-Ogievskiy Vladimir ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include "qemu/osdep.h" ++ ++#include "qemu/transactions.h" ++#include "qemu/queue.h" ++ ++typedef struct TransactionAction { ++ TransactionActionDrv *drv; ++ void *opaque; ++ QSLIST_ENTRY(TransactionAction) entry; ++} TransactionAction; ++ ++struct Transaction { ++ QSLIST_HEAD(, TransactionAction) actions; ++}; ++ ++Transaction *tran_new(void) ++{ ++ Transaction *tran = g_new(Transaction, 1); ++ ++ QSLIST_INIT(&tran->actions); ++ ++ return tran; ++} ++ ++void tran_add(Transaction *tran, TransactionActionDrv *drv, void *opaque) ++{ ++ TransactionAction *act; ++ ++ act = g_new(TransactionAction, 1); ++ *act = (TransactionAction) { ++ .drv = drv, ++ .opaque = opaque ++ }; ++ ++ QSLIST_INSERT_HEAD(&tran->actions, act, entry); ++} ++ ++void tran_abort(Transaction *tran) ++{ ++ TransactionAction *act, *next; ++ ++ QSLIST_FOREACH(act, &tran->actions, entry) { ++ if (act->drv->abort) { ++ act->drv->abort(act->opaque); ++ } ++ } ++ ++ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { ++ if (act->drv->clean) { ++ act->drv->clean(act->opaque); ++ } ++ ++ g_free(act); ++ } ++ ++ g_free(tran); ++} ++ ++void tran_commit(Transaction *tran) ++{ ++ TransactionAction *act, *next; ++ ++ QSLIST_FOREACH(act, &tran->actions, entry) { ++ if (act->drv->commit) { ++ act->drv->commit(act->opaque); ++ } ++ } ++ ++ QSLIST_FOREACH_SAFE(act, &tran->actions, entry, next) { ++ if (act->drv->clean) { ++ act->drv->clean(act->opaque); ++ } ++ ++ g_free(act); ++ } ++ ++ g_free(tran); ++} +diff --git a/qcow2/lib/util/unicode.c b/qcow2/lib/util/unicode.c +new file mode 100644 +index 00000000..8580bc59 +--- /dev/null ++++ b/qcow2/lib/util/unicode.c +@@ -0,0 +1,156 @@ ++/* ++ * Dealing with Unicode ++ * ++ * Copyright (C) 2013 Red Hat, Inc. ++ * ++ * Authors: ++ * Markus Armbruster ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or ++ * later. See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/unicode.h" ++ ++static bool is_valid_codepoint(int codepoint) ++{ ++ if (codepoint > 0x10FFFFu) { ++ return false; /* beyond Unicode range */ ++ } ++ if ((codepoint >= 0xFDD0 && codepoint <= 0xFDEF) ++ || (codepoint & 0xFFFE) == 0xFFFE) { ++ return false; /* noncharacter */ ++ } ++ if (codepoint >= 0xD800 && codepoint <= 0xDFFF) { ++ return false; /* surrogate code point */ ++ } ++ return true; ++} ++ ++/** ++ * mod_utf8_codepoint: ++ * @s: string encoded in modified UTF-8 ++ * @n: maximum number of bytes to read from @s, if less than 6 ++ * @end: set to end of sequence on return ++ * ++ * Convert the modified UTF-8 sequence at the start of @s. Modified ++ * UTF-8 is exactly like UTF-8, except U+0000 is encoded as ++ * "\xC0\x80". ++ * ++ * If @n is zero or @s points to a zero byte, the sequence is invalid, ++ * and @end is set to @s. ++ * ++ * If @s points to an impossible byte (0xFE or 0xFF) or a continuation ++ * byte, the sequence is invalid, and @end is set to @s + 1 ++ * ++ * Else, the first byte determines how many continuation bytes are ++ * expected. If there are fewer, the sequence is invalid, and @end is ++ * set to @s + 1 + actual number of continuation bytes. Else, the ++ * sequence is well-formed, and @end is set to @s + 1 + expected ++ * number of continuation bytes. ++ * ++ * A well-formed sequence is valid unless it encodes a codepoint ++ * outside the Unicode range U+0000..U+10FFFF, one of Unicode's 66 ++ * noncharacters, a surrogate codepoint, or is overlong. Except the ++ * overlong sequence "\xC0\x80" is valid. ++ * ++ * Conversion succeeds if and only if the sequence is valid. ++ * ++ * Returns: the Unicode codepoint on success, -1 on failure. ++ */ ++int mod_utf8_codepoint(const char *s, size_t n, char **end) ++{ ++ static int min_cp[5] = { 0x80, 0x800, 0x10000, 0x200000, 0x4000000 }; ++ const unsigned char *p; ++ unsigned byte, mask, len, i; ++ int cp; ++ ++ if (n == 0 || *s == 0) { ++ /* empty sequence */ ++ *end = (char *)s; ++ return -1; ++ } ++ ++ p = (const unsigned char *)s; ++ byte = *p++; ++ if (byte < 0x80) { ++ cp = byte; /* one byte sequence */ ++ } else if (byte >= 0xFE) { ++ cp = -1; /* impossible bytes 0xFE, 0xFF */ ++ } else if ((byte & 0x40) == 0) { ++ cp = -1; /* unexpected continuation byte */ ++ } else { ++ /* multi-byte sequence */ ++ len = 0; ++ for (mask = 0x80; byte & mask; mask >>= 1) { ++ len++; ++ } ++ assert(len > 1 && len < 7); ++ cp = byte & (mask - 1); ++ for (i = 1; i < len; i++) { ++ byte = i < n ? *p : 0; ++ if ((byte & 0xC0) != 0x80) { ++ cp = -1; /* continuation byte missing */ ++ goto out; ++ } ++ p++; ++ cp <<= 6; ++ cp |= byte & 0x3F; ++ } ++ if (!is_valid_codepoint(cp)) { ++ cp = -1; ++ } else if (cp < min_cp[len - 2] && !(cp == 0 && len == 2)) { ++ cp = -1; /* overlong, not \xC0\x80 */ ++ } ++ } ++ ++out: ++ *end = (char *)p; ++ return cp; ++} ++ ++/** ++ * mod_utf8_encode: ++ * @buf: Destination buffer ++ * @bufsz: size of @buf, at least 5. ++ * @codepoint: Unicode codepoint to encode ++ * ++ * Convert Unicode codepoint @codepoint to modified UTF-8. ++ * ++ * Returns: the length of the UTF-8 sequence on success, -1 when ++ * @codepoint is invalid. ++ */ ++ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint) ++{ ++ assert(bufsz >= 5); ++ ++ if (!is_valid_codepoint(codepoint)) { ++ return -1; ++ } ++ ++ if (codepoint > 0 && codepoint <= 0x7F) { ++ buf[0] = codepoint & 0x7F; ++ buf[1] = 0; ++ return 1; ++ } ++ if (codepoint <= 0x7FF) { ++ buf[0] = 0xC0 | ((codepoint >> 6) & 0x1F); ++ buf[1] = 0x80 | (codepoint & 0x3F); ++ buf[2] = 0; ++ return 2; ++ } ++ if (codepoint <= 0xFFFF) { ++ buf[0] = 0xE0 | ((codepoint >> 12) & 0x0F); ++ buf[1] = 0x80 | ((codepoint >> 6) & 0x3F); ++ buf[2] = 0x80 | (codepoint & 0x3F); ++ buf[3] = 0; ++ return 3; ++ } ++ buf[0] = 0xF0 | ((codepoint >> 18) & 0x07); ++ buf[1] = 0x80 | ((codepoint >> 12) & 0x3F); ++ buf[2] = 0x80 | ((codepoint >> 6) & 0x3F); ++ buf[3] = 0x80 | (codepoint & 0x3F); ++ buf[4] = 0; ++ return 4; ++} +diff --git a/qcow2/lib/util/uri.c b/qcow2/lib/util/uri.c +new file mode 100644 +index 00000000..573174bf +--- /dev/null ++++ b/qcow2/lib/util/uri.c +@@ -0,0 +1,1466 @@ ++/** ++ * uri.c: set of generic URI related routines ++ * ++ * Reference: RFCs 3986, 2732 and 2373 ++ * ++ * Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved. ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * DANIEL VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ * Except as contained in this notice, the name of Daniel Veillard shall not ++ * be used in advertising or otherwise to promote the sale, use or other ++ * dealings in this Software without prior written authorization from him. ++ * ++ * daniel@veillard.com ++ * ++ ** ++ * ++ * Copyright (C) 2007, 2009-2010 Red Hat, Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library. If not, see . ++ * ++ * Authors: ++ * Richard W.M. Jones ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/cutils.h" ++ ++#include "qemu/uri.h" ++ ++static void uri_clean(URI *uri); ++ ++/* ++ * Old rule from 2396 used in legacy handling code ++ * alpha = lowalpha | upalpha ++ */ ++#define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x)) ++ ++/* ++ * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | ++ * "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | ++ * "u" | "v" | "w" | "x" | "y" | "z" ++ */ ++ ++#define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z')) ++ ++/* ++ * upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | ++ * "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | ++ * "U" | "V" | "W" | "X" | "Y" | "Z" ++ */ ++#define IS_UPALPHA(x) (((x) >= 'A') && ((x) <= 'Z')) ++ ++#ifdef IS_DIGIT ++#undef IS_DIGIT ++#endif ++/* ++ * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ++ */ ++#define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9')) ++ ++/* ++ * alphanum = alpha | digit ++ */ ++ ++#define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x)) ++ ++/* ++ * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" ++ */ ++ ++#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') || \ ++ ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') || \ ++ ((x) == '(') || ((x) == ')')) ++ ++/* ++ * unwise = "{" | "}" | "|" | "\" | "^" | "`" ++ */ ++ ++#define IS_UNWISE(p) \ ++ (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \ ++ ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \ ++ ((*(p) == ']')) || ((*(p) == '`'))) ++/* ++ * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | ++ * "[" | "]" ++ */ ++ ++#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \ ++ ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \ ++ ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \ ++ ((x) == ']')) ++ ++/* ++ * unreserved = alphanum | mark ++ */ ++ ++#define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x)) ++ ++/* ++ * Skip to next pointer char, handle escaped sequences ++ */ ++ ++#define NEXT(p) ((*p == '%') ? p += 3 : p++) ++ ++/* ++ * Productions from the spec. ++ * ++ * authority = server | reg_name ++ * reg_name = 1*( unreserved | escaped | "$" | "," | ++ * ";" | ":" | "@" | "&" | "=" | "+" ) ++ * ++ * path = [ abs_path | opaque_part ] ++ */ ++ ++/************************************************************************ ++ * * ++ * RFC 3986 parser * ++ * * ++ ************************************************************************/ ++ ++#define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9')) ++#define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) || \ ++ ((*(p) >= 'A') && (*(p) <= 'Z'))) ++#define ISA_HEXDIG(p) \ ++ (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) || \ ++ ((*(p) >= 'A') && (*(p) <= 'F'))) ++ ++/* ++ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" ++ * / "*" / "+" / "," / ";" / "=" ++ */ ++#define ISA_SUB_DELIM(p) \ ++ (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) || \ ++ ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) || \ ++ ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) || \ ++ ((*(p) == '=')) || ((*(p) == '\''))) ++ ++/* ++ * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" ++ */ ++#define ISA_UNRESERVED(p) \ ++ ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) || \ ++ ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~'))) ++ ++/* ++ * pct-encoded = "%" HEXDIG HEXDIG ++ */ ++#define ISA_PCT_ENCODED(p) \ ++ ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2))) ++ ++/* ++ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" ++ */ ++#define ISA_PCHAR(p) \ ++ (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \ ++ ((*(p) == ':')) || ((*(p) == '@'))) ++ ++/** ++ * rfc3986_parse_scheme: ++ * @uri: pointer to an URI structure ++ * @str: pointer to the string to analyze ++ * ++ * Parse an URI scheme ++ * ++ * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_scheme(URI *uri, const char **str) ++{ ++ const char *cur; ++ ++ if (str == NULL) { ++ return -1; ++ } ++ ++ cur = *str; ++ if (!ISA_ALPHA(cur)) { ++ return 2; ++ } ++ cur++; ++ while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || (*cur == '+') || (*cur == '-') || ++ (*cur == '.')) { ++ cur++; ++ } ++ if (uri != NULL) { ++ g_free(uri->scheme); ++ uri->scheme = g_strndup(*str, cur - *str); ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_fragment: ++ * @uri: pointer to an URI structure ++ * @str: pointer to the string to analyze ++ * ++ * Parse the query part of an URI ++ * ++ * fragment = *( pchar / "/" / "?" ) ++ * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']' ++ * in the fragment identifier but this is used very broadly for ++ * xpointer scheme selection, so we are allowing it here to not break ++ * for example all the DocBook processing chains. ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_fragment(URI *uri, const char **str) ++{ ++ const char *cur; ++ ++ if (str == NULL) { ++ return -1; ++ } ++ ++ cur = *str; ++ ++ while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || ++ (*cur == '[') || (*cur == ']') || ++ ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) { ++ NEXT(cur); ++ } ++ if (uri != NULL) { ++ g_free(uri->fragment); ++ if (uri->cleanup & 2) { ++ uri->fragment = g_strndup(*str, cur - *str); ++ } else { ++ uri->fragment = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_query: ++ * @uri: pointer to an URI structure ++ * @str: pointer to the string to analyze ++ * ++ * Parse the query part of an URI ++ * ++ * query = *uric ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_query(URI *uri, const char **str) ++{ ++ const char *cur; ++ ++ if (str == NULL) { ++ return -1; ++ } ++ ++ cur = *str; ++ ++ while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || ++ ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) { ++ NEXT(cur); ++ } ++ if (uri != NULL) { ++ g_free(uri->query); ++ uri->query = g_strndup(*str, cur - *str); ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_port: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse a port part and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * port = *DIGIT ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_port(URI *uri, const char **str) ++{ ++ const char *cur = *str; ++ int port = 0; ++ ++ if (ISA_DIGIT(cur)) { ++ while (ISA_DIGIT(cur)) { ++ port = port * 10 + (*cur - '0'); ++ if (port > 65535) { ++ return 1; ++ } ++ cur++; ++ } ++ if (uri) { ++ uri->port = port; ++ } ++ *str = cur; ++ return 0; ++ } ++ return 1; ++} ++ ++/** ++ * rfc3986_parse_user_info: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse a user information part and fill in the appropriate fields ++ * of the @uri structure ++ * ++ * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_user_info(URI *uri, const char **str) ++{ ++ const char *cur; ++ ++ cur = *str; ++ while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur) || ++ (*cur == ':')) { ++ NEXT(cur); ++ } ++ if (*cur == '@') { ++ if (uri != NULL) { ++ g_free(uri->user); ++ if (uri->cleanup & 2) { ++ uri->user = g_strndup(*str, cur - *str); ++ } else { ++ uri->user = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } ++ *str = cur; ++ return 0; ++ } ++ return 1; ++} ++ ++/** ++ * rfc3986_parse_dec_octet: ++ * @str: the string to analyze ++ * ++ * dec-octet = DIGIT ; 0-9 ++ * / %x31-39 DIGIT ; 10-99 ++ * / "1" 2DIGIT ; 100-199 ++ * / "2" %x30-34 DIGIT ; 200-249 ++ * / "25" %x30-35 ; 250-255 ++ * ++ * Skip a dec-octet. ++ * ++ * Returns 0 if found and skipped, 1 otherwise ++ */ ++static int rfc3986_parse_dec_octet(const char **str) ++{ ++ const char *cur = *str; ++ ++ if (!(ISA_DIGIT(cur))) { ++ return 1; ++ } ++ if (!ISA_DIGIT(cur + 1)) { ++ cur++; ++ } else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur + 2))) { ++ cur += 2; ++ } else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2))) { ++ cur += 3; ++ } else if ((*cur == '2') && (*(cur + 1) >= '0') && (*(cur + 1) <= '4') && ++ (ISA_DIGIT(cur + 2))) { ++ cur += 3; ++ } else if ((*cur == '2') && (*(cur + 1) == '5') && (*(cur + 2) >= '0') && ++ (*(cur + 1) <= '5')) { ++ cur += 3; ++ } else { ++ return 1; ++ } ++ *str = cur; ++ return 0; ++} ++/** ++ * rfc3986_parse_host: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an host part and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * host = IP-literal / IPv4address / reg-name ++ * IP-literal = "[" ( IPv6address / IPvFuture ) "]" ++ * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet ++ * reg-name = *( unreserved / pct-encoded / sub-delims ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_host(URI *uri, const char **str) ++{ ++ const char *cur = *str; ++ const char *host; ++ ++ host = cur; ++ /* ++ * IPv6 and future addressing scheme are enclosed between brackets ++ */ ++ if (*cur == '[') { ++ cur++; ++ while ((*cur != ']') && (*cur != 0)) { ++ cur++; ++ } ++ if (*cur != ']') { ++ return 1; ++ } ++ cur++; ++ goto found; ++ } ++ /* ++ * try to parse an IPv4 ++ */ ++ if (ISA_DIGIT(cur)) { ++ if (rfc3986_parse_dec_octet(&cur) != 0) { ++ goto not_ipv4; ++ } ++ if (*cur != '.') { ++ goto not_ipv4; ++ } ++ cur++; ++ if (rfc3986_parse_dec_octet(&cur) != 0) { ++ goto not_ipv4; ++ } ++ if (*cur != '.') { ++ goto not_ipv4; ++ } ++ if (rfc3986_parse_dec_octet(&cur) != 0) { ++ goto not_ipv4; ++ } ++ if (*cur != '.') { ++ goto not_ipv4; ++ } ++ if (rfc3986_parse_dec_octet(&cur) != 0) { ++ goto not_ipv4; ++ } ++ goto found; ++ not_ipv4: ++ cur = *str; ++ } ++ /* ++ * then this should be a hostname which can be empty ++ */ ++ while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) { ++ NEXT(cur); ++ } ++found: ++ if (uri != NULL) { ++ g_free(uri->authority); ++ uri->authority = NULL; ++ g_free(uri->server); ++ if (cur != host) { ++ if (uri->cleanup & 2) { ++ uri->server = g_strndup(host, cur - host); ++ } else { ++ uri->server = g_uri_unescape_segment(host, cur, NULL); ++ } ++ } else { ++ uri->server = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_authority: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an authority part and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * authority = [ userinfo "@" ] host [ ":" port ] ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_authority(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ /* ++ * try to parse a userinfo and check for the trailing @ ++ */ ++ ret = rfc3986_parse_user_info(uri, &cur); ++ if ((ret != 0) || (*cur != '@')) { ++ cur = *str; ++ } else { ++ cur++; ++ } ++ ret = rfc3986_parse_host(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ if (*cur == ':') { ++ cur++; ++ ret = rfc3986_parse_port(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_segment: ++ * @str: the string to analyze ++ * @forbid: an optional forbidden character ++ * @empty: allow an empty segment ++ * ++ * Parse a segment and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * segment = *pchar ++ * segment-nz = 1*pchar ++ * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) ++ * ; non-zero-length segment without any colon ":" ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_segment(const char **str, char forbid, int empty) ++{ ++ const char *cur; ++ ++ cur = *str; ++ if (!ISA_PCHAR(cur)) { ++ if (empty) { ++ return 0; ++ } ++ return 1; ++ } ++ while (ISA_PCHAR(cur) && (*cur != forbid)) { ++ NEXT(cur); ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_path_ab_empty: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an path absolute or empty and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * path-abempty = *( "/" segment ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_path_ab_empty(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ while (*cur == '/') { ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 1); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (uri != NULL) { ++ g_free(uri->path); ++ if (*str != cur) { ++ if (uri->cleanup & 2) { ++ uri->path = g_strndup(*str, cur - *str); ++ } else { ++ uri->path = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } else { ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_path_absolute: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an path absolute and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * path-absolute = "/" [ segment-nz *( "/" segment ) ] ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_path_absolute(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ if (*cur != '/') { ++ return 1; ++ } ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 0); ++ if (ret == 0) { ++ while (*cur == '/') { ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 1); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ } ++ if (uri != NULL) { ++ g_free(uri->path); ++ if (cur != *str) { ++ if (uri->cleanup & 2) { ++ uri->path = g_strndup(*str, cur - *str); ++ } else { ++ uri->path = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } else { ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_path_rootless: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an path without root and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * path-rootless = segment-nz *( "/" segment ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_path_rootless(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ ret = rfc3986_parse_segment(&cur, 0, 0); ++ if (ret != 0) { ++ return ret; ++ } ++ while (*cur == '/') { ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 1); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (uri != NULL) { ++ g_free(uri->path); ++ if (cur != *str) { ++ if (uri->cleanup & 2) { ++ uri->path = g_strndup(*str, cur - *str); ++ } else { ++ uri->path = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } else { ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_path_no_scheme: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an path which is not a scheme and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * path-noscheme = segment-nz-nc *( "/" segment ) ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_path_no_scheme(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ ret = rfc3986_parse_segment(&cur, ':', 0); ++ if (ret != 0) { ++ return ret; ++ } ++ while (*cur == '/') { ++ cur++; ++ ret = rfc3986_parse_segment(&cur, 0, 1); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (uri != NULL) { ++ g_free(uri->path); ++ if (cur != *str) { ++ if (uri->cleanup & 2) { ++ uri->path = g_strndup(*str, cur - *str); ++ } else { ++ uri->path = g_uri_unescape_segment(*str, cur, NULL); ++ } ++ } else { ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_hier_part: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an hierarchical part and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * hier-part = "//" authority path-abempty ++ * / path-absolute ++ * / path-rootless ++ * / path-empty ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_hier_part(URI *uri, const char **str) ++{ ++ const char *cur; ++ int ret; ++ ++ cur = *str; ++ ++ if ((*cur == '/') && (*(cur + 1) == '/')) { ++ cur += 2; ++ ret = rfc3986_parse_authority(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ ret = rfc3986_parse_path_ab_empty(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ *str = cur; ++ return 0; ++ } else if (*cur == '/') { ++ ret = rfc3986_parse_path_absolute(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ } else if (ISA_PCHAR(cur)) { ++ ret = rfc3986_parse_path_rootless(uri, &cur); ++ if (ret != 0) { ++ return ret; ++ } ++ } else { ++ /* path-empty is effectively empty */ ++ if (uri != NULL) { ++ g_free(uri->path); ++ uri->path = NULL; ++ } ++ } ++ *str = cur; ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_relative_ref: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an URI string and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * relative-ref = relative-part [ "?" query ] [ "#" fragment ] ++ * relative-part = "//" authority path-abempty ++ * / path-absolute ++ * / path-noscheme ++ * / path-empty ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_relative_ref(URI *uri, const char *str) ++{ ++ int ret; ++ ++ if ((*str == '/') && (*(str + 1) == '/')) { ++ str += 2; ++ ret = rfc3986_parse_authority(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ ret = rfc3986_parse_path_ab_empty(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } else if (*str == '/') { ++ ret = rfc3986_parse_path_absolute(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } else if (ISA_PCHAR(str)) { ++ ret = rfc3986_parse_path_no_scheme(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } else { ++ /* path-empty is effectively empty */ ++ if (uri != NULL) { ++ g_free(uri->path); ++ uri->path = NULL; ++ } ++ } ++ ++ if (*str == '?') { ++ str++; ++ ret = rfc3986_parse_query(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (*str == '#') { ++ str++; ++ ret = rfc3986_parse_fragment(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (*str != 0) { ++ uri_clean(uri); ++ return 1; ++ } ++ return 0; ++} ++ ++/** ++ * rfc3986_parse: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an URI string and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * scheme ":" hier-part [ "?" query ] [ "#" fragment ] ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse(URI *uri, const char *str) ++{ ++ int ret; ++ ++ ret = rfc3986_parse_scheme(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ if (*str != ':') { ++ return 1; ++ } ++ str++; ++ ret = rfc3986_parse_hier_part(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ if (*str == '?') { ++ str++; ++ ret = rfc3986_parse_query(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (*str == '#') { ++ str++; ++ ret = rfc3986_parse_fragment(uri, &str); ++ if (ret != 0) { ++ return ret; ++ } ++ } ++ if (*str != 0) { ++ uri_clean(uri); ++ return 1; ++ } ++ return 0; ++} ++ ++/** ++ * rfc3986_parse_uri_reference: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an URI reference string and fills in the appropriate fields ++ * of the @uri structure ++ * ++ * URI-reference = URI / relative-ref ++ * ++ * Returns 0 or the error code ++ */ ++static int rfc3986_parse_uri_reference(URI *uri, const char *str) ++{ ++ int ret; ++ ++ if (str == NULL) { ++ return -1; ++ } ++ uri_clean(uri); ++ ++ /* ++ * Try first to parse absolute refs, then fallback to relative if ++ * it fails. ++ */ ++ ret = rfc3986_parse(uri, str); ++ if (ret != 0) { ++ uri_clean(uri); ++ ret = rfc3986_parse_relative_ref(uri, str); ++ if (ret != 0) { ++ uri_clean(uri); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * uri_parse: ++ * @str: the URI string to analyze ++ * ++ * Parse an URI based on RFC 3986 ++ * ++ * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] ++ * ++ * Returns a newly built URI or NULL in case of error ++ */ ++URI *uri_parse(const char *str) ++{ ++ URI *uri; ++ int ret; ++ ++ if (str == NULL) { ++ return NULL; ++ } ++ uri = uri_new(); ++ ret = rfc3986_parse_uri_reference(uri, str); ++ if (ret) { ++ uri_free(uri); ++ return NULL; ++ } ++ return uri; ++} ++ ++/** ++ * uri_parse_into: ++ * @uri: pointer to an URI structure ++ * @str: the string to analyze ++ * ++ * Parse an URI reference string based on RFC 3986 and fills in the ++ * appropriate fields of the @uri structure ++ * ++ * URI-reference = URI / relative-ref ++ * ++ * Returns 0 or the error code ++ */ ++int uri_parse_into(URI *uri, const char *str) ++{ ++ return rfc3986_parse_uri_reference(uri, str); ++} ++ ++/** ++ * uri_parse_raw: ++ * @str: the URI string to analyze ++ * @raw: if 1 unescaping of URI pieces are disabled ++ * ++ * Parse an URI but allows to keep intact the original fragments. ++ * ++ * URI-reference = URI / relative-ref ++ * ++ * Returns a newly built URI or NULL in case of error ++ */ ++URI *uri_parse_raw(const char *str, int raw) ++{ ++ URI *uri; ++ int ret; ++ ++ if (str == NULL) { ++ return NULL; ++ } ++ uri = uri_new(); ++ if (raw) { ++ uri->cleanup |= 2; ++ } ++ ret = uri_parse_into(uri, str); ++ if (ret) { ++ uri_free(uri); ++ return NULL; ++ } ++ return uri; ++} ++ ++/************************************************************************ ++ * * ++ * Generic URI structure functions * ++ * * ++ ************************************************************************/ ++ ++/** ++ * uri_new: ++ * ++ * Simply creates an empty URI ++ * ++ * Returns the new structure or NULL in case of error ++ */ ++URI *uri_new(void) ++{ ++ return g_new0(URI, 1); ++} ++ ++/** ++ * realloc2n: ++ * ++ * Function to handle properly a reallocation when saving an URI ++ * Also imposes some limit on the length of an URI string output ++ */ ++static char *realloc2n(char *ret, int *max) ++{ ++ char *temp; ++ int tmp; ++ ++ tmp = *max * 2; ++ temp = g_realloc(ret, (tmp + 1)); ++ *max = tmp; ++ return temp; ++} ++ ++/** ++ * uri_to_string: ++ * @uri: pointer to an URI ++ * ++ * Save the URI as an escaped string ++ * ++ * Returns a new string (to be deallocated by caller) ++ */ ++char *uri_to_string(URI *uri) ++{ ++ char *ret = NULL; ++ char *temp; ++ const char *p; ++ int len; ++ int max; ++ ++ if (uri == NULL) { ++ return NULL; ++ } ++ ++ max = 80; ++ ret = g_malloc(max + 1); ++ len = 0; ++ ++ if (uri->scheme != NULL) { ++ p = uri->scheme; ++ while (*p != 0) { ++ if (len >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = *p++; ++ } ++ if (len >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = ':'; ++ } ++ if (uri->opaque != NULL) { ++ p = uri->opaque; ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ } else { ++ if (uri->server != NULL) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '/'; ++ ret[len++] = '/'; ++ if (uri->user != NULL) { ++ p = uri->user; ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if ((IS_UNRESERVED(*(p))) || ((*(p) == ';')) || ++ ((*(p) == ':')) || ((*(p) == '&')) || ((*(p) == '=')) || ++ ((*(p) == '+')) || ((*(p) == '$')) || ((*(p) == ','))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '@'; ++ } ++ p = uri->server; ++ while (*p != 0) { ++ if (len >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = *p++; ++ } ++ if (uri->port > 0) { ++ if (len + 10 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ len += snprintf(&ret[len], max - len, ":%d", uri->port); ++ } ++ } else if (uri->authority != NULL) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '/'; ++ ret[len++] = '/'; ++ p = uri->authority; ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if ((IS_UNRESERVED(*(p))) || ((*(p) == '$')) || ++ ((*(p) == ',')) || ((*(p) == ';')) || ((*(p) == ':')) || ++ ((*(p) == '@')) || ((*(p) == '&')) || ((*(p) == '=')) || ++ ((*(p) == '+'))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ } else if (uri->scheme != NULL) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '/'; ++ ret[len++] = '/'; ++ } ++ if (uri->path != NULL) { ++ p = uri->path; ++ /* ++ * the colon in file:///d: should not be escaped or ++ * Windows accesses fail later. ++ */ ++ if ((uri->scheme != NULL) && (p[0] == '/') && ++ (((p[1] >= 'a') && (p[1] <= 'z')) || ++ ((p[1] >= 'A') && (p[1] <= 'Z'))) && ++ (p[2] == ':') && (!strcmp(uri->scheme, "file"))) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = *p++; ++ ret[len++] = *p++; ++ ret[len++] = *p++; ++ } ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) || ++ ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) || ++ ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) || ++ ((*(p) == ','))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ } ++ if (uri->query != NULL) { ++ if (len + 1 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '?'; ++ p = uri->query; ++ while (*p != 0) { ++ if (len + 1 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = *p++; ++ } ++ } ++ } ++ if (uri->fragment != NULL) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len++] = '#'; ++ p = uri->fragment; ++ while (*p != 0) { ++ if (len + 3 >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) { ++ ret[len++] = *p++; ++ } else { ++ int val = *(unsigned char *)p++; ++ int hi = val / 0x10, lo = val % 0x10; ++ ret[len++] = '%'; ++ ret[len++] = hi + (hi > 9 ? 'A' - 10 : '0'); ++ ret[len++] = lo + (lo > 9 ? 'A' - 10 : '0'); ++ } ++ } ++ } ++ if (len >= max) { ++ temp = realloc2n(ret, &max); ++ ret = temp; ++ } ++ ret[len] = 0; ++ return ret; ++} ++ ++/** ++ * uri_clean: ++ * @uri: pointer to an URI ++ * ++ * Make sure the URI struct is free of content ++ */ ++static void uri_clean(URI *uri) ++{ ++ if (uri == NULL) { ++ return; ++ } ++ ++ g_free(uri->scheme); ++ uri->scheme = NULL; ++ g_free(uri->server); ++ uri->server = NULL; ++ g_free(uri->user); ++ uri->user = NULL; ++ g_free(uri->path); ++ uri->path = NULL; ++ g_free(uri->fragment); ++ uri->fragment = NULL; ++ g_free(uri->opaque); ++ uri->opaque = NULL; ++ g_free(uri->authority); ++ uri->authority = NULL; ++ g_free(uri->query); ++ uri->query = NULL; ++} ++ ++/** ++ * uri_free: ++ * @uri: pointer to an URI, NULL is ignored ++ * ++ * Free up the URI struct ++ */ ++void uri_free(URI *uri) ++{ ++ uri_clean(uri); ++ g_free(uri); ++} ++ ++/************************************************************************ ++ * * ++ * Public functions * ++ * * ++ ************************************************************************/ ++ ++/* ++ * Utility functions to help parse and assemble query strings. ++ */ ++ ++struct QueryParams *query_params_new(int init_alloc) ++{ ++ struct QueryParams *ps; ++ ++ if (init_alloc <= 0) { ++ init_alloc = 1; ++ } ++ ++ ps = g_new(QueryParams, 1); ++ ps->n = 0; ++ ps->alloc = init_alloc; ++ ps->p = g_new(QueryParam, ps->alloc); ++ ++ return ps; ++} ++ ++/* Ensure there is space to store at least one more parameter ++ * at the end of the set. ++ */ ++static int query_params_append(struct QueryParams *ps, const char *name, ++ const char *value) ++{ ++ if (ps->n >= ps->alloc) { ++ ps->p = g_renew(QueryParam, ps->p, ps->alloc * 2); ++ ps->alloc *= 2; ++ } ++ ++ ps->p[ps->n].name = g_strdup(name); ++ ps->p[ps->n].value = g_strdup(value); ++ ps->p[ps->n].ignore = 0; ++ ps->n++; ++ ++ return 0; ++} ++ ++void query_params_free(struct QueryParams *ps) ++{ ++ int i; ++ ++ for (i = 0; i < ps->n; ++i) { ++ g_free(ps->p[i].name); ++ g_free(ps->p[i].value); ++ } ++ g_free(ps->p); ++ g_free(ps); ++} ++ ++struct QueryParams *query_params_parse(const char *query) ++{ ++ struct QueryParams *ps; ++ const char *end, *eq; ++ ++ ps = query_params_new(0); ++ if (!query || query[0] == '\0') { ++ return ps; ++ } ++ ++ while (*query) { ++ char *name = NULL, *value = NULL; ++ ++ /* Find the next separator, or end of the string. */ ++ end = strchr(query, '&'); ++ if (!end) { ++ end = qemu_strchrnul(query, ';'); ++ } ++ ++ /* Find the first '=' character between here and end. */ ++ eq = strchr(query, '='); ++ if (eq && eq >= end) { ++ eq = NULL; ++ } ++ ++ /* Empty section (eg. "&&"). */ ++ if (end == query) { ++ goto next; ++ } ++ ++ /* If there is no '=' character, then we have just "name" ++ * and consistent with CGI.pm we assume value is "". ++ */ ++ else if (!eq) { ++ name = g_uri_unescape_segment(query, end, NULL); ++ value = NULL; ++ } ++ /* Or if we have "name=" here (works around annoying ++ * problem when calling uri_string_unescape with len = 0). ++ */ ++ else if (eq + 1 == end) { ++ name = g_uri_unescape_segment(query, eq, NULL); ++ value = g_new0(char, 1); ++ } ++ /* If the '=' character is at the beginning then we have ++ * "=value" and consistent with CGI.pm we _ignore_ this. ++ */ ++ else if (query == eq) { ++ goto next; ++ } ++ ++ /* Otherwise it's "name=value". */ ++ else { ++ name = g_uri_unescape_segment(query, eq, NULL); ++ value = g_uri_unescape_segment(eq + 1, end, NULL); ++ } ++ ++ /* Append to the parameter set. */ ++ query_params_append(ps, name, value); ++ g_free(name); ++ g_free(value); ++ ++ next: ++ query = end; ++ if (*query) { ++ query++; /* skip '&' separator */ ++ } ++ } ++ ++ return ps; ++} +diff --git a/qcow2/lib/util/yank.c b/qcow2/lib/util/yank.c +new file mode 100644 +index 00000000..eaac5053 +--- /dev/null ++++ b/qcow2/lib/util/yank.c +@@ -0,0 +1,199 @@ ++/* ++ * QEMU yank feature ++ * ++ * Copyright (c) Lukas Straub ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "qemu/thread.h" ++#include "qemu/queue.h" ++#include "qemu/lockable.h" ++#include "qapi/qapi-commands-yank.h" ++#include "qapi/qapi-visit-yank.h" ++#include "qapi/clone-visitor.h" ++#include "qemu/yank.h" ++ ++struct YankFuncAndParam { ++ YankFn *func; ++ void *opaque; ++ QLIST_ENTRY(YankFuncAndParam) next; ++}; ++ ++struct YankInstanceEntry { ++ YankInstance *instance; ++ QLIST_HEAD(, YankFuncAndParam) yankfns; ++ QLIST_ENTRY(YankInstanceEntry) next; ++}; ++ ++typedef struct YankFuncAndParam YankFuncAndParam; ++typedef struct YankInstanceEntry YankInstanceEntry; ++ ++/* ++ * This lock protects the yank_instance_list below. Because it's taken by ++ * OOB-capable commands, it must be "fast", i.e. it may only be held for a ++ * bounded, short time. See docs/devel/qapi-code-gen.rst for additional ++ * information. ++ */ ++static QemuMutex yank_lock; ++ ++static QLIST_HEAD(, YankInstanceEntry) yank_instance_list ++ = QLIST_HEAD_INITIALIZER(yank_instance_list); ++ ++static bool yank_instance_equal(const YankInstance *a, const YankInstance *b) ++{ ++ if (a->type != b->type) { ++ return false; ++ } ++ ++ switch (a->type) { ++ case YANK_INSTANCE_TYPE_BLOCK_NODE: ++ return g_str_equal(a->u.block_node.node_name, ++ b->u.block_node.node_name); ++ ++ case YANK_INSTANCE_TYPE_CHARDEV: ++ return g_str_equal(a->u.chardev.id, b->u.chardev.id); ++ ++ case YANK_INSTANCE_TYPE_MIGRATION: ++ return true; ++ ++ default: ++ abort(); ++ } ++} ++ ++static YankInstanceEntry *yank_find_entry(const YankInstance *instance) ++{ ++ YankInstanceEntry *entry; ++ ++ QLIST_FOREACH(entry, &yank_instance_list, next) { ++ if (yank_instance_equal(entry->instance, instance)) { ++ return entry; ++ } ++ } ++ return NULL; ++} ++ ++bool yank_register_instance(const YankInstance *instance, Error **errp) ++{ ++ YankInstanceEntry *entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ ++ if (yank_find_entry(instance)) { ++ error_setg(errp, "duplicate yank instance"); ++ return false; ++ } ++ ++ entry = g_new0(YankInstanceEntry, 1); ++ entry->instance = QAPI_CLONE(YankInstance, instance); ++ QLIST_INIT(&entry->yankfns); ++ QLIST_INSERT_HEAD(&yank_instance_list, entry, next); ++ ++ return true; ++} ++ ++void yank_unregister_instance(const YankInstance *instance) ++{ ++ YankInstanceEntry *entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ entry = yank_find_entry(instance); ++ assert(entry); ++ ++ assert(QLIST_EMPTY(&entry->yankfns)); ++ QLIST_REMOVE(entry, next); ++ qapi_free_YankInstance(entry->instance); ++ g_free(entry); ++} ++ ++void yank_register_function(const YankInstance *instance, ++ YankFn *func, ++ void *opaque) ++{ ++ YankInstanceEntry *entry; ++ YankFuncAndParam *func_entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ entry = yank_find_entry(instance); ++ assert(entry); ++ ++ func_entry = g_new0(YankFuncAndParam, 1); ++ func_entry->func = func; ++ func_entry->opaque = opaque; ++ ++ QLIST_INSERT_HEAD(&entry->yankfns, func_entry, next); ++} ++ ++void yank_unregister_function(const YankInstance *instance, ++ YankFn *func, ++ void *opaque) ++{ ++ YankInstanceEntry *entry; ++ YankFuncAndParam *func_entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ entry = yank_find_entry(instance); ++ assert(entry); ++ ++ QLIST_FOREACH(func_entry, &entry->yankfns, next) { ++ if (func_entry->func == func && func_entry->opaque == opaque) { ++ QLIST_REMOVE(func_entry, next); ++ g_free(func_entry); ++ return; ++ } ++ } ++ ++ abort(); ++} ++ ++void qmp_yank(YankInstanceList *instances, ++ Error **errp) ++{ ++ YankInstanceList *tail; ++ YankInstanceEntry *entry; ++ YankFuncAndParam *func_entry; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ for (tail = instances; tail; tail = tail->next) { ++ entry = yank_find_entry(tail->value); ++ if (!entry) { ++ error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, "Instance not found"); ++ return; ++ } ++ } ++ for (tail = instances; tail; tail = tail->next) { ++ entry = yank_find_entry(tail->value); ++ assert(entry); ++ QLIST_FOREACH(func_entry, &entry->yankfns, next) { ++ func_entry->func(func_entry->opaque); ++ } ++ } ++} ++ ++YankInstanceList *qmp_query_yank(Error **errp) ++{ ++ YankInstanceEntry *entry; ++ YankInstanceList *ret; ++ ++ ret = NULL; ++ ++ QEMU_LOCK_GUARD(&yank_lock); ++ QLIST_FOREACH(entry, &yank_instance_list, next) { ++ YankInstanceList *new_entry; ++ new_entry = g_new0(YankInstanceList, 1); ++ new_entry->value = QAPI_CLONE(YankInstance, entry->instance); ++ new_entry->next = ret; ++ ret = new_entry; ++ } ++ ++ return ret; ++} ++ ++static void __attribute__((__constructor__)) yank_init(void) ++{ ++ qemu_mutex_init(&yank_lock); ++} +diff --git a/qcow2/qemu-config.c b/qcow2/qemu-config.c +new file mode 100644 +index 00000000..a90c18da +--- /dev/null ++++ b/qcow2/qemu-config.c +@@ -0,0 +1,295 @@ ++#include "qemu/osdep.h" ++#include "block/qdict.h" /* for qdict_extract_subqdict() */ ++#include "qapi/error.h" ++#include "qapi/qmp/qdict.h" ++#include "qapi/qmp/qlist.h" ++#include "qemu/error-report.h" ++#include "qemu/option.h" ++#include "qemu/config-file.h" ++ ++QemuOptsList *vm_config_groups[48]; ++QemuOptsList *drive_config_groups[5]; ++ ++static QemuOptsList *find_list(QemuOptsList **lists, const char *group, ++ Error **errp) ++{ ++ int i; ++ ++ qemu_load_module_for_opts(group); ++ for (i = 0; lists[i] != NULL; i++) { ++ if (strcmp(lists[i]->name, group) == 0) ++ break; ++ } ++ if (lists[i] == NULL) { ++ error_setg(errp, "There is no option group '%s'", group); ++ } ++ return lists[i]; ++} ++ ++QemuOptsList *qemu_find_opts(const char *group) ++{ ++ QemuOptsList *ret; ++ Error *local_err = NULL; ++ ++ ret = find_list(vm_config_groups, group, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ } ++ ++ return ret; ++} ++ ++QemuOpts *qemu_find_opts_singleton(const char *group) ++{ ++ QemuOptsList *list; ++ QemuOpts *opts; ++ ++ list = qemu_find_opts(group); ++ assert(list); ++ opts = qemu_opts_find(list, NULL); ++ if (!opts) { ++ opts = qemu_opts_create(list, NULL, 0, &error_abort); ++ } ++ return opts; ++} ++ ++QemuOptsList *qemu_find_opts_err(const char *group, Error **errp) ++{ ++ return find_list(vm_config_groups, group, errp); ++} ++ ++void qemu_add_drive_opts(QemuOptsList *list) ++{ ++ int entries, i; ++ ++ entries = ARRAY_SIZE(drive_config_groups); ++ entries--; /* keep list NULL terminated */ ++ for (i = 0; i < entries; i++) { ++ if (drive_config_groups[i] == NULL) { ++ drive_config_groups[i] = list; ++ return; ++ } ++ } ++ fprintf(stderr, "ran out of space in drive_config_groups"); ++ abort(); ++} ++ ++void qemu_add_opts(QemuOptsList *list) ++{ ++ int entries, i; ++ ++ entries = ARRAY_SIZE(vm_config_groups); ++ entries--; /* keep list NULL terminated */ ++ for (i = 0; i < entries; i++) { ++ if (vm_config_groups[i] == NULL) { ++ vm_config_groups[i] = list; ++ return; ++ } ++ } ++ fprintf(stderr, "ran out of space in vm_config_groups"); ++ abort(); ++} ++ ++/* Returns number of config groups on success, -errno on error */ ++static int qemu_config_foreach(FILE *fp, QEMUConfigCB *cb, void *opaque, ++ const char *fname, Error **errp) ++{ ++ ERRP_GUARD(); ++ char line[1024], prev_group[64], group[64], arg[64], value[1024]; ++ Location loc; ++ QDict *qdict = NULL; ++ int res = -EINVAL, lno = 0; ++ int count = 0; ++ ++ loc_push_none(&loc); ++ while (fgets(line, sizeof(line), fp) != NULL) { ++ ++lno; ++ if (line[0] == '\n') { ++ /* skip empty lines */ ++ continue; ++ } ++ if (line[0] == '#') { ++ /* comment */ ++ continue; ++ } ++ if (line[0] == '[') { ++ QDict *prev = qdict; ++ if (sscanf(line, "[%63s \"%63[^\"]\"]", group, value) == 2) { ++ qdict = qdict_new(); ++ qdict_put_str(qdict, "id", value); ++ count++; ++ } else if (sscanf(line, "[%63[^]]]", group) == 1) { ++ qdict = qdict_new(); ++ count++; ++ } ++ if (qdict != prev) { ++ if (prev) { ++ cb(prev_group, prev, opaque, errp); ++ qobject_unref(prev); ++ if (*errp) { ++ goto out; ++ } ++ } ++ strcpy(prev_group, group); ++ continue; ++ } ++ } ++ loc_set_file(fname, lno); ++ value[0] = '\0'; ++ if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2 || ++ sscanf(line, " %63s = \"\"", arg) == 1) { ++ /* arg = value */ ++ if (qdict == NULL) { ++ error_setg(errp, "no group defined"); ++ goto out; ++ } ++ qdict_put_str(qdict, arg, value); ++ continue; ++ } ++ error_setg(errp, "parse error"); ++ goto out; ++ } ++ if (ferror(fp)) { ++ loc_pop(&loc); ++ error_setg_errno(errp, errno, "Cannot read config file"); ++ goto out_no_loc; ++ } ++ res = count; ++ if (qdict) { ++ cb(group, qdict, opaque, errp); ++ } ++out: ++ loc_pop(&loc); ++out_no_loc: ++ qobject_unref(qdict); ++ return res; ++} ++ ++void qemu_config_do_parse(const char *group, QDict *qdict, void *opaque, Error **errp) ++{ ++ QemuOptsList **lists = opaque; ++ QemuOptsList *list; ++ ++ list = find_list(lists, group, errp); ++ if (!list) { ++ return; ++ } ++ ++ qemu_opts_from_qdict(list, qdict, errp); ++} ++ ++int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname, Error **errp) ++{ ++ return qemu_config_foreach(fp, qemu_config_do_parse, lists, fname, errp); ++} ++ ++int qemu_read_config_file(const char *filename, QEMUConfigCB *cb, Error **errp) ++{ ++ FILE *f = fopen(filename, "r"); ++ int ret; ++ ++ if (f == NULL) { ++ error_setg_file_open(errp, errno, filename); ++ return -errno; ++ } ++ ++ ret = qemu_config_foreach(f, cb, vm_config_groups, filename, errp); ++ fclose(f); ++ return ret; ++} ++ ++static bool config_parse_qdict_section(QDict *options, QemuOptsList *opts, ++ Error **errp) ++{ ++ QemuOpts *subopts; ++ g_autoptr(QDict) subqdict = NULL; ++ g_autoptr(QList) list = NULL; ++ size_t orig_size, enum_size; ++ char *prefix; ++ ++ prefix = g_strdup_printf("%s.", opts->name); ++ qdict_extract_subqdict(options, &subqdict, prefix); ++ g_free(prefix); ++ orig_size = qdict_size(subqdict); ++ if (!orig_size) { ++ return true; ++ } ++ ++ subopts = qemu_opts_create(opts, NULL, 0, errp); ++ if (!subopts) { ++ return false; ++ } ++ ++ if (!qemu_opts_absorb_qdict(subopts, subqdict, errp)) { ++ return false; ++ } ++ ++ enum_size = qdict_size(subqdict); ++ if (enum_size < orig_size && enum_size) { ++ error_setg(errp, "Unknown option '%s' for [%s]", ++ qdict_first(subqdict)->key, opts->name); ++ return false; ++ } ++ ++ if (enum_size) { ++ /* Multiple, enumerated sections */ ++ QListEntry *list_entry; ++ unsigned i = 0; ++ ++ /* Not required anymore */ ++ qemu_opts_del(subopts); ++ ++ qdict_array_split(subqdict, &list); ++ if (qdict_size(subqdict)) { ++ error_setg(errp, "Unused option '%s' for [%s]", ++ qdict_first(subqdict)->key, opts->name); ++ return false; ++ } ++ ++ QLIST_FOREACH_ENTRY(list, list_entry) { ++ QDict *section = qobject_to(QDict, qlist_entry_obj(list_entry)); ++ char *opt_name; ++ ++ if (!section) { ++ error_setg(errp, "[%s] section (index %u) does not consist of " ++ "keys", opts->name, i); ++ return false; ++ } ++ ++ opt_name = g_strdup_printf("%s.%u", opts->name, i++); ++ subopts = qemu_opts_create(opts, opt_name, 1, errp); ++ g_free(opt_name); ++ if (!subopts) { ++ return false; ++ } ++ ++ if (!qemu_opts_absorb_qdict(subopts, section, errp)) { ++ qemu_opts_del(subopts); ++ return false; ++ } ++ ++ if (qdict_size(section)) { ++ error_setg(errp, "[%s] section doesn't support the option '%s'", ++ opts->name, qdict_first(section)->key); ++ qemu_opts_del(subopts); ++ return false; ++ } ++ } ++ } ++ ++ return true; ++} ++ ++bool qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, ++ Error **errp) ++{ ++ int i; ++ ++ for (i = 0; lists[i]; i++) { ++ if (!config_parse_qdict_section(options, lists[i], errp)) { ++ return false; ++ } ++ } ++ ++ return true; ++} +diff --git a/qcow2/qemu-img-cmds.h b/qcow2/qemu-img-cmds.h +new file mode 100644 +index 00000000..a045b509 +--- /dev/null ++++ b/qcow2/qemu-img-cmds.h +@@ -0,0 +1,46 @@ ++ ++ ++DEF("amend", img_amend, ++"amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] [--force] -o options filename") ++ ++DEF("bench", img_bench, ++"bench [-c count] [-d depth] [-f fmt] [--flush-interval=flush_interval] [-i aio] [-n] [--no-drain] [-o offset] [--pattern=pattern] [-q] [-s buffer_size] [-S step_size] [-t cache] [-w] [-U] filename") ++ ++DEF("bitmap", img_bitmap, ++"bitmap (--merge SOURCE | --add | --remove | --clear | --enable | --disable)... [-b source_file [-F source_fmt]] [-g granularity] [--object objectdef] [--image-opts | -f fmt] filename bitmap") ++ ++DEF("check", img_check, ++"check [--object objectdef] [--image-opts] [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename") ++ ++DEF("commit", img_commit, ++"commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-r rate_limit] [-d] [-p] filename") ++ ++DEF("compare", img_compare, ++"compare [--object objectdef] [--image-opts] [-f fmt] [-F fmt] [-T src_cache] [-p] [-q] [-s] [-U] filename1 filename2") ++ ++DEF("convert", img_convert, ++"convert [--object objectdef] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file [-F backing_fmt]] [-o options] [-l snapshot_param] [-S sparse_size] [-r rate_limit] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename") ++ ++DEF("create", img_create, ++"create [--object objectdef] [-q] [-f fmt] [-b backing_file [-F backing_fmt]] [-u] [-o options] filename [size]") ++ ++DEF("dd", img_dd, ++"dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output") ++ ++DEF("info", img_info, ++"info [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [--backing-chain] [-U] filename") ++ ++DEF("map", img_map, ++"map [--object objectdef] [--image-opts] [-f fmt] [--start-offset=offset] [--max-length=len] [--output=ofmt] [-U] filename") ++ ++DEF("measure", img_measure, ++"measure [--output=ofmt] [-O output_fmt] [-o options] [--size N | [--object objectdef] [--image-opts] [-f fmt] [-l snapshot_param] filename]") ++ ++DEF("snapshot", img_snapshot, ++"snapshot [--object objectdef] [--image-opts] [-U] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") ++ ++DEF("rebase", img_rebase, ++"rebase [--object objectdef] [--image-opts] [-U] [-q] [-f fmt] [-t cache] [-T src_cache] [-p] [-u] [-c] -b backing_file [-F backing_fmt] filename") ++ ++DEF("resize", img_resize, ++"resize [--object objectdef] [--image-opts] [-f fmt] [--preallocation=prealloc] [-q] [--shrink] filename [+ | -]size") +diff --git a/qcow2/qemu-img.c b/qcow2/qemu-img.c +new file mode 100644 +index 00000000..7668f867 +--- /dev/null ++++ b/qcow2/qemu-img.c +@@ -0,0 +1,5621 @@ ++/* ++ * QEMU disk image utility ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include ++ ++#include "qemu/help-texts.h" ++#include "qemu/qemu-progress.h" ++#include "qemu-version.h" ++#include "qapi/error.h" ++#include "qapi/qapi-commands-block-core.h" ++#include "qapi/qapi-visit-block-core.h" ++#include "qapi/qobject-output-visitor.h" ++#include "qapi/qmp/qjson.h" ++#include "qapi/qmp/qdict.h" ++#include "qemu/cutils.h" ++#include "qemu/config-file.h" ++#include "qemu/option.h" ++#include "qemu/error-report.h" ++#include "qemu/log.h" ++#include "qemu/main-loop.h" ++#include "qemu/module.h" ++#include "qemu/sockets.h" ++#include "qemu/units.h" ++#include "qemu/memalign.h" ++#include "qom/object_interfaces.h" ++#include "sysemu/block-backend.h" ++#include "block/block_int.h" ++#include "block/blockjob.h" ++#include "block/dirty-bitmap.h" ++#include "block/qapi.h" ++#include "crypto/init.h" ++#include "trace/control.h" ++#include "qemu/throttle.h" ++#include "block/throttle-groups.h" ++ ++#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \ ++ "\n" QEMU_COPYRIGHT "\n" ++ ++typedef struct img_cmd_t { ++ const char *name; ++ int (*handler)(int argc, char **argv); ++} img_cmd_t; ++ ++enum { ++ OPTION_OUTPUT = 256, ++ OPTION_BACKING_CHAIN = 257, ++ OPTION_OBJECT = 258, ++ OPTION_IMAGE_OPTS = 259, ++ OPTION_PATTERN = 260, ++ OPTION_FLUSH_INTERVAL = 261, ++ OPTION_NO_DRAIN = 262, ++ OPTION_TARGET_IMAGE_OPTS = 263, ++ OPTION_SIZE = 264, ++ OPTION_PREALLOCATION = 265, ++ OPTION_SHRINK = 266, ++ OPTION_SALVAGE = 267, ++ OPTION_TARGET_IS_ZERO = 268, ++ OPTION_ADD = 269, ++ OPTION_REMOVE = 270, ++ OPTION_CLEAR = 271, ++ OPTION_ENABLE = 272, ++ OPTION_DISABLE = 273, ++ OPTION_MERGE = 274, ++ OPTION_BITMAPS = 275, ++ OPTION_FORCE = 276, ++ OPTION_SKIP_BROKEN = 277, ++}; ++ ++typedef enum OutputFormat { ++ OFORMAT_JSON, ++ OFORMAT_HUMAN, ++} OutputFormat; ++ ++/* Default to cache=writeback as data integrity is not important for qemu-img */ ++#define BDRV_DEFAULT_CACHE "writeback" ++ ++static void format_print(void *opaque, const char *name) ++{ ++ printf(" %s", name); ++} ++ ++static G_NORETURN G_GNUC_PRINTF(1, 2) ++void error_exit(const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ error_vreport(fmt, ap); ++ va_end(ap); ++ ++ error_printf("Try 'qemu-img --help' for more information\n"); ++ exit(EXIT_FAILURE); ++} ++ ++static G_NORETURN ++void missing_argument(const char *option) ++{ ++ error_exit("missing argument for option '%s'", option); ++} ++ ++static G_NORETURN ++void unrecognized_option(const char *option) ++{ ++ error_exit("unrecognized option '%s'", option); ++} ++ ++/* Please keep in synch with docs/tools/qemu-img.rst */ ++static G_NORETURN ++void help(void) ++{ ++ const char *help_msg = ++ QEMU_IMG_VERSION ++ "usage: qemu-img [standard options] command [command options]\n" ++ "QEMU disk image utility\n" ++ "\n" ++ " '-h', '--help' display this help and exit\n" ++ " '-V', '--version' output version information and exit\n" ++ " '-T', '--trace' [[enable=]][,events=][,file=]\n" ++ " specify tracing options\n" ++ "\n" ++ "Command syntax:\n" ++#define DEF(option, callback, arg_string) \ ++ " " arg_string "\n" ++#include "qemu-img-cmds.h" ++#undef DEF ++ "\n" ++ "Command parameters:\n" ++ " 'filename' is a disk image filename\n" ++ " 'objectdef' is a QEMU user creatable object definition. See the qemu(1)\n" ++ " manual page for a description of the object properties. The most common\n" ++ " object type is a 'secret', which is used to supply passwords and/or\n" ++ " encryption keys.\n" ++ " 'fmt' is the disk image format. It is guessed automatically in most cases\n" ++ " 'cache' is the cache mode used to write the output disk image, the valid\n" ++ " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" ++ " 'directsync' and 'unsafe' (default for convert)\n" ++ " 'src_cache' is the cache mode used to read input disk images, the valid\n" ++ " options are the same as for the 'cache' option\n" ++ " 'size' is the disk image size in bytes. Optional suffixes\n" ++ " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n" ++ " 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are\n" ++ " supported. 'b' is ignored.\n" ++ " 'output_filename' is the destination disk image filename\n" ++ " 'output_fmt' is the destination format\n" ++ " 'options' is a comma separated list of format specific options in a\n" ++ " name=value format. Use -o help for an overview of the options supported by\n" ++ " the used format\n" ++ " 'snapshot_param' is param used for internal snapshot, format\n" ++ " is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" ++ " '[ID_OR_NAME]'\n" ++ " '-c' indicates that target image must be compressed (qcow format only)\n" ++ " '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n" ++ " new backing file match exactly. The image doesn't need a working\n" ++ " backing file before rebasing in this case (useful for renaming the\n" ++ " backing file). For image creation, allow creating without attempting\n" ++ " to open the backing file.\n" ++ " '-h' with or without a command shows this help and lists the supported formats\n" ++ " '-p' show progress of command (only certain commands)\n" ++ " '-q' use Quiet mode - do not print any output (except errors)\n" ++ " '-S' indicates the consecutive number of bytes (defaults to 4k) that must\n" ++ " contain only zeros for qemu-img to create a sparse image during\n" ++ " conversion. If the number of bytes is 0, the source will not be scanned for\n" ++ " unallocated or zero sectors, and the destination image will always be\n" ++ " fully allocated\n" ++ " '--output' takes the format in which the output must be done (human or json)\n" ++ " '-n' skips the target volume creation (useful if the volume is created\n" ++ " prior to running qemu-img)\n" ++ "\n" ++ "Parameters to bitmap subcommand:\n" ++ " 'bitmap' is the name of the bitmap to manipulate, through one or more\n" ++ " actions from '--add', '--remove', '--clear', '--enable', '--disable',\n" ++ " or '--merge source'\n" ++ " '-g granularity' sets the granularity for '--add' actions\n" ++ " '-b source' and '-F src_fmt' tell '--merge' actions to find the source\n" ++ " bitmaps from an alternative file\n" ++ "\n" ++ "Parameters to check subcommand:\n" ++ " '-r' tries to repair any inconsistencies that are found during the check.\n" ++ " '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all\n" ++ " kinds of errors, with a higher risk of choosing the wrong fix or\n" ++ " hiding corruption that has already occurred.\n" ++ "\n" ++ "Parameters to convert subcommand:\n" ++ " '--bitmaps' copies all top-level persistent bitmaps to destination\n" ++ " '-m' specifies how many coroutines work in parallel during the convert\n" ++ " process (defaults to 8)\n" ++ " '-W' allow to write to the target out of order rather than sequential\n" ++ "\n" ++ "Parameters to snapshot subcommand:\n" ++ " 'snapshot' is the name of the snapshot to create, apply or delete\n" ++ " '-a' applies a snapshot (revert disk to saved state)\n" ++ " '-c' creates a snapshot\n" ++ " '-d' deletes a snapshot\n" ++ " '-l' lists all snapshots in the given image\n" ++ "\n" ++ "Parameters to compare subcommand:\n" ++ " '-f' first image format\n" ++ " '-F' second image format\n" ++ " '-s' run in Strict mode - fail on different image size or sector allocation\n" ++ "\n" ++ "Parameters to dd subcommand:\n" ++ " 'bs=BYTES' read and write up to BYTES bytes at a time " ++ "(default: 512)\n" ++ " 'count=N' copy only N input blocks\n" ++ " 'if=FILE' read from FILE\n" ++ " 'of=FILE' write to FILE\n" ++ " 'skip=N' skip N bs-sized blocks at the start of input\n"; ++ ++ printf("%s\nSupported formats:", help_msg); ++ bdrv_iterate_format(format_print, NULL, false); ++ printf("\n\n" QEMU_HELP_BOTTOM "\n"); ++ exit(EXIT_SUCCESS); ++} ++ ++/* ++ * Is @list safe for accumulate_options()? ++ * It is when multiple of them can be joined together separated by ','. ++ * To make that work, @list must not start with ',' (or else a ++ * separating ',' preceding it gets escaped), and it must not end with ++ * an odd number of ',' (or else a separating ',' following it gets ++ * escaped), or be empty (or else a separating ',' preceding it can ++ * escape a separating ',' following it). ++ * ++ */ ++static bool is_valid_option_list(const char *list) ++{ ++ size_t len = strlen(list); ++ size_t i; ++ ++ if (!list[0] || list[0] == ',') { ++ return false; ++ } ++ ++ for (i = len; i > 0 && list[i - 1] == ','; i--) { ++ } ++ if ((len - i) % 2) { ++ return false; ++ } ++ ++ return true; ++} ++ ++static int accumulate_options(char **options, char *list) ++{ ++ char *new_options; ++ ++ if (!is_valid_option_list(list)) { ++ error_report("Invalid option list: %s", list); ++ return -1; ++ } ++ ++ if (!*options) { ++ *options = g_strdup(list); ++ } else { ++ new_options = g_strdup_printf("%s,%s", *options, list); ++ g_free(*options); ++ *options = new_options; ++ } ++ return 0; ++} ++ ++static QemuOptsList qemu_source_opts = { ++ .name = "source", ++ .implied_opt_name = "file", ++ .head = QTAILQ_HEAD_INITIALIZER(qemu_source_opts.head), ++ .desc = { ++ { } ++ }, ++}; ++ ++static int G_GNUC_PRINTF(2, 3) qprintf(bool quiet, const char *fmt, ...) ++{ ++ int ret = 0; ++ if (!quiet) { ++ va_list args; ++ va_start(args, fmt); ++ ret = vprintf(fmt, args); ++ va_end(args); ++ } ++ return ret; ++} ++ ++ ++static int print_block_option_help(const char *filename, const char *fmt) ++{ ++ BlockDriver *drv, *proto_drv; ++ QemuOptsList *create_opts = NULL; ++ Error *local_err = NULL; ++ ++ /* Find driver and parse its options */ ++ drv = bdrv_find_format(fmt); ++ if (!drv) { ++ error_report("Unknown file format '%s'", fmt); ++ return 1; ++ } ++ ++ if (!drv->create_opts) { ++ error_report("Format driver '%s' does not support image creation", fmt); ++ return 1; ++ } ++ ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ if (filename) { ++ proto_drv = bdrv_find_protocol(filename, true, &local_err); ++ if (!proto_drv) { ++ error_report_err(local_err); ++ qemu_opts_free(create_opts); ++ return 1; ++ } ++ if (!proto_drv->create_opts) { ++ error_report("Protocol driver '%s' does not support image creation", ++ proto_drv->format_name); ++ qemu_opts_free(create_opts); ++ return 1; ++ } ++ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); ++ } ++ ++ if (filename) { ++ printf("Supported options:\n"); ++ } else { ++ printf("Supported %s options:\n", fmt); ++ } ++ qemu_opts_print_help(create_opts, false); ++ qemu_opts_free(create_opts); ++ ++ if (!filename) { ++ printf("\n" ++ "The protocol level may support further options.\n" ++ "Specify the target filename to include those options.\n"); ++ } ++ ++ return 0; ++} ++ ++ ++static BlockBackend *img_open_opts(const char *optstr, ++ QemuOpts *opts, int flags, bool writethrough, ++ bool quiet, bool force_share) ++{ ++ QDict *options; ++ Error *local_err = NULL; ++ BlockBackend *blk; ++ options = qemu_opts_to_qdict(opts, NULL); ++ if (force_share) { ++ if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE) ++ && strcmp(qdict_get_str(options, BDRV_OPT_FORCE_SHARE), "on")) { ++ error_report("--force-share/-U conflicts with image options"); ++ qobject_unref(options); ++ return NULL; ++ } ++ qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on"); ++ } ++ blk = blk_new_open(NULL, NULL, options, flags, &local_err); ++ if (!blk) { ++ error_reportf_err(local_err, "Could not open '%s': ", optstr); ++ return NULL; ++ } ++ blk_set_enable_write_cache(blk, !writethrough); ++ ++ return blk; ++} ++ ++static BlockBackend *img_open_file(const char *filename, ++ QDict *options, ++ const char *fmt, int flags, ++ bool writethrough, bool quiet, ++ bool force_share) ++{ ++ BlockBackend *blk; ++ Error *local_err = NULL; ++ ++ if (!options) { ++ options = qdict_new(); ++ } ++ if (fmt) { ++ qdict_put_str(options, "driver", fmt); ++ } ++ ++ if (force_share) { ++ qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); ++ } ++ blk = blk_new_open(filename, NULL, options, flags, &local_err); ++ if (!blk) { ++ error_reportf_err(local_err, "Could not open '%s': ", filename); ++ return NULL; ++ } ++ blk_set_enable_write_cache(blk, !writethrough); ++ ++ return blk; ++} ++ ++ ++static int img_add_key_secrets(void *opaque, ++ const char *name, const char *value, ++ Error **errp) ++{ ++ QDict *options = opaque; ++ ++ if (g_str_has_suffix(name, "key-secret")) { ++ qdict_put_str(options, name, value); ++ } ++ ++ return 0; ++} ++ ++ ++static BlockBackend *img_open(bool image_opts, ++ const char *filename, ++ const char *fmt, int flags, bool writethrough, ++ bool quiet, bool force_share) ++{ ++ BlockBackend *blk; ++ if (image_opts) { ++ QemuOpts *opts; ++ if (fmt) { ++ error_report("--image-opts and --format are mutually exclusive"); ++ return NULL; ++ } ++ opts = qemu_opts_parse_noisily(qemu_find_opts("source"), ++ filename, true); ++ if (!opts) { ++ return NULL; ++ } ++ blk = img_open_opts(filename, opts, flags, writethrough, quiet, ++ force_share); ++ } else { ++ blk = img_open_file(filename, NULL, fmt, flags, writethrough, quiet, ++ force_share); ++ } ++ ++ if (blk) { ++ blk_set_force_allow_inactivate(blk); ++ } ++ ++ return blk; ++} ++ ++ ++static int add_old_style_options(const char *fmt, QemuOpts *opts, ++ const char *base_filename, ++ const char *base_fmt) ++{ ++ if (base_filename) { ++ if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, ++ NULL)) { ++ error_report("Backing file not supported for file format '%s'", ++ fmt); ++ return -1; ++ } ++ } ++ if (base_fmt) { ++ if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, NULL)) { ++ error_report("Backing file format not supported for file " ++ "format '%s'", fmt); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static int64_t cvtnum_full(const char *name, const char *value, int64_t min, ++ int64_t max) ++{ ++ int err; ++ uint64_t res; ++ ++ err = qemu_strtosz(value, NULL, &res); ++ if (err < 0 && err != -ERANGE) { ++ error_report("Invalid %s specified. You may use " ++ "k, M, G, T, P or E suffixes for", name); ++ error_report("kilobytes, megabytes, gigabytes, terabytes, " ++ "petabytes and exabytes."); ++ return err; ++ } ++ if (err == -ERANGE || res > max || res < min) { ++ error_report("Invalid %s specified. Must be between %" PRId64 ++ " and %" PRId64 ".", name, min, max); ++ return -ERANGE; ++ } ++ return res; ++} ++ ++static int64_t cvtnum(const char *name, const char *value) ++{ ++ return cvtnum_full(name, value, 0, INT64_MAX); ++} ++ ++static int img_create(int argc, char **argv) ++{ ++ int c; ++ uint64_t img_size = -1; ++ const char *fmt = "raw"; ++ const char *base_fmt = NULL; ++ const char *filename; ++ const char *base_filename = NULL; ++ char *options = NULL; ++ Error *local_err = NULL; ++ bool quiet = false; ++ int flags = 0; ++ ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":F:b:f:ho:qu", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'F': ++ base_fmt = optarg; ++ break; ++ case 'b': ++ base_filename = optarg; ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'o': ++ if (accumulate_options(&options, optarg) < 0) { ++ goto fail; ++ } ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 'u': ++ flags |= BDRV_O_NO_BACKING; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ } ++ } ++ ++ /* Get the filename */ ++ filename = (optind < argc) ? argv[optind] : NULL; ++ if (options && has_help_option(options)) { ++ g_free(options); ++ return print_block_option_help(filename, fmt); ++ } ++ ++ if (optind >= argc) { ++ error_exit("Expecting image file name"); ++ } ++ optind++; ++ ++ /* Get image size, if specified */ ++ if (optind < argc) { ++ int64_t sval; ++ ++ sval = cvtnum("image size", argv[optind++]); ++ if (sval < 0) { ++ goto fail; ++ } ++ img_size = (uint64_t)sval; ++ } ++ if (optind != argc) { ++ error_exit("Unexpected argument: %s", argv[optind]); ++ } ++ ++ bdrv_img_create(filename, fmt, base_filename, base_fmt, ++ options, img_size, flags, quiet, &local_err); ++ if (local_err) { ++ error_reportf_err(local_err, "%s: ", filename); ++ goto fail; ++ } ++ ++ g_free(options); ++ return 0; ++ ++fail: ++ g_free(options); ++ return 1; ++} ++ ++static void dump_json_image_check(ImageCheck *check, bool quiet) ++{ ++ GString *str; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_ImageCheck(v, NULL, &check, &error_abort); ++ visit_complete(v, &obj); ++ str = qobject_to_json_pretty(obj, true); ++ assert(str != NULL); ++ qprintf(quiet, "%s\n", str->str); ++ qobject_unref(obj); ++ visit_free(v); ++ g_string_free(str, true); ++} ++ ++static void dump_human_image_check(ImageCheck *check, bool quiet) ++{ ++ if (!(check->corruptions || check->leaks || check->check_errors)) { ++ qprintf(quiet, "No errors were found on the image.\n"); ++ } else { ++ if (check->corruptions) { ++ qprintf(quiet, "\n%" PRId64 " errors were found on the image.\n" ++ "Data may be corrupted, or further writes to the image " ++ "may corrupt it.\n", ++ check->corruptions); ++ } ++ ++ if (check->leaks) { ++ qprintf(quiet, ++ "\n%" PRId64 " leaked clusters were found on the image.\n" ++ "This means waste of disk space, but no harm to data.\n", ++ check->leaks); ++ } ++ ++ if (check->check_errors) { ++ qprintf(quiet, ++ "\n%" PRId64 ++ " internal errors have occurred during the check.\n", ++ check->check_errors); ++ } ++ } ++ ++ if (check->total_clusters != 0 && check->allocated_clusters != 0) { ++ qprintf(quiet, "%" PRId64 "/%" PRId64 " = %0.2f%% allocated, " ++ "%0.2f%% fragmented, %0.2f%% compressed clusters\n", ++ check->allocated_clusters, check->total_clusters, ++ check->allocated_clusters * 100.0 / check->total_clusters, ++ check->fragmented_clusters * 100.0 / check->allocated_clusters, ++ check->compressed_clusters * 100.0 / ++ check->allocated_clusters); ++ } ++ ++ if (check->image_end_offset) { ++ qprintf(quiet, ++ "Image end offset: %" PRId64 "\n", check->image_end_offset); ++ } ++} ++ ++static int collect_image_check(BlockDriverState *bs, ++ ImageCheck *check, ++ const char *filename, ++ const char *fmt, ++ int fix) ++{ ++ int ret; ++ BdrvCheckResult result; ++ ++ ret = bdrv_check(bs, &result, fix); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ check->filename = g_strdup(filename); ++ check->format = g_strdup(bdrv_get_format_name(bs)); ++ check->check_errors = result.check_errors; ++ check->corruptions = result.corruptions; ++ check->has_corruptions = result.corruptions != 0; ++ check->leaks = result.leaks; ++ check->has_leaks = result.leaks != 0; ++ check->corruptions_fixed = result.corruptions_fixed; ++ check->has_corruptions_fixed = result.corruptions_fixed != 0; ++ check->leaks_fixed = result.leaks_fixed; ++ check->has_leaks_fixed = result.leaks_fixed != 0; ++ check->image_end_offset = result.image_end_offset; ++ check->has_image_end_offset = result.image_end_offset != 0; ++ check->total_clusters = result.bfi.total_clusters; ++ check->has_total_clusters = result.bfi.total_clusters != 0; ++ check->allocated_clusters = result.bfi.allocated_clusters; ++ check->has_allocated_clusters = result.bfi.allocated_clusters != 0; ++ check->fragmented_clusters = result.bfi.fragmented_clusters; ++ check->has_fragmented_clusters = result.bfi.fragmented_clusters != 0; ++ check->compressed_clusters = result.bfi.compressed_clusters; ++ check->has_compressed_clusters = result.bfi.compressed_clusters != 0; ++ ++ return 0; ++} ++ ++/* ++ * Checks an image for consistency. Exit codes: ++ * ++ * 0 - Check completed, image is good ++ * 1 - Check not completed because of internal errors ++ * 2 - Check completed, image is corrupted ++ * 3 - Check completed, image has leaked clusters, but is good otherwise ++ * 63 - Checks are not supported by the image format ++ */ ++static int img_check(int argc, char **argv) ++{ ++ int c, ret; ++ OutputFormat output_format = OFORMAT_HUMAN; ++ const char *filename, *fmt, *output, *cache; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ int fix = 0; ++ int flags = BDRV_O_CHECK; ++ bool writethrough; ++ ImageCheck *check; ++ bool quiet = false; ++ bool image_opts = false; ++ bool force_share = false; ++ ++ fmt = NULL; ++ output = NULL; ++ cache = BDRV_DEFAULT_CACHE; ++ ++ for(;;) { ++ int option_index = 0; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"format", required_argument, 0, 'f'}, ++ {"repair", required_argument, 0, 'r'}, ++ {"output", required_argument, 0, OPTION_OUTPUT}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hf:r:T:qU", ++ long_options, &option_index); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'r': ++ flags |= BDRV_O_RDWR; ++ ++ if (!strcmp(optarg, "leaks")) { ++ fix = BDRV_FIX_LEAKS; ++ } else if (!strcmp(optarg, "all")) { ++ fix = BDRV_FIX_LEAKS | BDRV_FIX_ERRORS; ++ } else { ++ error_exit("Unknown option value for -r " ++ "(expecting 'leaks' or 'all'): %s", optarg); ++ } ++ break; ++ case OPTION_OUTPUT: ++ output = optarg; ++ break; ++ case 'T': ++ cache = optarg; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind++]; ++ ++ if (output && !strcmp(output, "json")) { ++ output_format = OFORMAT_JSON; ++ } else if (output && !strcmp(output, "human")) { ++ output_format = OFORMAT_HUMAN; ++ } else if (output) { ++ error_report("--output must be used with human or json as argument."); ++ return 1; ++ } ++ ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid source cache option: %s", cache); ++ return 1; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ force_share); ++ if (!blk) { ++ return 1; ++ } ++ bs = blk_bs(blk); ++ ++ check = g_new0(ImageCheck, 1); ++ ret = collect_image_check(bs, check, filename, fmt, fix); ++ ++ if (ret == -ENOTSUP) { ++ error_report("This image format does not support checks"); ++ ret = 63; ++ goto fail; ++ } ++ ++ if (check->corruptions_fixed || check->leaks_fixed) { ++ int corruptions_fixed, leaks_fixed; ++ bool has_leaks_fixed, has_corruptions_fixed; ++ ++ leaks_fixed = check->leaks_fixed; ++ has_leaks_fixed = check->has_leaks_fixed; ++ corruptions_fixed = check->corruptions_fixed; ++ has_corruptions_fixed = check->has_corruptions_fixed; ++ ++ if (output_format == OFORMAT_HUMAN) { ++ qprintf(quiet, ++ "The following inconsistencies were found and repaired:\n\n" ++ " %" PRId64 " leaked clusters\n" ++ " %" PRId64 " corruptions\n\n" ++ "Double checking the fixed image now...\n", ++ check->leaks_fixed, ++ check->corruptions_fixed); ++ } ++ ++ qapi_free_ImageCheck(check); ++ check = g_new0(ImageCheck, 1); ++ ret = collect_image_check(bs, check, filename, fmt, 0); ++ ++ check->leaks_fixed = leaks_fixed; ++ check->has_leaks_fixed = has_leaks_fixed; ++ check->corruptions_fixed = corruptions_fixed; ++ check->has_corruptions_fixed = has_corruptions_fixed; ++ } ++ ++ if (!ret) { ++ switch (output_format) { ++ case OFORMAT_HUMAN: ++ dump_human_image_check(check, quiet); ++ break; ++ case OFORMAT_JSON: ++ dump_json_image_check(check, quiet); ++ break; ++ } ++ } ++ ++ if (ret || check->check_errors) { ++ if (ret) { ++ error_report("Check failed: %s", strerror(-ret)); ++ } else { ++ error_report("Check failed"); ++ } ++ ret = 1; ++ goto fail; ++ } ++ ++ if (check->corruptions) { ++ ret = 2; ++ } else if (check->leaks) { ++ ret = 3; ++ } else { ++ ret = 0; ++ } ++ ++fail: ++ qapi_free_ImageCheck(check); ++ blk_unref(blk); ++ return ret; ++} ++ ++typedef struct CommonBlockJobCBInfo { ++ BlockDriverState *bs; ++ Error **errp; ++} CommonBlockJobCBInfo; ++ ++static void common_block_job_cb(void *opaque, int ret) ++{ ++ CommonBlockJobCBInfo *cbi = opaque; ++ ++ if (ret < 0) { ++ error_setg_errno(cbi->errp, -ret, "Block job failed"); ++ } ++} ++ ++static void run_block_job(BlockJob *job, Error **errp) ++{ ++ uint64_t progress_current, progress_total; ++ AioContext *aio_context = block_job_get_aio_context(job); ++ int ret = 0; ++ ++ job_lock(); ++ job_ref_locked(&job->job); ++ do { ++ float progress = 0.0f; ++ job_unlock(); ++ aio_poll(aio_context, true); ++ ++ progress_get_snapshot(&job->job.progress, &progress_current, ++ &progress_total); ++ if (progress_total) { ++ progress = (float)progress_current / progress_total * 100.f; ++ } ++ qemu_progress_print(progress, 0); ++ job_lock(); ++ } while (!job_is_ready_locked(&job->job) && ++ !job_is_completed_locked(&job->job)); ++ ++ if (!job_is_completed_locked(&job->job)) { ++ ret = job_complete_sync_locked(&job->job, errp); ++ } else { ++ ret = job->job.ret; ++ } ++ job_unref_locked(&job->job); ++ job_unlock(); ++ ++ /* publish completion progress only when success */ ++ if (!ret) { ++ qemu_progress_print(100.f, 0); ++ } ++} ++ ++static int img_commit(int argc, char **argv) ++{ ++ int c, ret, flags; ++ const char *filename, *fmt, *cache, *base; ++ BlockBackend *blk; ++ BlockDriverState *bs, *base_bs; ++ BlockJob *job; ++ bool progress = false, quiet = false, drop = false; ++ bool writethrough; ++ Error *local_err = NULL; ++ CommonBlockJobCBInfo cbi; ++ bool image_opts = false; ++ int64_t rate_limit = 0; ++ ++ fmt = NULL; ++ cache = BDRV_DEFAULT_CACHE; ++ base = NULL; ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":f:ht:b:dpqr:", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 't': ++ cache = optarg; ++ break; ++ case 'b': ++ base = optarg; ++ /* -b implies -d */ ++ drop = true; ++ break; ++ case 'd': ++ drop = true; ++ break; ++ case 'p': ++ progress = true; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 'r': ++ rate_limit = cvtnum("rate limit", optarg); ++ if (rate_limit < 0) { ++ return 1; ++ } ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ /* Progress is not shown in Quiet mode */ ++ if (quiet) { ++ progress = false; ++ } ++ ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind++]; ++ ++ flags = BDRV_O_RDWR | BDRV_O_UNMAP; ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache option: %s", cache); ++ return 1; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ false); ++ if (!blk) { ++ return 1; ++ } ++ bs = blk_bs(blk); ++ ++ qemu_progress_init(progress, 1.f); ++ qemu_progress_print(0.f, 100); ++ ++ bdrv_graph_rdlock_main_loop(); ++ if (base) { ++ base_bs = bdrv_find_backing_image(bs, base); ++ if (!base_bs) { ++ error_setg(&local_err, ++ "Did not find '%s' in the backing chain of '%s'", ++ base, filename); ++ bdrv_graph_rdunlock_main_loop(); ++ goto done; ++ } ++ } else { ++ /* This is different from QMP, which by default uses the deepest file in ++ * the backing chain (i.e., the very base); however, the traditional ++ * behavior of qemu-img commit is using the immediate backing file. */ ++ base_bs = bdrv_backing_chain_next(bs); ++ if (!base_bs) { ++ error_setg(&local_err, "Image does not have a backing file"); ++ bdrv_graph_rdunlock_main_loop(); ++ goto done; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ ++ cbi = (CommonBlockJobCBInfo){ ++ .errp = &local_err, ++ .bs = bs, ++ }; ++ ++ commit_active_start("commit", bs, base_bs, JOB_DEFAULT, rate_limit, ++ BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, ++ &cbi, false, &local_err); ++ if (local_err) { ++ goto done; ++ } ++ ++ /* When the block job completes, the BlockBackend reference will point to ++ * the old backing file. In order to avoid that the top image is already ++ * deleted, so we can still empty it afterwards, increment the reference ++ * counter here preemptively. */ ++ if (!drop) { ++ bdrv_ref(bs); ++ } ++ ++ job = block_job_get("commit"); ++ assert(job); ++ run_block_job(job, &local_err); ++ if (local_err) { ++ goto unref_backing; ++ } ++ ++ if (!drop) { ++ BlockBackend *old_backing_blk; ++ ++ old_backing_blk = blk_new_with_bs(bs, BLK_PERM_WRITE, BLK_PERM_ALL, ++ &local_err); ++ if (!old_backing_blk) { ++ goto unref_backing; ++ } ++ ret = blk_make_empty(old_backing_blk, &local_err); ++ blk_unref(old_backing_blk); ++ if (ret == -ENOTSUP) { ++ error_free(local_err); ++ local_err = NULL; ++ } else if (ret < 0) { ++ goto unref_backing; ++ } ++ } ++ ++unref_backing: ++ if (!drop) { ++ bdrv_unref(bs); ++ } ++ ++done: ++ qemu_progress_end(); ++ ++ /* ++ * Manually inactivate the image first because this way we can know whether ++ * an error occurred. blk_unref() doesn't tell us about failures. ++ */ ++ ret = bdrv_inactivate_all(); ++ if (ret < 0 && !local_err) { ++ error_setg_errno(&local_err, -ret, "Error while closing the image"); ++ } ++ blk_unref(blk); ++ ++ if (local_err) { ++ error_report_err(local_err); ++ return 1; ++ } ++ ++ qprintf(quiet, "Image committed.\n"); ++ return 0; ++} ++ ++/* ++ * Returns -1 if 'buf' contains only zeroes, otherwise the byte index ++ * of the first sector boundary within buf where the sector contains a ++ * non-zero byte. This function is robust to a buffer that is not ++ * sector-aligned. ++ */ ++static int64_t find_nonzero(const uint8_t *buf, int64_t n) ++{ ++ int64_t i; ++ int64_t end = QEMU_ALIGN_DOWN(n, BDRV_SECTOR_SIZE); ++ ++ for (i = 0; i < end; i += BDRV_SECTOR_SIZE) { ++ if (!buffer_is_zero(buf + i, BDRV_SECTOR_SIZE)) { ++ return i; ++ } ++ } ++ if (i < n && !buffer_is_zero(buf + i, n - end)) { ++ return i; ++ } ++ return -1; ++} ++ ++/* ++ * Returns true iff the first sector pointed to by 'buf' contains at least ++ * a non-NUL byte. ++ * ++ * 'pnum' is set to the number of sectors (including and immediately following ++ * the first one) that are known to be in the same allocated/unallocated state. ++ * The function will try to align the end offset to alignment boundaries so ++ * that the request will at least end aligned and consecutive requests will ++ * also start at an aligned offset. ++ */ ++static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum, ++ int64_t sector_num, int alignment) ++{ ++ bool is_zero; ++ int i, tail; ++ ++ if (n <= 0) { ++ *pnum = 0; ++ return 0; ++ } ++ is_zero = buffer_is_zero(buf, BDRV_SECTOR_SIZE); ++ for(i = 1; i < n; i++) { ++ buf += BDRV_SECTOR_SIZE; ++ if (is_zero != buffer_is_zero(buf, BDRV_SECTOR_SIZE)) { ++ break; ++ } ++ } ++ ++ if (i == n) { ++ /* ++ * The whole buf is the same. ++ * No reason to split it into chunks, so return now. ++ */ ++ *pnum = i; ++ return !is_zero; ++ } ++ ++ tail = (sector_num + i) & (alignment - 1); ++ if (tail) { ++ if (is_zero && i <= tail) { ++ /* ++ * For sure next sector after i is data, and it will rewrite this ++ * tail anyway due to RMW. So, let's just write data now. ++ */ ++ is_zero = false; ++ } ++ if (!is_zero) { ++ /* If possible, align up end offset of allocated areas. */ ++ i += alignment - tail; ++ i = MIN(i, n); ++ } else { ++ /* ++ * For sure next sector after i is data, and it will rewrite this ++ * tail anyway due to RMW. Better is avoid RMW and write zeroes up ++ * to aligned bound. ++ */ ++ i -= tail; ++ } ++ } ++ *pnum = i; ++ return !is_zero; ++} ++ ++/* ++ * Like is_allocated_sectors, but if the buffer starts with a used sector, ++ * up to 'min' consecutive sectors containing zeros are ignored. This avoids ++ * breaking up write requests for only small sparse areas. ++ */ ++static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, ++ int min, int64_t sector_num, int alignment) ++{ ++ int ret; ++ int num_checked, num_used; ++ ++ if (n < min) { ++ min = n; ++ } ++ ++ ret = is_allocated_sectors(buf, n, pnum, sector_num, alignment); ++ if (!ret) { ++ return ret; ++ } ++ ++ num_used = *pnum; ++ buf += BDRV_SECTOR_SIZE * *pnum; ++ n -= *pnum; ++ sector_num += *pnum; ++ num_checked = num_used; ++ ++ while (n > 0) { ++ ret = is_allocated_sectors(buf, n, pnum, sector_num, alignment); ++ ++ buf += BDRV_SECTOR_SIZE * *pnum; ++ n -= *pnum; ++ sector_num += *pnum; ++ num_checked += *pnum; ++ if (ret) { ++ num_used = num_checked; ++ } else if (*pnum >= min) { ++ break; ++ } ++ } ++ ++ *pnum = num_used; ++ return 1; ++} ++ ++/* ++ * Compares two buffers chunk by chunk, where @chsize is the chunk size. ++ * If @chsize is 0, default chunk size of BDRV_SECTOR_SIZE is used. ++ * Returns 0 if the first chunk of each buffer matches, non-zero otherwise. ++ * ++ * @pnum is set to the size of the buffer prefix aligned to @chsize that ++ * has the same matching status as the first chunk. ++ */ ++static int compare_buffers(const uint8_t *buf1, const uint8_t *buf2, ++ int64_t bytes, uint64_t chsize, int64_t *pnum) ++{ ++ bool res; ++ int64_t i; ++ ++ assert(bytes > 0); ++ ++ if (!chsize) { ++ chsize = BDRV_SECTOR_SIZE; ++ } ++ i = MIN(bytes, chsize); ++ ++ res = !!memcmp(buf1, buf2, i); ++ while (i < bytes) { ++ int64_t len = MIN(bytes - i, chsize); ++ ++ if (!!memcmp(buf1 + i, buf2 + i, len) != res) { ++ break; ++ } ++ i += len; ++ } ++ ++ *pnum = i; ++ return res; ++} ++ ++#define IO_BUF_SIZE (2 * MiB) ++ ++/* ++ * Check if passed sectors are empty (not allocated or contain only 0 bytes) ++ * ++ * Intended for use by 'qemu-img compare': Returns 0 in case sectors are ++ * filled with 0, 1 if sectors contain non-zero data (this is a comparison ++ * failure), and 4 on error (the exit status for read errors), after emitting ++ * an error message. ++ * ++ * @param blk: BlockBackend for the image ++ * @param offset: Starting offset to check ++ * @param bytes: Number of bytes to check ++ * @param filename: Name of disk file we are checking (logging purpose) ++ * @param buffer: Allocated buffer for storing read data ++ * @param quiet: Flag for quiet mode ++ */ ++static int check_empty_sectors(BlockBackend *blk, int64_t offset, ++ int64_t bytes, const char *filename, ++ uint8_t *buffer, bool quiet) ++{ ++ int ret = 0; ++ int64_t idx; ++ ++ ret = blk_pread(blk, offset, bytes, buffer, 0); ++ if (ret < 0) { ++ error_report("Error while reading offset %" PRId64 " of %s: %s", ++ offset, filename, strerror(-ret)); ++ return 4; ++ } ++ idx = find_nonzero(buffer, bytes); ++ if (idx >= 0) { ++ qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", ++ offset + idx); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Compares two images. Exit codes: ++ * ++ * 0 - Images are identical or the requested help was printed ++ * 1 - Images differ ++ * >1 - Error occurred ++ */ ++static int img_compare(int argc, char **argv) ++{ ++ const char *fmt1 = NULL, *fmt2 = NULL, *cache, *filename1, *filename2; ++ BlockBackend *blk1, *blk2; ++ BlockDriverState *bs1, *bs2; ++ int64_t total_size1, total_size2; ++ uint8_t *buf1 = NULL, *buf2 = NULL; ++ int64_t pnum1, pnum2; ++ int allocated1, allocated2; ++ int ret = 0; /* return value - 0 Ident, 1 Different, >1 Error */ ++ bool progress = false, quiet = false, strict = false; ++ int flags; ++ bool writethrough; ++ int64_t total_size; ++ int64_t offset = 0; ++ int64_t chunk; ++ int c; ++ uint64_t progress_base; ++ bool image_opts = false; ++ bool force_share = false; ++ ++ cache = BDRV_DEFAULT_CACHE; ++ for (;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hf:F:T:pqsU", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt1 = optarg; ++ break; ++ case 'F': ++ fmt2 = optarg; ++ break; ++ case 'T': ++ cache = optarg; ++ break; ++ case 'p': ++ progress = true; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 's': ++ strict = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ { ++ Error *local_err = NULL; ++ ++ if (!user_creatable_add_from_str(optarg, &local_err)) { ++ if (local_err) { ++ error_report_err(local_err); ++ exit(2); ++ } else { ++ /* Help was printed */ ++ exit(EXIT_SUCCESS); ++ } ++ } ++ break; ++ } ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ /* Progress is not shown in Quiet mode */ ++ if (quiet) { ++ progress = false; ++ } ++ ++ ++ if (optind != argc - 2) { ++ error_exit("Expecting two image file names"); ++ } ++ filename1 = argv[optind++]; ++ filename2 = argv[optind++]; ++ ++ /* Initialize before goto out */ ++ qemu_progress_init(progress, 2.0); ++ ++ flags = 0; ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid source cache option: %s", cache); ++ ret = 2; ++ goto out3; ++ } ++ ++ blk1 = img_open(image_opts, filename1, fmt1, flags, writethrough, quiet, ++ force_share); ++ if (!blk1) { ++ ret = 2; ++ goto out3; ++ } ++ ++ blk2 = img_open(image_opts, filename2, fmt2, flags, writethrough, quiet, ++ force_share); ++ if (!blk2) { ++ ret = 2; ++ goto out2; ++ } ++ bs1 = blk_bs(blk1); ++ bs2 = blk_bs(blk2); ++ ++ buf1 = blk_blockalign(blk1, IO_BUF_SIZE); ++ buf2 = blk_blockalign(blk2, IO_BUF_SIZE); ++ total_size1 = blk_getlength(blk1); ++ if (total_size1 < 0) { ++ error_report("Can't get size of %s: %s", ++ filename1, strerror(-total_size1)); ++ ret = 4; ++ goto out; ++ } ++ total_size2 = blk_getlength(blk2); ++ if (total_size2 < 0) { ++ error_report("Can't get size of %s: %s", ++ filename2, strerror(-total_size2)); ++ ret = 4; ++ goto out; ++ } ++ total_size = MIN(total_size1, total_size2); ++ progress_base = MAX(total_size1, total_size2); ++ ++ qemu_progress_print(0, 100); ++ ++ if (strict && total_size1 != total_size2) { ++ ret = 1; ++ qprintf(quiet, "Strict mode: Image size mismatch!\n"); ++ goto out; ++ } ++ ++ while (offset < total_size) { ++ int status1, status2; ++ ++ status1 = bdrv_block_status_above(bs1, NULL, offset, ++ total_size1 - offset, &pnum1, NULL, ++ NULL); ++ if (status1 < 0) { ++ ret = 3; ++ error_report("Sector allocation test failed for %s", filename1); ++ goto out; ++ } ++ allocated1 = status1 & BDRV_BLOCK_ALLOCATED; ++ ++ status2 = bdrv_block_status_above(bs2, NULL, offset, ++ total_size2 - offset, &pnum2, NULL, ++ NULL); ++ if (status2 < 0) { ++ ret = 3; ++ error_report("Sector allocation test failed for %s", filename2); ++ goto out; ++ } ++ allocated2 = status2 & BDRV_BLOCK_ALLOCATED; ++ ++ assert(pnum1 && pnum2); ++ chunk = MIN(pnum1, pnum2); ++ ++ if (strict) { ++ if (status1 != status2) { ++ ret = 1; ++ qprintf(quiet, "Strict mode: Offset %" PRId64 ++ " block status mismatch!\n", offset); ++ goto out; ++ } ++ } ++ if ((status1 & BDRV_BLOCK_ZERO) && (status2 & BDRV_BLOCK_ZERO)) { ++ /* nothing to do */ ++ } else if (allocated1 == allocated2) { ++ if (allocated1) { ++ int64_t pnum; ++ ++ chunk = MIN(chunk, IO_BUF_SIZE); ++ ret = blk_pread(blk1, offset, chunk, buf1, 0); ++ if (ret < 0) { ++ error_report("Error while reading offset %" PRId64 ++ " of %s: %s", ++ offset, filename1, strerror(-ret)); ++ ret = 4; ++ goto out; ++ } ++ ret = blk_pread(blk2, offset, chunk, buf2, 0); ++ if (ret < 0) { ++ error_report("Error while reading offset %" PRId64 ++ " of %s: %s", ++ offset, filename2, strerror(-ret)); ++ ret = 4; ++ goto out; ++ } ++ ret = compare_buffers(buf1, buf2, chunk, 0, &pnum); ++ if (ret || pnum != chunk) { ++ qprintf(quiet, "Content mismatch at offset %" PRId64 "!\n", ++ offset + (ret ? 0 : pnum)); ++ ret = 1; ++ goto out; ++ } ++ } ++ } else { ++ chunk = MIN(chunk, IO_BUF_SIZE); ++ if (allocated1) { ++ ret = check_empty_sectors(blk1, offset, chunk, ++ filename1, buf1, quiet); ++ } else { ++ ret = check_empty_sectors(blk2, offset, chunk, ++ filename2, buf1, quiet); ++ } ++ if (ret) { ++ goto out; ++ } ++ } ++ offset += chunk; ++ qemu_progress_print(((float) chunk / progress_base) * 100, 100); ++ } ++ ++ if (total_size1 != total_size2) { ++ BlockBackend *blk_over; ++ const char *filename_over; ++ ++ qprintf(quiet, "Warning: Image size mismatch!\n"); ++ if (total_size1 > total_size2) { ++ blk_over = blk1; ++ filename_over = filename1; ++ } else { ++ blk_over = blk2; ++ filename_over = filename2; ++ } ++ ++ while (offset < progress_base) { ++ ret = bdrv_block_status_above(blk_bs(blk_over), NULL, offset, ++ progress_base - offset, &chunk, ++ NULL, NULL); ++ if (ret < 0) { ++ ret = 3; ++ error_report("Sector allocation test failed for %s", ++ filename_over); ++ goto out; ++ ++ } ++ if (ret & BDRV_BLOCK_ALLOCATED && !(ret & BDRV_BLOCK_ZERO)) { ++ chunk = MIN(chunk, IO_BUF_SIZE); ++ ret = check_empty_sectors(blk_over, offset, chunk, ++ filename_over, buf1, quiet); ++ if (ret) { ++ goto out; ++ } ++ } ++ offset += chunk; ++ qemu_progress_print(((float) chunk / progress_base) * 100, 100); ++ } ++ } ++ ++ qprintf(quiet, "Images are identical.\n"); ++ ret = 0; ++ ++out: ++ qemu_vfree(buf1); ++ qemu_vfree(buf2); ++ blk_unref(blk2); ++out2: ++ blk_unref(blk1); ++out3: ++ qemu_progress_end(); ++ return ret; ++} ++ ++/* Convenience wrapper around qmp_block_dirty_bitmap_merge */ ++static void do_dirty_bitmap_merge(const char *dst_node, const char *dst_name, ++ const char *src_node, const char *src_name, ++ Error **errp) ++{ ++ BlockDirtyBitmapOrStr *merge_src; ++ BlockDirtyBitmapOrStrList *list = NULL; ++ ++ merge_src = g_new0(BlockDirtyBitmapOrStr, 1); ++ merge_src->type = QTYPE_QDICT; ++ merge_src->u.external.node = g_strdup(src_node); ++ merge_src->u.external.name = g_strdup(src_name); ++ QAPI_LIST_PREPEND(list, merge_src); ++ qmp_block_dirty_bitmap_merge(dst_node, dst_name, list, errp); ++ qapi_free_BlockDirtyBitmapOrStrList(list); ++} ++ ++enum ImgConvertBlockStatus { ++ BLK_DATA, ++ BLK_ZERO, ++ BLK_BACKING_FILE, ++}; ++ ++#define MAX_COROUTINES 16 ++#define CONVERT_THROTTLE_GROUP "img_convert" ++ ++typedef struct ImgConvertState { ++ BlockBackend **src; ++ int64_t *src_sectors; ++ int *src_alignment; ++ int src_num; ++ int64_t total_sectors; ++ int64_t allocated_sectors; ++ int64_t allocated_done; ++ int64_t sector_num; ++ int64_t wr_offs; ++ enum ImgConvertBlockStatus status; ++ int64_t sector_next_status; ++ BlockBackend *target; ++ bool has_zero_init; ++ bool compressed; ++ bool target_is_new; ++ bool target_has_backing; ++ int64_t target_backing_sectors; /* negative if unknown */ ++ bool wr_in_order; ++ bool copy_range; ++ bool salvage; ++ bool quiet; ++ int min_sparse; ++ int alignment; ++ size_t cluster_sectors; ++ size_t buf_sectors; ++ long num_coroutines; ++ int running_coroutines; ++ Coroutine *co[MAX_COROUTINES]; ++ int64_t wait_sector_num[MAX_COROUTINES]; ++ CoMutex lock; ++ int ret; ++} ImgConvertState; ++ ++static void convert_select_part(ImgConvertState *s, int64_t sector_num, ++ int *src_cur, int64_t *src_cur_offset) ++{ ++ *src_cur = 0; ++ *src_cur_offset = 0; ++ while (sector_num - *src_cur_offset >= s->src_sectors[*src_cur]) { ++ *src_cur_offset += s->src_sectors[*src_cur]; ++ (*src_cur)++; ++ assert(*src_cur < s->src_num); ++ } ++} ++ ++static int coroutine_mixed_fn GRAPH_RDLOCK ++convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) ++{ ++ int64_t src_cur_offset; ++ int ret, n, src_cur; ++ bool post_backing_zero = false; ++ ++ convert_select_part(s, sector_num, &src_cur, &src_cur_offset); ++ ++ assert(s->total_sectors > sector_num); ++ n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); ++ ++ if (s->target_backing_sectors >= 0) { ++ if (sector_num >= s->target_backing_sectors) { ++ post_backing_zero = true; ++ } else if (sector_num + n > s->target_backing_sectors) { ++ /* Split requests around target_backing_sectors (because ++ * starting from there, zeros are handled differently) */ ++ n = s->target_backing_sectors - sector_num; ++ } ++ } ++ ++ if (s->sector_next_status <= sector_num) { ++ uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE; ++ int64_t count; ++ int tail; ++ BlockDriverState *src_bs = blk_bs(s->src[src_cur]); ++ BlockDriverState *base; ++ ++ if (s->target_has_backing) { ++ base = bdrv_cow_bs(bdrv_skip_filters(src_bs)); ++ } else { ++ base = NULL; ++ } ++ ++ do { ++ count = n * BDRV_SECTOR_SIZE; ++ ++ ret = bdrv_block_status_above(src_bs, base, offset, count, &count, ++ NULL, NULL); ++ ++ if (ret < 0) { ++ if (s->salvage) { ++ if (n == 1) { ++ if (!s->quiet) { ++ warn_report("error while reading block status at " ++ "offset %" PRIu64 ": %s", offset, ++ strerror(-ret)); ++ } ++ /* Just try to read the data, then */ ++ ret = BDRV_BLOCK_DATA; ++ count = BDRV_SECTOR_SIZE; ++ } else { ++ /* Retry on a shorter range */ ++ n = DIV_ROUND_UP(n, 4); ++ } ++ } else { ++ error_report("error while reading block status at offset " ++ "%" PRIu64 ": %s", offset, strerror(-ret)); ++ return ret; ++ } ++ } ++ } while (ret < 0); ++ ++ n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); ++ ++ /* ++ * Avoid that s->sector_next_status becomes unaligned to the source ++ * request alignment and/or cluster size to avoid unnecessary read ++ * cycles. ++ */ ++ tail = (sector_num - src_cur_offset + n) % s->src_alignment[src_cur]; ++ if (n > tail) { ++ n -= tail; ++ } ++ ++ if (ret & BDRV_BLOCK_ZERO) { ++ s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO; ++ } else if (ret & BDRV_BLOCK_DATA) { ++ s->status = BLK_DATA; ++ } else { ++ s->status = s->target_has_backing ? BLK_BACKING_FILE : BLK_DATA; ++ } ++ ++ s->sector_next_status = sector_num + n; ++ } ++ ++ n = MIN(n, s->sector_next_status - sector_num); ++ if (s->status == BLK_DATA) { ++ n = MIN(n, s->buf_sectors); ++ } ++ ++ /* We need to write complete clusters for compressed images, so if an ++ * unallocated area is shorter than that, we must consider the whole ++ * cluster allocated. */ ++ if (s->compressed) { ++ if (n < s->cluster_sectors) { ++ n = MIN(s->cluster_sectors, s->total_sectors - sector_num); ++ s->status = BLK_DATA; ++ } else { ++ n = QEMU_ALIGN_DOWN(n, s->cluster_sectors); ++ } ++ } ++ ++ return n; ++} ++ ++static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, ++ int nb_sectors, uint8_t *buf) ++{ ++ uint64_t single_read_until = 0; ++ int n, ret; ++ ++ assert(nb_sectors <= s->buf_sectors); ++ while (nb_sectors > 0) { ++ BlockBackend *blk; ++ int src_cur; ++ int64_t bs_sectors, src_cur_offset; ++ uint64_t offset; ++ ++ /* In the case of compression with multiple source files, we can get a ++ * nb_sectors that spreads into the next part. So we must be able to ++ * read across multiple BDSes for one convert_read() call. */ ++ convert_select_part(s, sector_num, &src_cur, &src_cur_offset); ++ blk = s->src[src_cur]; ++ bs_sectors = s->src_sectors[src_cur]; ++ ++ offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; ++ ++ n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); ++ if (single_read_until > offset) { ++ n = 1; ++ } ++ ++ ret = blk_co_pread(blk, offset, n << BDRV_SECTOR_BITS, buf, 0); ++ if (ret < 0) { ++ if (s->salvage) { ++ if (n > 1) { ++ single_read_until = offset + (n << BDRV_SECTOR_BITS); ++ continue; ++ } else { ++ if (!s->quiet) { ++ warn_report("error while reading offset %" PRIu64 ++ ": %s", offset, strerror(-ret)); ++ } ++ memset(buf, 0, BDRV_SECTOR_SIZE); ++ } ++ } else { ++ return ret; ++ } ++ } ++ ++ sector_num += n; ++ nb_sectors -= n; ++ buf += n * BDRV_SECTOR_SIZE; ++ } ++ ++ return 0; ++} ++ ++ ++static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, ++ int nb_sectors, uint8_t *buf, ++ enum ImgConvertBlockStatus status) ++{ ++ int ret; ++ ++ while (nb_sectors > 0) { ++ int n = nb_sectors; ++ BdrvRequestFlags flags = s->compressed ? BDRV_REQ_WRITE_COMPRESSED : 0; ++ ++ switch (status) { ++ case BLK_BACKING_FILE: ++ /* If we have a backing file, leave clusters unallocated that are ++ * unallocated in the source image, so that the backing file is ++ * visible at the respective offset. */ ++ assert(s->target_has_backing); ++ break; ++ ++ case BLK_DATA: ++ /* If we're told to keep the target fully allocated (-S 0) or there ++ * is real non-zero data, we must write it. Otherwise we can treat ++ * it as zero sectors. ++ * Compressed clusters need to be written as a whole, so in that ++ * case we can only save the write if the buffer is completely ++ * zeroed. */ ++ if (!s->min_sparse || ++ (!s->compressed && ++ is_allocated_sectors_min(buf, n, &n, s->min_sparse, ++ sector_num, s->alignment)) || ++ (s->compressed && ++ !buffer_is_zero(buf, n * BDRV_SECTOR_SIZE))) ++ { ++ ret = blk_co_pwrite(s->target, sector_num << BDRV_SECTOR_BITS, ++ n << BDRV_SECTOR_BITS, buf, flags); ++ if (ret < 0) { ++ return ret; ++ } ++ break; ++ } ++ /* fall-through */ ++ ++ case BLK_ZERO: ++ if (s->has_zero_init) { ++ assert(!s->target_has_backing); ++ break; ++ } ++ ret = blk_co_pwrite_zeroes(s->target, ++ sector_num << BDRV_SECTOR_BITS, ++ n << BDRV_SECTOR_BITS, ++ BDRV_REQ_MAY_UNMAP); ++ if (ret < 0) { ++ return ret; ++ } ++ break; ++ } ++ ++ sector_num += n; ++ nb_sectors -= n; ++ buf += n * BDRV_SECTOR_SIZE; ++ } ++ ++ return 0; ++} ++ ++static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector_num, ++ int nb_sectors) ++{ ++ int n, ret; ++ ++ while (nb_sectors > 0) { ++ BlockBackend *blk; ++ int src_cur; ++ int64_t bs_sectors, src_cur_offset; ++ int64_t offset; ++ ++ convert_select_part(s, sector_num, &src_cur, &src_cur_offset); ++ offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; ++ blk = s->src[src_cur]; ++ bs_sectors = s->src_sectors[src_cur]; ++ ++ n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); ++ ++ ret = blk_co_copy_range(blk, offset, s->target, ++ sector_num << BDRV_SECTOR_BITS, ++ n << BDRV_SECTOR_BITS, 0, 0); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ sector_num += n; ++ nb_sectors -= n; ++ } ++ return 0; ++} ++ ++static void coroutine_fn convert_co_do_copy(void *opaque) ++{ ++ ImgConvertState *s = opaque; ++ uint8_t *buf = NULL; ++ int ret, i; ++ int index = -1; ++ ++ for (i = 0; i < s->num_coroutines; i++) { ++ if (s->co[i] == qemu_coroutine_self()) { ++ index = i; ++ break; ++ } ++ } ++ assert(index >= 0); ++ ++ s->running_coroutines++; ++ buf = blk_blockalign(s->target, s->buf_sectors * BDRV_SECTOR_SIZE); ++ ++ while (1) { ++ int n; ++ int64_t sector_num; ++ enum ImgConvertBlockStatus status; ++ bool copy_range; ++ ++ qemu_co_mutex_lock(&s->lock); ++ if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) { ++ qemu_co_mutex_unlock(&s->lock); ++ break; ++ } ++ WITH_GRAPH_RDLOCK_GUARD() { ++ n = convert_iteration_sectors(s, s->sector_num); ++ } ++ if (n < 0) { ++ qemu_co_mutex_unlock(&s->lock); ++ s->ret = n; ++ break; ++ } ++ /* save current sector and allocation status to local variables */ ++ sector_num = s->sector_num; ++ status = s->status; ++ if (!s->min_sparse && s->status == BLK_ZERO) { ++ n = MIN(n, s->buf_sectors); ++ } ++ /* increment global sector counter so that other coroutines can ++ * already continue reading beyond this request */ ++ s->sector_num += n; ++ qemu_co_mutex_unlock(&s->lock); ++ ++ if (status == BLK_DATA || (!s->min_sparse && status == BLK_ZERO)) { ++ s->allocated_done += n; ++ qemu_progress_print(100.0 * s->allocated_done / ++ s->allocated_sectors, 0); ++ } ++ ++retry: ++ copy_range = s->copy_range && s->status == BLK_DATA; ++ if (status == BLK_DATA && !copy_range) { ++ ret = convert_co_read(s, sector_num, n, buf); ++ if (ret < 0) { ++ error_report("error while reading at byte %lld: %s", ++ sector_num * BDRV_SECTOR_SIZE, strerror(-ret)); ++ s->ret = ret; ++ } ++ } else if (!s->min_sparse && status == BLK_ZERO) { ++ status = BLK_DATA; ++ memset(buf, 0x00, n * BDRV_SECTOR_SIZE); ++ } ++ ++ if (s->wr_in_order) { ++ /* keep writes in order */ ++ while (s->wr_offs != sector_num && s->ret == -EINPROGRESS) { ++ s->wait_sector_num[index] = sector_num; ++ qemu_coroutine_yield(); ++ } ++ s->wait_sector_num[index] = -1; ++ } ++ ++ if (s->ret == -EINPROGRESS) { ++ if (copy_range) { ++ WITH_GRAPH_RDLOCK_GUARD() { ++ ret = convert_co_copy_range(s, sector_num, n); ++ } ++ if (ret) { ++ s->copy_range = false; ++ goto retry; ++ } ++ } else { ++ ret = convert_co_write(s, sector_num, n, buf, status); ++ } ++ if (ret < 0) { ++ error_report("error while writing at byte %lld: %s", ++ sector_num * BDRV_SECTOR_SIZE, strerror(-ret)); ++ s->ret = ret; ++ } ++ } ++ ++ if (s->wr_in_order) { ++ /* reenter the coroutine that might have waited ++ * for this write to complete */ ++ s->wr_offs = sector_num + n; ++ for (i = 0; i < s->num_coroutines; i++) { ++ if (s->co[i] && s->wait_sector_num[i] == s->wr_offs) { ++ /* ++ * A -> B -> A cannot occur because A has ++ * s->wait_sector_num[i] == -1 during A -> B. Therefore ++ * B will never enter A during this time window. ++ */ ++ qemu_coroutine_enter(s->co[i]); ++ break; ++ } ++ } ++ } ++ } ++ ++ qemu_vfree(buf); ++ s->co[index] = NULL; ++ s->running_coroutines--; ++ if (!s->running_coroutines && s->ret == -EINPROGRESS) { ++ /* the convert job finished successfully */ ++ s->ret = 0; ++ } ++} ++ ++static int convert_do_copy(ImgConvertState *s) ++{ ++ int ret, i, n; ++ int64_t sector_num = 0; ++ ++ /* Check whether we have zero initialisation or can get it efficiently */ ++ if (!s->has_zero_init && s->target_is_new && s->min_sparse && ++ !s->target_has_backing) { ++ bdrv_graph_rdlock_main_loop(); ++ s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target)); ++ bdrv_graph_rdunlock_main_loop(); ++ } ++ ++ /* Allocate buffer for copied data. For compressed images, only one cluster ++ * can be copied at a time. */ ++ if (s->compressed) { ++ if (s->cluster_sectors <= 0 || s->cluster_sectors > s->buf_sectors) { ++ error_report("invalid cluster size"); ++ return -EINVAL; ++ } ++ s->buf_sectors = s->cluster_sectors; ++ } ++ ++ while (sector_num < s->total_sectors) { ++ bdrv_graph_rdlock_main_loop(); ++ n = convert_iteration_sectors(s, sector_num); ++ bdrv_graph_rdunlock_main_loop(); ++ if (n < 0) { ++ return n; ++ } ++ if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO)) ++ { ++ s->allocated_sectors += n; ++ } ++ sector_num += n; ++ } ++ ++ /* Do the copy */ ++ s->sector_next_status = 0; ++ s->ret = -EINPROGRESS; ++ ++ qemu_co_mutex_init(&s->lock); ++ for (i = 0; i < s->num_coroutines; i++) { ++ s->co[i] = qemu_coroutine_create(convert_co_do_copy, s); ++ s->wait_sector_num[i] = -1; ++ qemu_coroutine_enter(s->co[i]); ++ } ++ ++ while (s->running_coroutines) { ++ main_loop_wait(false); ++ } ++ ++ if (s->compressed && !s->ret) { ++ /* signal EOF to align */ ++ ret = blk_pwrite_compressed(s->target, 0, 0, NULL); ++ if (ret < 0) { ++ return ret; ++ } ++ } ++ ++ return s->ret; ++} ++ ++/* Check that bitmaps can be copied, or output an error */ ++static int convert_check_bitmaps(BlockDriverState *src, bool skip_broken) ++{ ++ BdrvDirtyBitmap *bm; ++ ++ if (!bdrv_supports_persistent_dirty_bitmap(src)) { ++ error_report("Source lacks bitmap support"); ++ return -1; ++ } ++ FOR_EACH_DIRTY_BITMAP(src, bm) { ++ if (!bdrv_dirty_bitmap_get_persistence(bm)) { ++ continue; ++ } ++ if (!skip_broken && bdrv_dirty_bitmap_inconsistent(bm)) { ++ error_report("Cannot copy inconsistent bitmap '%s'", ++ bdrv_dirty_bitmap_name(bm)); ++ error_printf("Try --skip-broken-bitmaps, or " ++ "use 'qemu-img bitmap --remove' to delete it\n"); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst, ++ bool skip_broken) ++{ ++ BdrvDirtyBitmap *bm; ++ Error *err = NULL; ++ ++ FOR_EACH_DIRTY_BITMAP(src, bm) { ++ const char *name; ++ ++ if (!bdrv_dirty_bitmap_get_persistence(bm)) { ++ continue; ++ } ++ name = bdrv_dirty_bitmap_name(bm); ++ if (skip_broken && bdrv_dirty_bitmap_inconsistent(bm)) { ++ warn_report("Skipping inconsistent bitmap '%s'", name); ++ continue; ++ } ++ qmp_block_dirty_bitmap_add(dst->node_name, name, ++ true, bdrv_dirty_bitmap_granularity(bm), ++ true, true, ++ true, !bdrv_dirty_bitmap_enabled(bm), ++ &err); ++ if (err) { ++ error_reportf_err(err, "Failed to create bitmap %s: ", name); ++ return -1; ++ } ++ ++ do_dirty_bitmap_merge(dst->node_name, name, src->node_name, name, ++ &err); ++ if (err) { ++ error_reportf_err(err, "Failed to populate bitmap %s: ", name); ++ qmp_block_dirty_bitmap_remove(dst->node_name, name, NULL); ++ return -1; ++ } ++ } ++ ++ return 0; ++} ++ ++#define MAX_BUF_SECTORS 32768 ++ ++static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) ++{ ++ ThrottleConfig cfg; ++ ++ throttle_config_init(&cfg); ++ cfg.buckets[THROTTLE_BPS_WRITE].avg = rate_limit; ++ ++ blk_io_limits_enable(blk, CONVERT_THROTTLE_GROUP); ++ blk_set_io_limits(blk, &cfg); ++} ++ ++static int img_convert(int argc, char **argv) ++{ ++ int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE; ++ const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe", ++ *src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL, ++ *out_filename, *out_baseimg_param, *snapshot_name = NULL, ++ *backing_fmt = NULL; ++ BlockDriver *drv = NULL, *proto_drv = NULL; ++ BlockDriverInfo bdi; ++ BlockDriverState *out_bs; ++ QemuOpts *opts = NULL, *sn_opts = NULL; ++ QemuOptsList *create_opts = NULL; ++ QDict *open_opts = NULL; ++ char *options = NULL; ++ Error *local_err = NULL; ++ bool writethrough, src_writethrough, image_opts = false, ++ skip_create = false, progress = false, tgt_image_opts = false; ++ int64_t ret = -EINVAL; ++ bool force_share = false; ++ bool explict_min_sparse = false; ++ bool bitmaps = false; ++ bool skip_broken = false; ++ int64_t rate_limit = 0; ++ ++ ImgConvertState s = (ImgConvertState) { ++ /* Need at least 4k of zeros for sparse detection */ ++ .min_sparse = 8, ++ .copy_range = false, ++ .buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE, ++ .wr_in_order = true, ++ .num_coroutines = 8, ++ }; ++ ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, ++ {"salvage", no_argument, 0, OPTION_SALVAGE}, ++ {"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO}, ++ {"bitmaps", no_argument, 0, OPTION_BITMAPS}, ++ {"skip-broken-bitmaps", no_argument, 0, OPTION_SKIP_BROKEN}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hf:O:B:CcF:o:l:S:pt:T:qnm:WUr:", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'O': ++ out_fmt = optarg; ++ break; ++ case 'B': ++ out_baseimg = optarg; ++ break; ++ case 'C': ++ s.copy_range = true; ++ break; ++ case 'c': ++ s.compressed = true; ++ break; ++ case 'F': ++ backing_fmt = optarg; ++ break; ++ case 'o': ++ if (accumulate_options(&options, optarg) < 0) { ++ goto fail_getopt; ++ } ++ break; ++ case 'l': ++ if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { ++ sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, ++ optarg, false); ++ if (!sn_opts) { ++ error_report("Failed in parsing snapshot param '%s'", ++ optarg); ++ goto fail_getopt; ++ } ++ } else { ++ snapshot_name = optarg; ++ } ++ break; ++ case 'S': ++ { ++ int64_t sval; ++ ++ sval = cvtnum("buffer size for sparse output", optarg); ++ if (sval < 0) { ++ goto fail_getopt; ++ } else if (!QEMU_IS_ALIGNED(sval, BDRV_SECTOR_SIZE) || ++ sval / BDRV_SECTOR_SIZE > MAX_BUF_SECTORS) { ++ error_report("Invalid buffer size for sparse output specified. " ++ "Valid sizes are multiples of %llu up to %llu. Select " ++ "0 to disable sparse detection (fully allocates output).", ++ BDRV_SECTOR_SIZE, MAX_BUF_SECTORS * BDRV_SECTOR_SIZE); ++ goto fail_getopt; ++ } ++ ++ s.min_sparse = sval / BDRV_SECTOR_SIZE; ++ explict_min_sparse = true; ++ break; ++ } ++ case 'p': ++ progress = true; ++ break; ++ case 't': ++ cache = optarg; ++ break; ++ case 'T': ++ src_cache = optarg; ++ break; ++ case 'q': ++ s.quiet = true; ++ break; ++ case 'n': ++ skip_create = true; ++ break; ++ case 'm': ++ if (qemu_strtol(optarg, NULL, 0, &s.num_coroutines) || ++ s.num_coroutines < 1 || s.num_coroutines > MAX_COROUTINES) { ++ error_report("Invalid number of coroutines. Allowed number of" ++ " coroutines is between 1 and %d", MAX_COROUTINES); ++ goto fail_getopt; ++ } ++ break; ++ case 'W': ++ s.wr_in_order = false; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case 'r': ++ rate_limit = cvtnum("rate limit", optarg); ++ if (rate_limit < 0) { ++ goto fail_getopt; ++ } ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case OPTION_SALVAGE: ++ s.salvage = true; ++ break; ++ case OPTION_TARGET_IMAGE_OPTS: ++ tgt_image_opts = true; ++ break; ++ case OPTION_TARGET_IS_ZERO: ++ /* ++ * The user asserting that the target is blank has the ++ * same effect as the target driver supporting zero ++ * initialisation. ++ */ ++ s.has_zero_init = true; ++ break; ++ case OPTION_BITMAPS: ++ bitmaps = true; ++ break; ++ case OPTION_SKIP_BROKEN: ++ skip_broken = true; ++ break; ++ } ++ } ++ ++ if (!out_fmt && !tgt_image_opts) { ++ out_fmt = "raw"; ++ } ++ ++ if (skip_broken && !bitmaps) { ++ error_report("Use of --skip-broken-bitmaps requires --bitmaps"); ++ goto fail_getopt; ++ } ++ ++ if (s.compressed && s.copy_range) { ++ error_report("Cannot enable copy offloading when -c is used"); ++ goto fail_getopt; ++ } ++ ++ if (explict_min_sparse && s.copy_range) { ++ error_report("Cannot enable copy offloading when -S is used"); ++ goto fail_getopt; ++ } ++ ++ if (s.copy_range && s.salvage) { ++ error_report("Cannot use copy offloading in salvaging mode"); ++ goto fail_getopt; ++ } ++ ++ if (tgt_image_opts && !skip_create) { ++ error_report("--target-image-opts requires use of -n flag"); ++ goto fail_getopt; ++ } ++ ++ if (skip_create && options) { ++ error_report("-o has no effect when skipping image creation"); ++ goto fail_getopt; ++ } ++ ++ if (s.has_zero_init && !skip_create) { ++ error_report("--target-is-zero requires use of -n flag"); ++ goto fail_getopt; ++ } ++ ++ s.src_num = argc - optind - 1; ++ out_filename = s.src_num >= 1 ? argv[argc - 1] : NULL; ++ ++ if (options && has_help_option(options)) { ++ if (out_fmt) { ++ ret = print_block_option_help(out_filename, out_fmt); ++ goto fail_getopt; ++ } else { ++ error_report("Option help requires a format be specified"); ++ goto fail_getopt; ++ } ++ } ++ ++ if (s.src_num < 1) { ++ error_report("Must specify image file name"); ++ goto fail_getopt; ++ } ++ ++ /* ret is still -EINVAL until here */ ++ ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); ++ if (ret < 0) { ++ error_report("Invalid source cache option: %s", src_cache); ++ goto fail_getopt; ++ } ++ ++ /* Initialize before goto out */ ++ if (s.quiet) { ++ progress = false; ++ } ++ qemu_progress_init(progress, 1.0); ++ qemu_progress_print(0, 100); ++ ++ s.src = g_new0(BlockBackend *, s.src_num); ++ s.src_sectors = g_new(int64_t, s.src_num); ++ s.src_alignment = g_new(int, s.src_num); ++ ++ for (bs_i = 0; bs_i < s.src_num; bs_i++) { ++ BlockDriverState *src_bs; ++ s.src[bs_i] = img_open(image_opts, argv[optind + bs_i], ++ fmt, src_flags, src_writethrough, s.quiet, ++ force_share); ++ if (!s.src[bs_i]) { ++ ret = -1; ++ goto out; ++ } ++ s.src_sectors[bs_i] = blk_nb_sectors(s.src[bs_i]); ++ if (s.src_sectors[bs_i] < 0) { ++ error_report("Could not get size of %s: %s", ++ argv[optind + bs_i], strerror(-s.src_sectors[bs_i])); ++ ret = -1; ++ goto out; ++ } ++ src_bs = blk_bs(s.src[bs_i]); ++ s.src_alignment[bs_i] = DIV_ROUND_UP(src_bs->bl.request_alignment, ++ BDRV_SECTOR_SIZE); ++ if (!bdrv_get_info(src_bs, &bdi)) { ++ s.src_alignment[bs_i] = MAX(s.src_alignment[bs_i], ++ bdi.cluster_size / BDRV_SECTOR_SIZE); ++ } ++ s.total_sectors += s.src_sectors[bs_i]; ++ } ++ ++ if (sn_opts) { ++ bdrv_snapshot_load_tmp(blk_bs(s.src[0]), ++ qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), ++ qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME), ++ &local_err); ++ } else if (snapshot_name != NULL) { ++ if (s.src_num > 1) { ++ error_report("No support for concatenating multiple snapshot"); ++ ret = -1; ++ goto out; ++ } ++ ++ bdrv_snapshot_load_tmp_by_id_or_name(blk_bs(s.src[0]), snapshot_name, ++ &local_err); ++ } ++ if (local_err) { ++ error_reportf_err(local_err, "Failed to load snapshot: "); ++ ret = -1; ++ goto out; ++ } ++ ++ if (!skip_create) { ++ /* Find driver and parse its options */ ++ drv = bdrv_find_format(out_fmt); ++ if (!drv) { ++ error_report("Unknown file format '%s'", out_fmt); ++ ret = -1; ++ goto out; ++ } ++ ++ proto_drv = bdrv_find_protocol(out_filename, true, &local_err); ++ if (!proto_drv) { ++ error_report_err(local_err); ++ ret = -1; ++ goto out; ++ } ++ ++ if (!drv->create_opts) { ++ error_report("Format driver '%s' does not support image creation", ++ drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ ++ if (!proto_drv->create_opts) { ++ error_report("Protocol driver '%s' does not support image creation", ++ proto_drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); ++ ++ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); ++ if (options) { ++ if (!qemu_opts_do_parse(opts, options, NULL, &local_err)) { ++ error_report_err(local_err); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, ++ s.total_sectors * BDRV_SECTOR_SIZE, &error_abort); ++ ret = add_old_style_options(out_fmt, opts, out_baseimg, backing_fmt); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ /* Get backing file name if -o backing_file was used */ ++ out_baseimg_param = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); ++ if (out_baseimg_param) { ++ out_baseimg = out_baseimg_param; ++ } ++ s.target_has_backing = (bool) out_baseimg; ++ ++ if (s.has_zero_init && s.target_has_backing) { ++ error_report("Cannot use --target-is-zero when the destination " ++ "image has a backing file"); ++ goto out; ++ } ++ ++ if (s.src_num > 1 && out_baseimg) { ++ error_report("Having a backing file for the target makes no sense when " ++ "concatenating multiple input images"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (out_baseimg_param) { ++ if (!qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT)) { ++ error_report("Use of backing file requires explicit " ++ "backing format"); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ /* Check if compression is supported */ ++ if (s.compressed) { ++ bool encryption = ++ qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT, false); ++ const char *encryptfmt = ++ qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT); ++ const char *preallocation = ++ qemu_opt_get(opts, BLOCK_OPT_PREALLOC); ++ ++ if (drv && !block_driver_can_compress(drv)) { ++ error_report("Compression not supported for this file format"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (encryption || encryptfmt) { ++ error_report("Compression and encryption not supported at " ++ "the same time"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (preallocation ++ && strcmp(preallocation, "off")) ++ { ++ error_report("Compression and preallocation not supported at " ++ "the same time"); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ /* Determine if bitmaps need copying */ ++ if (bitmaps) { ++ if (s.src_num > 1) { ++ error_report("Copying bitmaps only possible with single source"); ++ ret = -1; ++ goto out; ++ } ++ ret = convert_check_bitmaps(blk_bs(s.src[0]), skip_broken); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ ++ /* ++ * The later open call will need any decryption secrets, and ++ * bdrv_create() will purge "opts", so extract them now before ++ * they are lost. ++ */ ++ if (!skip_create) { ++ open_opts = qdict_new(); ++ qemu_opt_foreach(opts, img_add_key_secrets, open_opts, &error_abort); ++ ++ /* Create the new image */ ++ ret = bdrv_create(drv, out_filename, opts, &local_err); ++ if (ret < 0) { ++ error_reportf_err(local_err, "%s: error while converting %s: ", ++ out_filename, out_fmt); ++ goto out; ++ } ++ } ++ ++ s.target_is_new = !skip_create; ++ ++ flags = s.min_sparse ? (BDRV_O_RDWR | BDRV_O_UNMAP) : BDRV_O_RDWR; ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache option: %s", cache); ++ goto out; ++ } ++ ++ if (flags & BDRV_O_NOCACHE) { ++ /* ++ * If we open the target with O_DIRECT, it may be necessary to ++ * extend its size to align to the physical sector size. ++ */ ++ flags |= BDRV_O_RESIZE; ++ } ++ ++ if (skip_create) { ++ s.target = img_open(tgt_image_opts, out_filename, out_fmt, ++ flags, writethrough, s.quiet, false); ++ } else { ++ /* TODO ultimately we should allow --target-image-opts ++ * to be used even when -n is not given. ++ * That has to wait for bdrv_create to be improved ++ * to allow filenames in option syntax ++ */ ++ s.target = img_open_file(out_filename, open_opts, out_fmt, ++ flags, writethrough, s.quiet, false); ++ open_opts = NULL; /* blk_new_open will have freed it */ ++ } ++ if (!s.target) { ++ ret = -1; ++ goto out; ++ } ++ out_bs = blk_bs(s.target); ++ ++ if (bitmaps && !bdrv_supports_persistent_dirty_bitmap(out_bs)) { ++ error_report("Format driver '%s' does not support bitmaps", ++ out_bs->drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ ++ if (s.compressed && !block_driver_can_compress(out_bs->drv)) { ++ error_report("Compression not supported for this file format"); ++ ret = -1; ++ goto out; ++ } ++ ++ /* increase bufsectors from the default 4096 (2M) if opt_transfer ++ * or discard_alignment of the out_bs is greater. Limit to ++ * MAX_BUF_SECTORS as maximum which is currently 32768 (16MB). */ ++ s.buf_sectors = MIN(MAX_BUF_SECTORS, ++ MAX(s.buf_sectors, ++ MAX(out_bs->bl.opt_transfer >> BDRV_SECTOR_BITS, ++ out_bs->bl.pdiscard_alignment >> ++ BDRV_SECTOR_BITS))); ++ ++ /* try to align the write requests to the destination to avoid unnecessary ++ * RMW cycles. */ ++ s.alignment = MAX(pow2floor(s.min_sparse), ++ DIV_ROUND_UP(out_bs->bl.request_alignment, ++ BDRV_SECTOR_SIZE)); ++ assert(is_power_of_2(s.alignment)); ++ ++ if (skip_create) { ++ int64_t output_sectors = blk_nb_sectors(s.target); ++ if (output_sectors < 0) { ++ error_report("unable to get output image length: %s", ++ strerror(-output_sectors)); ++ ret = -1; ++ goto out; ++ } else if (output_sectors < s.total_sectors) { ++ error_report("output file is smaller than input file"); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ if (s.target_has_backing && s.target_is_new) { ++ /* Errors are treated as "backing length unknown" (which means ++ * s.target_backing_sectors has to be negative, which it will ++ * be automatically). The backing file length is used only ++ * for optimizations, so such a case is not fatal. */ ++ bdrv_graph_rdlock_main_loop(); ++ s.target_backing_sectors = ++ bdrv_nb_sectors(bdrv_backing_chain_next(out_bs)); ++ bdrv_graph_rdunlock_main_loop(); ++ } else { ++ s.target_backing_sectors = -1; ++ } ++ ++ ret = bdrv_get_info(out_bs, &bdi); ++ if (ret < 0) { ++ if (s.compressed) { ++ error_report("could not get block driver info"); ++ goto out; ++ } ++ } else { ++ s.compressed = s.compressed || bdi.needs_compressed_writes; ++ s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; ++ } ++ ++ if (rate_limit) { ++ set_rate_limit(s.target, rate_limit); ++ } ++ ++ ret = convert_do_copy(&s); ++ ++ /* Now copy the bitmaps */ ++ if (bitmaps && ret == 0) { ++ ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs, skip_broken); ++ } ++ ++out: ++ if (!ret) { ++ qemu_progress_print(100, 0); ++ } ++ qemu_progress_end(); ++ qemu_opts_del(opts); ++ qemu_opts_free(create_opts); ++ qobject_unref(open_opts); ++ blk_unref(s.target); ++ if (s.src) { ++ for (bs_i = 0; bs_i < s.src_num; bs_i++) { ++ blk_unref(s.src[bs_i]); ++ } ++ g_free(s.src); ++ } ++ g_free(s.src_sectors); ++ g_free(s.src_alignment); ++fail_getopt: ++ qemu_opts_del(sn_opts); ++ g_free(options); ++ ++ return !!ret; ++} ++ ++ ++static void dump_snapshots(BlockDriverState *bs) ++{ ++ QEMUSnapshotInfo *sn_tab, *sn; ++ int nb_sns, i; ++ ++ nb_sns = bdrv_snapshot_list(bs, &sn_tab); ++ if (nb_sns <= 0) ++ return; ++ printf("Snapshot list:\n"); ++ bdrv_snapshot_dump(NULL); ++ printf("\n"); ++ for(i = 0; i < nb_sns; i++) { ++ sn = &sn_tab[i]; ++ bdrv_snapshot_dump(sn); ++ printf("\n"); ++ } ++ g_free(sn_tab); ++} ++ ++static void dump_json_block_graph_info_list(BlockGraphInfoList *list) ++{ ++ GString *str; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_BlockGraphInfoList(v, NULL, &list, &error_abort); ++ visit_complete(v, &obj); ++ str = qobject_to_json_pretty(obj, true); ++ assert(str != NULL); ++ printf("%s\n", str->str); ++ qobject_unref(obj); ++ visit_free(v); ++ g_string_free(str, true); ++} ++ ++static void dump_json_block_graph_info(BlockGraphInfo *info) ++{ ++ GString *str; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_BlockGraphInfo(v, NULL, &info, &error_abort); ++ visit_complete(v, &obj); ++ str = qobject_to_json_pretty(obj, true); ++ assert(str != NULL); ++ printf("%s\n", str->str); ++ qobject_unref(obj); ++ visit_free(v); ++ g_string_free(str, true); ++} ++ ++static void dump_human_image_info(BlockGraphInfo *info, int indentation, ++ const char *path) ++{ ++ BlockChildInfoList *children_list; ++ ++ bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation, ++ info->children == NULL); ++ ++ for (children_list = info->children; children_list; ++ children_list = children_list->next) ++ { ++ BlockChildInfo *child = children_list->value; ++ g_autofree char *child_path = NULL; ++ ++ printf("%*sChild node '%s%s':\n", ++ indentation * 4, "", path, child->name); ++ child_path = g_strdup_printf("%s%s/", path, child->name); ++ dump_human_image_info(child->info, indentation + 1, child_path); ++ } ++} ++ ++static void dump_human_image_info_list(BlockGraphInfoList *list) ++{ ++ BlockGraphInfoList *elem; ++ bool delim = false; ++ ++ for (elem = list; elem; elem = elem->next) { ++ if (delim) { ++ printf("\n"); ++ } ++ delim = true; ++ ++ dump_human_image_info(elem->value, 0, "/"); ++ } ++} ++ ++static gboolean str_equal_func(gconstpointer a, gconstpointer b) ++{ ++ return strcmp(a, b) == 0; ++} ++ ++/** ++ * Open an image file chain and return an BlockGraphInfoList ++ * ++ * @filename: topmost image filename ++ * @fmt: topmost image format (may be NULL to autodetect) ++ * @chain: true - enumerate entire backing file chain ++ * false - only topmost image file ++ * ++ * Returns a list of BlockNodeInfo objects or NULL if there was an error ++ * opening an image file. If there was an error a message will have been ++ * printed to stderr. ++ */ ++static BlockGraphInfoList *collect_image_info_list(bool image_opts, ++ const char *filename, ++ const char *fmt, ++ bool chain, bool force_share) ++{ ++ BlockGraphInfoList *head = NULL; ++ BlockGraphInfoList **tail = &head; ++ GHashTable *filenames; ++ Error *err = NULL; ++ ++ filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL); ++ ++ while (filename) { ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ BlockGraphInfo *info; ++ ++ if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) { ++ error_report("Backing file '%s' creates an infinite loop.", ++ filename); ++ goto err; ++ } ++ g_hash_table_insert(filenames, (gpointer)filename, NULL); ++ ++ blk = img_open(image_opts, filename, fmt, ++ BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false, ++ force_share); ++ if (!blk) { ++ goto err; ++ } ++ bs = blk_bs(blk); ++ ++ /* ++ * Note that the returned BlockGraphInfo object will not have ++ * information about this image's backing node, because we have opened ++ * it with BDRV_O_NO_BACKING. Printing this object will therefore not ++ * duplicate the backing chain information that we obtain by walking ++ * the chain manually here. ++ */ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_query_block_graph_info(bs, &info, &err); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (err) { ++ error_report_err(err); ++ blk_unref(blk); ++ goto err; ++ } ++ ++ QAPI_LIST_APPEND(tail, info); ++ ++ blk_unref(blk); ++ ++ /* Clear parameters that only apply to the topmost image */ ++ filename = fmt = NULL; ++ image_opts = false; ++ ++ if (chain) { ++ if (info->full_backing_filename) { ++ filename = info->full_backing_filename; ++ } else if (info->backing_filename) { ++ error_report("Could not determine absolute backing filename," ++ " but backing filename '%s' present", ++ info->backing_filename); ++ goto err; ++ } ++ if (info->backing_filename_format) { ++ fmt = info->backing_filename_format; ++ } ++ } ++ } ++ g_hash_table_destroy(filenames); ++ return head; ++ ++err: ++ qapi_free_BlockGraphInfoList(head); ++ g_hash_table_destroy(filenames); ++ return NULL; ++} ++ ++static int img_info(int argc, char **argv) ++{ ++ int c; ++ OutputFormat output_format = OFORMAT_HUMAN; ++ bool chain = false; ++ const char *filename, *fmt, *output; ++ BlockGraphInfoList *list; ++ bool image_opts = false; ++ bool force_share = false; ++ ++ fmt = NULL; ++ output = NULL; ++ for(;;) { ++ int option_index = 0; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"format", required_argument, 0, 'f'}, ++ {"output", required_argument, 0, OPTION_OUTPUT}, ++ {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":f:hU", ++ long_options, &option_index); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OUTPUT: ++ output = optarg; ++ break; ++ case OPTION_BACKING_CHAIN: ++ chain = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind++]; ++ ++ if (output && !strcmp(output, "json")) { ++ output_format = OFORMAT_JSON; ++ } else if (output && !strcmp(output, "human")) { ++ output_format = OFORMAT_HUMAN; ++ } else if (output) { ++ error_report("--output must be used with human or json as argument."); ++ return 1; ++ } ++ ++ list = collect_image_info_list(image_opts, filename, fmt, chain, ++ force_share); ++ if (!list) { ++ return 1; ++ } ++ ++ switch (output_format) { ++ case OFORMAT_HUMAN: ++ dump_human_image_info_list(list); ++ break; ++ case OFORMAT_JSON: ++ if (chain) { ++ dump_json_block_graph_info_list(list); ++ } else { ++ dump_json_block_graph_info(list->value); ++ } ++ break; ++ } ++ ++ qapi_free_BlockGraphInfoList(list); ++ return 0; ++} ++ ++static int dump_map_entry(OutputFormat output_format, MapEntry *e, ++ MapEntry *next) ++{ ++ switch (output_format) { ++ case OFORMAT_HUMAN: ++ if (e->data && !e->has_offset) { ++ error_report("File contains external, encrypted or compressed clusters."); ++ return -1; ++ } ++ if (e->data && !e->zero) { ++ printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", ++ e->start, e->length, ++ e->has_offset ? e->offset : 0, ++ e->filename ?: ""); ++ } ++ /* This format ignores the distinction between 0, ZERO and ZERO|DATA. ++ * Modify the flags here to allow more coalescing. ++ */ ++ if (next && (!next->data || next->zero)) { ++ next->data = false; ++ next->zero = true; ++ } ++ break; ++ case OFORMAT_JSON: ++ printf("{ \"start\": %"PRId64", \"length\": %"PRId64"," ++ " \"depth\": %"PRId64", \"present\": %s, \"zero\": %s," ++ " \"data\": %s, \"compressed\": %s", ++ e->start, e->length, e->depth, ++ e->present ? "true" : "false", ++ e->zero ? "true" : "false", ++ e->data ? "true" : "false", ++ e->compressed ? "true" : "false"); ++ if (e->has_offset) { ++ printf(", \"offset\": %"PRId64"", e->offset); ++ } ++ putchar('}'); ++ ++ if (next) { ++ puts(","); ++ } ++ break; ++ } ++ return 0; ++} ++ ++static int get_block_status(BlockDriverState *bs, int64_t offset, ++ int64_t bytes, MapEntry *e) ++{ ++ int ret; ++ int depth; ++ BlockDriverState *file; ++ bool has_offset; ++ int64_t map; ++ char *filename = NULL; ++ ++ GLOBAL_STATE_CODE(); ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* As an optimization, we could cache the current range of unallocated ++ * clusters in each file of the chain, and avoid querying the same ++ * range repeatedly. ++ */ ++ ++ depth = 0; ++ for (;;) { ++ bs = bdrv_skip_filters(bs); ++ ret = bdrv_block_status(bs, offset, bytes, &bytes, &map, &file); ++ if (ret < 0) { ++ return ret; ++ } ++ assert(bytes); ++ if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) { ++ break; ++ } ++ bs = bdrv_cow_bs(bs); ++ if (bs == NULL) { ++ ret = 0; ++ break; ++ } ++ ++ depth++; ++ } ++ ++ has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); ++ ++ if (file && has_offset) { ++ bdrv_refresh_filename(file); ++ filename = file->filename; ++ } ++ ++ *e = (MapEntry) { ++ .start = offset, ++ .length = bytes, ++ .data = !!(ret & BDRV_BLOCK_DATA), ++ .zero = !!(ret & BDRV_BLOCK_ZERO), ++ .compressed = !!(ret & BDRV_BLOCK_COMPRESSED), ++ .offset = map, ++ .has_offset = has_offset, ++ .depth = depth, ++ .present = !!(ret & BDRV_BLOCK_ALLOCATED), ++ .filename = filename, ++ }; ++ ++ return 0; ++} ++ ++static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) ++{ ++ if (curr->length == 0) { ++ return false; ++ } ++ if (curr->zero != next->zero || ++ curr->data != next->data || ++ curr->compressed != next->compressed || ++ curr->depth != next->depth || ++ curr->present != next->present || ++ !curr->filename != !next->filename || ++ curr->has_offset != next->has_offset) { ++ return false; ++ } ++ if (curr->filename && strcmp(curr->filename, next->filename)) { ++ return false; ++ } ++ if (curr->has_offset && curr->offset + curr->length != next->offset) { ++ return false; ++ } ++ return true; ++} ++ ++static int img_map(int argc, char **argv) ++{ ++ int c; ++ OutputFormat output_format = OFORMAT_HUMAN; ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ const char *filename, *fmt, *output; ++ int64_t length; ++ MapEntry curr = { .length = 0 }, next; ++ int ret = 0; ++ bool image_opts = false; ++ bool force_share = false; ++ int64_t start_offset = 0; ++ int64_t max_length = -1; ++ ++ fmt = NULL; ++ output = NULL; ++ for (;;) { ++ int option_index = 0; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"format", required_argument, 0, 'f'}, ++ {"output", required_argument, 0, OPTION_OUTPUT}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {"start-offset", required_argument, 0, 's'}, ++ {"max-length", required_argument, 0, 'l'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":f:s:l:hU", ++ long_options, &option_index); ++ if (c == -1) { ++ break; ++ } ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OUTPUT: ++ output = optarg; ++ break; ++ case 's': ++ start_offset = cvtnum("start offset", optarg); ++ if (start_offset < 0) { ++ return 1; ++ } ++ break; ++ case 'l': ++ max_length = cvtnum("max length", optarg); ++ if (max_length < 0) { ++ return 1; ++ } ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind]; ++ ++ if (output && !strcmp(output, "json")) { ++ output_format = OFORMAT_JSON; ++ } else if (output && !strcmp(output, "human")) { ++ output_format = OFORMAT_HUMAN; ++ } else if (output) { ++ error_report("--output must be used with human or json as argument."); ++ return 1; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, 0, false, false, force_share); ++ if (!blk) { ++ return 1; ++ } ++ bs = blk_bs(blk); ++ ++ if (output_format == OFORMAT_HUMAN) { ++ printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File"); ++ } else if (output_format == OFORMAT_JSON) { ++ putchar('['); ++ } ++ ++ length = blk_getlength(blk); ++ if (length < 0) { ++ error_report("Failed to get size for '%s'", filename); ++ return 1; ++ } ++ if (max_length != -1) { ++ length = MIN(start_offset + max_length, length); ++ } ++ ++ curr.start = start_offset; ++ while (curr.start + curr.length < length) { ++ int64_t offset = curr.start + curr.length; ++ int64_t n = length - offset; ++ ++ ret = get_block_status(bs, offset, n, &next); ++ if (ret < 0) { ++ error_report("Could not read file metadata: %s", strerror(-ret)); ++ goto out; ++ } ++ ++ if (entry_mergeable(&curr, &next)) { ++ curr.length += next.length; ++ continue; ++ } ++ ++ if (curr.length > 0) { ++ ret = dump_map_entry(output_format, &curr, &next); ++ if (ret < 0) { ++ goto out; ++ } ++ } ++ curr = next; ++ } ++ ++ ret = dump_map_entry(output_format, &curr, NULL); ++ if (output_format == OFORMAT_JSON) { ++ puts("]"); ++ } ++ ++out: ++ blk_unref(blk); ++ return ret < 0; ++} ++ ++#define SNAPSHOT_LIST 1 ++#define SNAPSHOT_CREATE 2 ++#define SNAPSHOT_APPLY 3 ++#define SNAPSHOT_DELETE 4 ++ ++static int img_snapshot(int argc, char **argv) ++{ ++ BlockBackend *blk; ++ BlockDriverState *bs; ++ QEMUSnapshotInfo sn; ++ char *filename, *snapshot_name = NULL; ++ int c, ret = 0, bdrv_oflags; ++ int action = 0; ++ bool quiet = false; ++ Error *err = NULL; ++ bool image_opts = false; ++ bool force_share = false; ++ int64_t rt; ++ ++ bdrv_oflags = BDRV_O_RDWR; ++ /* Parse commandline parameters */ ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":la:c:d:hqU", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ return 0; ++ case 'l': ++ if (action) { ++ error_exit("Cannot mix '-l', '-a', '-c', '-d'"); ++ return 0; ++ } ++ action = SNAPSHOT_LIST; ++ bdrv_oflags &= ~BDRV_O_RDWR; /* no need for RW */ ++ break; ++ case 'a': ++ if (action) { ++ error_exit("Cannot mix '-l', '-a', '-c', '-d'"); ++ return 0; ++ } ++ action = SNAPSHOT_APPLY; ++ snapshot_name = optarg; ++ break; ++ case 'c': ++ if (action) { ++ error_exit("Cannot mix '-l', '-a', '-c', '-d'"); ++ return 0; ++ } ++ action = SNAPSHOT_CREATE; ++ snapshot_name = optarg; ++ break; ++ case 'd': ++ if (action) { ++ error_exit("Cannot mix '-l', '-a', '-c', '-d'"); ++ return 0; ++ } ++ action = SNAPSHOT_DELETE; ++ snapshot_name = optarg; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[optind++]; ++ ++ /* Open the image */ ++ blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet, ++ force_share); ++ if (!blk) { ++ return 1; ++ } ++ bs = blk_bs(blk); ++ ++ /* Perform the requested action */ ++ switch(action) { ++ case SNAPSHOT_LIST: ++ dump_snapshots(bs); ++ break; ++ ++ case SNAPSHOT_CREATE: ++ memset(&sn, 0, sizeof(sn)); ++ pstrcpy(sn.name, sizeof(sn.name), snapshot_name); ++ ++ rt = g_get_real_time(); ++ sn.date_sec = rt / G_USEC_PER_SEC; ++ sn.date_nsec = (rt % G_USEC_PER_SEC) * 1000; ++ ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_snapshot_create(bs, &sn); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (ret) { ++ error_report("Could not create snapshot '%s': %s", ++ snapshot_name, strerror(-ret)); ++ } ++ break; ++ ++ case SNAPSHOT_APPLY: ++ ret = bdrv_snapshot_goto(bs, snapshot_name, &err); ++ if (ret) { ++ error_reportf_err(err, "Could not apply snapshot '%s': ", ++ snapshot_name); ++ } ++ break; ++ ++ case SNAPSHOT_DELETE: ++ bdrv_graph_rdlock_main_loop(); ++ ret = bdrv_snapshot_find(bs, &sn, snapshot_name); ++ if (ret < 0) { ++ error_report("Could not delete snapshot '%s': snapshot not " ++ "found", snapshot_name); ++ ret = 1; ++ } else { ++ ret = bdrv_snapshot_delete(bs, sn.id_str, sn.name, &err); ++ if (ret < 0) { ++ error_reportf_err(err, "Could not delete snapshot '%s': ", ++ snapshot_name); ++ ret = 1; ++ } ++ } ++ bdrv_graph_rdunlock_main_loop(); ++ break; ++ } ++ ++ /* Cleanup */ ++ blk_unref(blk); ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++static int img_rebase(int argc, char **argv) ++{ ++ BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; ++ uint8_t *buf_old = NULL; ++ uint8_t *buf_new = NULL; ++ BlockDriverState *bs = NULL, *prefix_chain_bs = NULL; ++ BlockDriverState *unfiltered_bs, *unfiltered_bs_cow; ++ BlockDriverInfo bdi = {0}; ++ char *filename; ++ const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; ++ int c, flags, src_flags, ret; ++ BdrvRequestFlags write_flags = 0; ++ bool writethrough, src_writethrough; ++ int unsafe = 0; ++ bool force_share = false; ++ int progress = 0; ++ bool quiet = false; ++ bool compress = false; ++ Error *local_err = NULL; ++ bool image_opts = false; ++ int64_t write_align; ++ ++ /* Parse commandline parameters */ ++ fmt = NULL; ++ cache = BDRV_DEFAULT_CACHE; ++ src_cache = BDRV_DEFAULT_CACHE; ++ out_baseimg = NULL; ++ out_basefmt = NULL; ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force-share", no_argument, 0, 'U'}, ++ {"compress", no_argument, 0, 'c'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hf:F:b:upt:T:qUc", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ return 0; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'F': ++ out_basefmt = optarg; ++ break; ++ case 'b': ++ out_baseimg = optarg; ++ break; ++ case 'u': ++ unsafe = 1; ++ break; ++ case 'p': ++ progress = 1; ++ break; ++ case 't': ++ cache = optarg; ++ break; ++ case 'T': ++ src_cache = optarg; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case 'c': ++ compress = true; ++ break; ++ } ++ } ++ ++ if (quiet) { ++ progress = 0; ++ } ++ ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ if (!unsafe && !out_baseimg) { ++ error_exit("Must specify backing file (-b) or use unsafe mode (-u)"); ++ } ++ filename = argv[optind++]; ++ ++ qemu_progress_init(progress, 2.0); ++ qemu_progress_print(0, 100); ++ ++ flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache option: %s", cache); ++ goto out; ++ } ++ ++ src_flags = 0; ++ ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); ++ if (ret < 0) { ++ error_report("Invalid source cache option: %s", src_cache); ++ goto out; ++ } ++ ++ /* The source files are opened read-only, don't care about WCE */ ++ assert((src_flags & BDRV_O_RDWR) == 0); ++ (void) src_writethrough; ++ ++ /* ++ * Open the images. ++ * ++ * Ignore the old backing file for unsafe rebase in case we want to correct ++ * the reference to a renamed or moved backing file. ++ */ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ false); ++ if (!blk) { ++ ret = -1; ++ goto out; ++ } ++ bs = blk_bs(blk); ++ ++ bdrv_graph_rdlock_main_loop(); ++ unfiltered_bs = bdrv_skip_filters(bs); ++ unfiltered_bs_cow = bdrv_cow_bs(unfiltered_bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (compress && !block_driver_can_compress(unfiltered_bs->drv)) { ++ error_report("Compression not supported for this file format"); ++ ret = -1; ++ goto out; ++ } else if (compress) { ++ write_flags |= BDRV_REQ_WRITE_COMPRESSED; ++ } ++ ++ if (out_basefmt != NULL) { ++ if (bdrv_find_format(out_basefmt) == NULL) { ++ error_report("Invalid format name: '%s'", out_basefmt); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ /* ++ * We need overlay subcluster size (or cluster size in case writes are ++ * compressed) to make sure write requests are aligned. ++ */ ++ ret = bdrv_get_info(unfiltered_bs, &bdi); ++ if (ret < 0) { ++ error_report("could not get block driver info"); ++ goto out; ++ } else if (bdi.subcluster_size == 0) { ++ bdi.cluster_size = bdi.subcluster_size = 1; ++ } ++ ++ write_align = compress ? bdi.cluster_size : bdi.subcluster_size; ++ ++ /* For safe rebasing we need to compare old and new backing file */ ++ if (!unsafe) { ++ QDict *options = NULL; ++ BlockDriverState *base_bs; ++ ++ bdrv_graph_rdlock_main_loop(); ++ base_bs = bdrv_cow_bs(unfiltered_bs); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (base_bs) { ++ blk_old_backing = blk_new(qemu_get_aio_context(), ++ BLK_PERM_CONSISTENT_READ, ++ BLK_PERM_ALL); ++ ret = blk_insert_bs(blk_old_backing, base_bs, ++ &local_err); ++ if (ret < 0) { ++ error_reportf_err(local_err, ++ "Could not reuse old backing file '%s': ", ++ base_bs->filename); ++ goto out; ++ } ++ } else { ++ blk_old_backing = NULL; ++ } ++ ++ if (out_baseimg[0]) { ++ const char *overlay_filename; ++ char *out_real_path; ++ ++ options = qdict_new(); ++ if (out_basefmt) { ++ qdict_put_str(options, "driver", out_basefmt); ++ } ++ if (force_share) { ++ qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ bdrv_refresh_filename(bs); ++ bdrv_graph_rdunlock_main_loop(); ++ overlay_filename = bs->exact_filename[0] ? bs->exact_filename ++ : bs->filename; ++ out_real_path = ++ bdrv_get_full_backing_filename_from_filename(overlay_filename, ++ out_baseimg, ++ &local_err); ++ if (local_err) { ++ qobject_unref(options); ++ error_reportf_err(local_err, ++ "Could not resolve backing filename: "); ++ ret = -1; ++ goto out; ++ } ++ ++ /* ++ * Find out whether we rebase an image on top of a previous image ++ * in its chain. ++ */ ++ prefix_chain_bs = bdrv_find_backing_image(bs, out_real_path); ++ if (prefix_chain_bs) { ++ qobject_unref(options); ++ g_free(out_real_path); ++ ++ blk_new_backing = blk_new(qemu_get_aio_context(), ++ BLK_PERM_CONSISTENT_READ, ++ BLK_PERM_ALL); ++ ret = blk_insert_bs(blk_new_backing, prefix_chain_bs, ++ &local_err); ++ if (ret < 0) { ++ error_reportf_err(local_err, ++ "Could not reuse backing file '%s': ", ++ out_baseimg); ++ goto out; ++ } ++ } else { ++ blk_new_backing = blk_new_open(out_real_path, NULL, ++ options, src_flags, &local_err); ++ g_free(out_real_path); ++ if (!blk_new_backing) { ++ error_reportf_err(local_err, ++ "Could not open new backing file '%s': ", ++ out_baseimg); ++ ret = -1; ++ goto out; ++ } ++ } ++ } ++ } ++ ++ /* ++ * Check each unallocated cluster in the COW file. If it is unallocated, ++ * accesses go to the backing file. We must therefore compare this cluster ++ * in the old and new backing file, and if they differ we need to copy it ++ * from the old backing file into the COW file. ++ * ++ * If qemu-img crashes during this step, no harm is done. The content of ++ * the image is the same as the original one at any time. ++ */ ++ if (!unsafe) { ++ int64_t size; ++ int64_t old_backing_size = 0; ++ int64_t new_backing_size = 0; ++ uint64_t offset; ++ int64_t n, n_old = 0, n_new = 0; ++ float local_progress = 0; ++ ++ if (blk_old_backing && bdrv_opt_mem_align(blk_bs(blk_old_backing)) > ++ bdrv_opt_mem_align(blk_bs(blk))) { ++ buf_old = blk_blockalign(blk_old_backing, IO_BUF_SIZE); ++ } else { ++ buf_old = blk_blockalign(blk, IO_BUF_SIZE); ++ } ++ buf_new = blk_blockalign(blk_new_backing, IO_BUF_SIZE); ++ ++ size = blk_getlength(blk); ++ if (size < 0) { ++ error_report("Could not get size of '%s': %s", ++ filename, strerror(-size)); ++ ret = -1; ++ goto out; ++ } ++ if (blk_old_backing) { ++ old_backing_size = blk_getlength(blk_old_backing); ++ if (old_backing_size < 0) { ++ char backing_name[PATH_MAX]; ++ ++ bdrv_get_backing_filename(bs, backing_name, ++ sizeof(backing_name)); ++ error_report("Could not get size of '%s': %s", ++ backing_name, strerror(-old_backing_size)); ++ ret = -1; ++ goto out; ++ } ++ } ++ if (blk_new_backing) { ++ new_backing_size = blk_getlength(blk_new_backing); ++ if (new_backing_size < 0) { ++ error_report("Could not get size of '%s': %s", ++ out_baseimg, strerror(-new_backing_size)); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ if (size != 0) { ++ local_progress = (float)100 / (size / MIN(size, IO_BUF_SIZE)); ++ } ++ ++ for (offset = 0; offset < size; offset += n) { ++ bool old_backing_eof = false; ++ int64_t n_alloc; ++ ++ /* How many bytes can we handle with the next read? */ ++ n = MIN(IO_BUF_SIZE, size - offset); ++ ++ /* If the cluster is allocated, we don't need to take action */ ++ ret = bdrv_is_allocated(unfiltered_bs, offset, n, &n); ++ if (ret < 0) { ++ error_report("error while reading image metadata: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ if (ret) { ++ continue; ++ } ++ ++ if (prefix_chain_bs) { ++ uint64_t bytes = n; ++ ++ /* ++ * If cluster wasn't changed since prefix_chain, we don't need ++ * to take action ++ */ ++ ret = bdrv_is_allocated_above(unfiltered_bs_cow, ++ prefix_chain_bs, false, ++ offset, n, &n); ++ if (ret < 0) { ++ error_report("error while reading image metadata: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ if (!ret && n) { ++ continue; ++ } ++ if (!n) { ++ /* ++ * If we've reached EOF of the old backing, it means that ++ * offsets beyond the old backing size were read as zeroes. ++ * Now we will need to explicitly zero the cluster in ++ * order to preserve that state after the rebase. ++ */ ++ n = bytes; ++ } ++ } ++ ++ /* ++ * At this point we know that the region [offset; offset + n) ++ * is unallocated within the target image. This region might be ++ * unaligned to the target image's (sub)cluster boundaries, as ++ * old backing may have smaller clusters (or have subclusters). ++ * We extend it to the aligned boundaries to avoid CoW on ++ * partial writes in blk_pwrite(), ++ */ ++ n += offset - QEMU_ALIGN_DOWN(offset, write_align); ++ offset = QEMU_ALIGN_DOWN(offset, write_align); ++ n += QEMU_ALIGN_UP(offset + n, write_align) - (offset + n); ++ n = MIN(n, size - offset); ++ assert(!bdrv_is_allocated(unfiltered_bs, offset, n, &n_alloc) && ++ n_alloc == n); ++ ++ /* ++ * Much like with the target image, we'll try to read as much ++ * of the old and new backings as we can. ++ */ ++ n_old = MIN(n, MAX(0, old_backing_size - (int64_t) offset)); ++ n_new = MIN(n, MAX(0, new_backing_size - (int64_t) offset)); ++ ++ /* ++ * Read old and new backing file and take into consideration that ++ * backing files may be smaller than the COW image. ++ */ ++ memset(buf_old + n_old, 0, n - n_old); ++ if (!n_old) { ++ old_backing_eof = true; ++ } else { ++ ret = blk_pread(blk_old_backing, offset, n_old, buf_old, 0); ++ if (ret < 0) { ++ error_report("error while reading from old backing file"); ++ goto out; ++ } ++ } ++ ++ memset(buf_new + n_new, 0, n - n_new); ++ if (n_new) { ++ ret = blk_pread(blk_new_backing, offset, n_new, buf_new, 0); ++ if (ret < 0) { ++ error_report("error while reading from new backing file"); ++ goto out; ++ } ++ } ++ ++ /* If they differ, we need to write to the COW file */ ++ uint64_t written = 0; ++ ++ while (written < n) { ++ int64_t pnum; ++ ++ if (compare_buffers(buf_old + written, buf_new + written, ++ n - written, write_align, &pnum)) ++ { ++ if (old_backing_eof) { ++ ret = blk_pwrite_zeroes(blk, offset + written, pnum, 0); ++ } else { ++ assert(written + pnum <= IO_BUF_SIZE); ++ ret = blk_pwrite(blk, offset + written, pnum, ++ buf_old + written, write_flags); ++ } ++ if (ret < 0) { ++ error_report("Error while writing to COW image: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ } ++ ++ written += pnum; ++ if (offset + written >= old_backing_size) { ++ old_backing_eof = true; ++ } ++ } ++ qemu_progress_print(local_progress, 100); ++ } ++ } ++ ++ /* ++ * Change the backing file. All clusters that are different from the old ++ * backing file are overwritten in the COW file now, so the visible content ++ * doesn't change when we switch the backing file. ++ */ ++ if (out_baseimg && *out_baseimg) { ++ ret = bdrv_change_backing_file(unfiltered_bs, out_baseimg, out_basefmt, ++ true); ++ } else { ++ ret = bdrv_change_backing_file(unfiltered_bs, NULL, NULL, false); ++ } ++ ++ if (ret == -ENOSPC) { ++ error_report("Could not change the backing file to '%s': No " ++ "space left in the file header", out_baseimg); ++ } else if (ret == -EINVAL && out_baseimg && !out_basefmt) { ++ error_report("Could not change the backing file to '%s': backing " ++ "format must be specified", out_baseimg); ++ } else if (ret < 0) { ++ error_report("Could not change the backing file to '%s': %s", ++ out_baseimg, strerror(-ret)); ++ } ++ ++ qemu_progress_print(100, 0); ++ /* ++ * TODO At this point it is possible to check if any clusters that are ++ * allocated in the COW file are the same in the backing file. If so, they ++ * could be dropped from the COW file. Don't do this before switching the ++ * backing file, in case of a crash this would lead to corruption. ++ */ ++out: ++ qemu_progress_end(); ++ /* Cleanup */ ++ if (!unsafe) { ++ blk_unref(blk_old_backing); ++ blk_unref(blk_new_backing); ++ } ++ qemu_vfree(buf_old); ++ qemu_vfree(buf_new); ++ ++ blk_unref(blk); ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++static int img_resize(int argc, char **argv) ++{ ++ Error *err = NULL; ++ int c, ret, relative; ++ const char *filename, *fmt, *size; ++ int64_t n, total_size, current_size; ++ bool quiet = false; ++ BlockBackend *blk = NULL; ++ PreallocMode prealloc = PREALLOC_MODE_OFF; ++ QemuOpts *param; ++ ++ static QemuOptsList resize_options = { ++ .name = "resize_options", ++ .head = QTAILQ_HEAD_INITIALIZER(resize_options.head), ++ .desc = { ++ { ++ .name = BLOCK_OPT_SIZE, ++ .type = QEMU_OPT_SIZE, ++ .help = "Virtual disk size" ++ }, { ++ /* end of list */ ++ } ++ }, ++ }; ++ bool image_opts = false; ++ bool shrink = false; ++ ++ /* Remove size from argv manually so that negative numbers are not treated ++ * as options by getopt. */ ++ if (argc < 3) { ++ error_exit("Not enough arguments"); ++ return 1; ++ } ++ ++ size = argv[--argc]; ++ ++ /* Parse getopt arguments */ ++ fmt = NULL; ++ for(;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"preallocation", required_argument, 0, OPTION_PREALLOCATION}, ++ {"shrink", no_argument, 0, OPTION_SHRINK}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":f:hq", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ switch(c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case OPTION_PREALLOCATION: ++ prealloc = qapi_enum_parse(&PreallocMode_lookup, optarg, ++ PREALLOC_MODE__MAX, NULL); ++ if (prealloc == PREALLOC_MODE__MAX) { ++ error_report("Invalid preallocation mode '%s'", optarg); ++ return 1; ++ } ++ break; ++ case OPTION_SHRINK: ++ shrink = true; ++ break; ++ } ++ } ++ if (optind != argc - 1) { ++ error_exit("Expecting image file name and size"); ++ } ++ filename = argv[optind++]; ++ ++ /* Choose grow, shrink, or absolute resize mode */ ++ switch (size[0]) { ++ case '+': ++ relative = 1; ++ size++; ++ break; ++ case '-': ++ relative = -1; ++ size++; ++ break; ++ default: ++ relative = 0; ++ break; ++ } ++ ++ /* Parse size */ ++ param = qemu_opts_create(&resize_options, NULL, 0, &error_abort); ++ if (!qemu_opt_set(param, BLOCK_OPT_SIZE, size, &err)) { ++ error_report_err(err); ++ ret = -1; ++ qemu_opts_del(param); ++ goto out; ++ } ++ n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); ++ qemu_opts_del(param); ++ ++ blk = img_open(image_opts, filename, fmt, ++ BDRV_O_RDWR | BDRV_O_RESIZE, false, quiet, ++ false); ++ if (!blk) { ++ ret = -1; ++ goto out; ++ } ++ ++ current_size = blk_getlength(blk); ++ if (current_size < 0) { ++ error_report("Failed to inquire current image length: %s", ++ strerror(-current_size)); ++ ret = -1; ++ goto out; ++ } ++ ++ if (relative) { ++ total_size = current_size + n * relative; ++ } else { ++ total_size = n; ++ } ++ if (total_size <= 0) { ++ error_report("New image size must be positive"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (total_size <= current_size && prealloc != PREALLOC_MODE_OFF) { ++ error_report("Preallocation can only be used for growing images"); ++ ret = -1; ++ goto out; ++ } ++ ++ if (total_size < current_size && !shrink) { ++ error_report("Use the --shrink option to perform a shrink operation."); ++ warn_report("Shrinking an image will delete all data beyond the " ++ "shrunken image's end. Before performing such an " ++ "operation, make sure there is no important data there."); ++ ret = -1; ++ goto out; ++ } ++ ++ /* ++ * The user expects the image to have the desired size after ++ * resizing, so pass @exact=true. It is of no use to report ++ * success when the image has not actually been resized. ++ */ ++ ret = blk_truncate(blk, total_size, true, prealloc, 0, &err); ++ if (!ret) { ++ qprintf(quiet, "Image resized.\n"); ++ } else { ++ error_report_err(err); ++ } ++out: ++ blk_unref(blk); ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++static void amend_status_cb(BlockDriverState *bs, ++ int64_t offset, int64_t total_work_size, ++ void *opaque) ++{ ++ qemu_progress_print(100.f * offset / total_work_size, 0); ++} ++ ++static int print_amend_option_help(const char *format) ++{ ++ BlockDriver *drv; ++ ++ GRAPH_RDLOCK_GUARD_MAINLOOP(); ++ ++ /* Find driver and parse its options */ ++ drv = bdrv_find_format(format); ++ if (!drv) { ++ error_report("Unknown file format '%s'", format); ++ return 1; ++ } ++ ++ if (!drv->bdrv_amend_options) { ++ error_report("Format driver '%s' does not support option amendment", ++ format); ++ return 1; ++ } ++ ++ /* Every driver supporting amendment must have amend_opts */ ++ assert(drv->amend_opts); ++ ++ printf("Amend options for '%s':\n", format); ++ qemu_opts_print_help(drv->amend_opts, false); ++ return 0; ++} ++ ++static int img_amend(int argc, char **argv) ++{ ++ Error *err = NULL; ++ int c, ret = 0; ++ char *options = NULL; ++ QemuOptsList *amend_opts = NULL; ++ QemuOpts *opts = NULL; ++ const char *fmt = NULL, *filename, *cache; ++ int flags; ++ bool writethrough; ++ bool quiet = false, progress = false; ++ BlockBackend *blk = NULL; ++ BlockDriverState *bs = NULL; ++ bool image_opts = false; ++ bool force = false; ++ ++ cache = BDRV_DEFAULT_CACHE; ++ for (;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"force", no_argument, 0, OPTION_FORCE}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":ho:f:t:pq", ++ long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'o': ++ if (accumulate_options(&options, optarg) < 0) { ++ ret = -1; ++ goto out_no_progress; ++ } ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 't': ++ cache = optarg; ++ break; ++ case 'p': ++ progress = true; ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case OPTION_FORCE: ++ force = true; ++ break; ++ } ++ } ++ ++ if (!options) { ++ error_exit("Must specify options (-o)"); ++ } ++ ++ if (quiet) { ++ progress = false; ++ } ++ qemu_progress_init(progress, 1.0); ++ ++ filename = (optind == argc - 1) ? argv[argc - 1] : NULL; ++ if (fmt && has_help_option(options)) { ++ /* If a format is explicitly specified (and possibly no filename is ++ * given), print option help here */ ++ ret = print_amend_option_help(fmt); ++ goto out; ++ } ++ ++ if (optind != argc - 1) { ++ error_report("Expecting one image file name"); ++ ret = -1; ++ goto out; ++ } ++ ++ flags = BDRV_O_RDWR; ++ ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache option: %s", cache); ++ goto out; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ false); ++ if (!blk) { ++ ret = -1; ++ goto out; ++ } ++ bs = blk_bs(blk); ++ ++ fmt = bs->drv->format_name; ++ ++ if (has_help_option(options)) { ++ /* If the format was auto-detected, print option help here */ ++ ret = print_amend_option_help(fmt); ++ goto out; ++ } ++ ++ bdrv_graph_rdlock_main_loop(); ++ if (!bs->drv->bdrv_amend_options) { ++ error_report("Format driver '%s' does not support option amendment", ++ fmt); ++ bdrv_graph_rdunlock_main_loop(); ++ ret = -1; ++ goto out; ++ } ++ ++ /* Every driver supporting amendment must have amend_opts */ ++ assert(bs->drv->amend_opts); ++ ++ amend_opts = qemu_opts_append(amend_opts, bs->drv->amend_opts); ++ opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort); ++ if (!qemu_opts_do_parse(opts, options, NULL, &err)) { ++ /* Try to parse options using the create options */ ++ amend_opts = qemu_opts_append(amend_opts, bs->drv->create_opts); ++ qemu_opts_del(opts); ++ opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort); ++ if (qemu_opts_do_parse(opts, options, NULL, NULL)) { ++ error_append_hint(&err, ++ "This option is only supported for image creation\n"); ++ } ++ ++ bdrv_graph_rdunlock_main_loop(); ++ error_report_err(err); ++ ret = -1; ++ goto out; ++ } ++ ++ /* In case the driver does not call amend_status_cb() */ ++ qemu_progress_print(0.f, 0); ++ ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, force, &err); ++ qemu_progress_print(100.f, 0); ++ bdrv_graph_rdunlock_main_loop(); ++ ++ if (ret < 0) { ++ error_report_err(err); ++ goto out; ++ } ++ ++out: ++ qemu_progress_end(); ++ ++out_no_progress: ++ blk_unref(blk); ++ qemu_opts_del(opts); ++ qemu_opts_free(amend_opts); ++ g_free(options); ++ ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++typedef struct BenchData { ++ BlockBackend *blk; ++ uint64_t image_size; ++ bool write; ++ int bufsize; ++ int step; ++ int nrreq; ++ int n; ++ int flush_interval; ++ bool drain_on_flush; ++ uint8_t *buf; ++ QEMUIOVector *qiov; ++ ++ int in_flight; ++ bool in_flush; ++ uint64_t offset; ++} BenchData; ++ ++static void bench_undrained_flush_cb(void *opaque, int ret) ++{ ++ if (ret < 0) { ++ error_report("Failed flush request: %s", strerror(-ret)); ++ exit(EXIT_FAILURE); ++ } ++} ++ ++static void bench_cb(void *opaque, int ret) ++{ ++ BenchData *b = opaque; ++ BlockAIOCB *acb; ++ ++ if (ret < 0) { ++ error_report("Failed request: %s", strerror(-ret)); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (b->in_flush) { ++ /* Just finished a flush with drained queue: Start next requests */ ++ assert(b->in_flight == 0); ++ b->in_flush = false; ++ } else if (b->in_flight > 0) { ++ int remaining = b->n - b->in_flight; ++ ++ b->n--; ++ b->in_flight--; ++ ++ /* Time for flush? Drain queue if requested, then flush */ ++ if (b->flush_interval && remaining % b->flush_interval == 0) { ++ if (!b->in_flight || !b->drain_on_flush) { ++ BlockCompletionFunc *cb; ++ ++ if (b->drain_on_flush) { ++ b->in_flush = true; ++ cb = bench_cb; ++ } else { ++ cb = bench_undrained_flush_cb; ++ } ++ ++ acb = blk_aio_flush(b->blk, cb, b); ++ if (!acb) { ++ error_report("Failed to issue flush request"); ++ exit(EXIT_FAILURE); ++ } ++ } ++ if (b->drain_on_flush) { ++ return; ++ } ++ } ++ } ++ ++ while (b->n > b->in_flight && b->in_flight < b->nrreq) { ++ int64_t offset = b->offset; ++ /* blk_aio_* might look for completed I/Os and kick bench_cb ++ * again, so make sure this operation is counted by in_flight ++ * and b->offset is ready for the next submission. ++ */ ++ b->in_flight++; ++ b->offset += b->step; ++ b->offset %= b->image_size; ++ if (b->write) { ++ acb = blk_aio_pwritev(b->blk, offset, b->qiov, 0, bench_cb, b); ++ } else { ++ acb = blk_aio_preadv(b->blk, offset, b->qiov, 0, bench_cb, b); ++ } ++ if (!acb) { ++ error_report("Failed to issue request"); ++ exit(EXIT_FAILURE); ++ } ++ } ++} ++ ++static int img_bench(int argc, char **argv) ++{ ++ int c, ret = 0; ++ const char *fmt = NULL, *filename; ++ bool quiet = false; ++ bool image_opts = false; ++ bool is_write = false; ++ int count = 75000; ++ int depth = 64; ++ int64_t offset = 0; ++ size_t bufsize = 4096; ++ int pattern = 0; ++ size_t step = 0; ++ int flush_interval = 0; ++ bool drain_on_flush = true; ++ int64_t image_size; ++ BlockBackend *blk = NULL; ++ BenchData data = {}; ++ int flags = 0; ++ bool writethrough = false; ++ struct timeval t1, t2; ++ int i; ++ bool force_share = false; ++ size_t buf_size = 0; ++ ++ for (;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"flush-interval", required_argument, 0, OPTION_FLUSH_INTERVAL}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"pattern", required_argument, 0, OPTION_PATTERN}, ++ {"no-drain", no_argument, 0, OPTION_NO_DRAIN}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":hc:d:f:ni:o:qs:S:t:wU", long_options, ++ NULL); ++ if (c == -1) { ++ break; ++ } ++ ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'c': ++ { ++ unsigned long res; ++ ++ if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { ++ error_report("Invalid request count specified"); ++ return 1; ++ } ++ count = res; ++ break; ++ } ++ case 'd': ++ { ++ unsigned long res; ++ ++ if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { ++ error_report("Invalid queue depth specified"); ++ return 1; ++ } ++ depth = res; ++ break; ++ } ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'n': ++ flags |= BDRV_O_NATIVE_AIO; ++ break; ++ case 'i': ++ ret = bdrv_parse_aio(optarg, &flags); ++ if (ret < 0) { ++ error_report("Invalid aio option: %s", optarg); ++ ret = -1; ++ goto out; ++ } ++ break; ++ case 'o': ++ { ++ offset = cvtnum("offset", optarg); ++ if (offset < 0) { ++ return 1; ++ } ++ break; ++ } ++ break; ++ case 'q': ++ quiet = true; ++ break; ++ case 's': ++ { ++ int64_t sval; ++ ++ sval = cvtnum_full("buffer size", optarg, 0, INT_MAX); ++ if (sval < 0) { ++ return 1; ++ } ++ ++ bufsize = sval; ++ break; ++ } ++ case 'S': ++ { ++ int64_t sval; ++ ++ sval = cvtnum_full("step_size", optarg, 0, INT_MAX); ++ if (sval < 0) { ++ return 1; ++ } ++ ++ step = sval; ++ break; ++ } ++ case 't': ++ ret = bdrv_parse_cache_mode(optarg, &flags, &writethrough); ++ if (ret < 0) { ++ error_report("Invalid cache mode"); ++ ret = -1; ++ goto out; ++ } ++ break; ++ case 'w': ++ flags |= BDRV_O_RDWR; ++ is_write = true; ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_PATTERN: ++ { ++ unsigned long res; ++ ++ if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > 0xff) { ++ error_report("Invalid pattern byte specified"); ++ return 1; ++ } ++ pattern = res; ++ break; ++ } ++ case OPTION_FLUSH_INTERVAL: ++ { ++ unsigned long res; ++ ++ if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { ++ error_report("Invalid flush interval specified"); ++ return 1; ++ } ++ flush_interval = res; ++ break; ++ } ++ case OPTION_NO_DRAIN: ++ drain_on_flush = false; ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ if (optind != argc - 1) { ++ error_exit("Expecting one image file name"); ++ } ++ filename = argv[argc - 1]; ++ ++ if (!is_write && flush_interval) { ++ error_report("--flush-interval is only available in write tests"); ++ ret = -1; ++ goto out; ++ } ++ if (flush_interval && flush_interval < depth) { ++ error_report("Flush interval can't be smaller than depth"); ++ ret = -1; ++ goto out; ++ } ++ ++ blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet, ++ force_share); ++ if (!blk) { ++ ret = -1; ++ goto out; ++ } ++ ++ image_size = blk_getlength(blk); ++ if (image_size < 0) { ++ ret = image_size; ++ goto out; ++ } ++ ++ data = (BenchData) { ++ .blk = blk, ++ .image_size = image_size, ++ .bufsize = bufsize, ++ .step = step ?: bufsize, ++ .nrreq = depth, ++ .n = count, ++ .offset = offset, ++ .write = is_write, ++ .flush_interval = flush_interval, ++ .drain_on_flush = drain_on_flush, ++ }; ++ printf("Sending %d %s requests, %d bytes each, %d in parallel " ++ "(starting at offset %" PRId64 ", step size %d)\n", ++ data.n, data.write ? "write" : "read", data.bufsize, data.nrreq, ++ data.offset, data.step); ++ if (flush_interval) { ++ printf("Sending flush every %d requests\n", flush_interval); ++ } ++ ++ buf_size = data.nrreq * data.bufsize; ++ data.buf = blk_blockalign(blk, buf_size); ++ memset(data.buf, pattern, data.nrreq * data.bufsize); ++ ++ blk_register_buf(blk, data.buf, buf_size, &error_fatal); ++ ++ data.qiov = g_new(QEMUIOVector, data.nrreq); ++ for (i = 0; i < data.nrreq; i++) { ++ qemu_iovec_init(&data.qiov[i], 1); ++ qemu_iovec_add(&data.qiov[i], ++ data.buf + i * data.bufsize, data.bufsize); ++ } ++ ++ gettimeofday(&t1, NULL); ++ bench_cb(&data, 0); ++ ++ while (data.n > 0) { ++ main_loop_wait(false); ++ } ++ gettimeofday(&t2, NULL); ++ ++ printf("Run completed in %3.3f seconds.\n", ++ (t2.tv_sec - t1.tv_sec) ++ + ((double)(t2.tv_usec - t1.tv_usec) / 1000000)); ++ ++out: ++ if (data.buf) { ++ blk_unregister_buf(blk, data.buf, buf_size); ++ } ++ qemu_vfree(data.buf); ++ blk_unref(blk); ++ ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++enum ImgBitmapAct { ++ BITMAP_ADD, ++ BITMAP_REMOVE, ++ BITMAP_CLEAR, ++ BITMAP_ENABLE, ++ BITMAP_DISABLE, ++ BITMAP_MERGE, ++}; ++typedef struct ImgBitmapAction { ++ enum ImgBitmapAct act; ++ const char *src; /* only used for merge */ ++ QSIMPLEQ_ENTRY(ImgBitmapAction) next; ++} ImgBitmapAction; ++ ++static int img_bitmap(int argc, char **argv) ++{ ++ Error *err = NULL; ++ int c, ret = 1; ++ QemuOpts *opts = NULL; ++ const char *fmt = NULL, *src_fmt = NULL, *src_filename = NULL; ++ const char *filename, *bitmap; ++ BlockBackend *blk = NULL, *src = NULL; ++ BlockDriverState *bs = NULL, *src_bs = NULL; ++ bool image_opts = false; ++ int64_t granularity = 0; ++ bool add = false, merge = false; ++ QSIMPLEQ_HEAD(, ImgBitmapAction) actions; ++ ImgBitmapAction *act, *act_next; ++ const char *op; ++ int inactivate_ret; ++ ++ QSIMPLEQ_INIT(&actions); ++ ++ for (;;) { ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"add", no_argument, 0, OPTION_ADD}, ++ {"remove", no_argument, 0, OPTION_REMOVE}, ++ {"clear", no_argument, 0, OPTION_CLEAR}, ++ {"enable", no_argument, 0, OPTION_ENABLE}, ++ {"disable", no_argument, 0, OPTION_DISABLE}, ++ {"merge", required_argument, 0, OPTION_MERGE}, ++ {"granularity", required_argument, 0, 'g'}, ++ {"source-file", required_argument, 0, 'b'}, ++ {"source-format", required_argument, 0, 'F'}, ++ {0, 0, 0, 0} ++ }; ++ c = getopt_long(argc, argv, ":b:f:F:g:h", long_options, NULL); ++ if (c == -1) { ++ break; ++ } ++ ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'b': ++ src_filename = optarg; ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'F': ++ src_fmt = optarg; ++ break; ++ case 'g': ++ granularity = cvtnum("granularity", optarg); ++ if (granularity < 0) { ++ return 1; ++ } ++ break; ++ case OPTION_ADD: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_ADD; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ add = true; ++ break; ++ case OPTION_REMOVE: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_REMOVE; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ break; ++ case OPTION_CLEAR: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_CLEAR; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ break; ++ case OPTION_ENABLE: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_ENABLE; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ break; ++ case OPTION_DISABLE: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_DISABLE; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ break; ++ case OPTION_MERGE: ++ act = g_new0(ImgBitmapAction, 1); ++ act->act = BITMAP_MERGE; ++ act->src = optarg; ++ QSIMPLEQ_INSERT_TAIL(&actions, act, next); ++ merge = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ if (QSIMPLEQ_EMPTY(&actions)) { ++ error_report("Need at least one of --add, --remove, --clear, " ++ "--enable, --disable, or --merge"); ++ goto out; ++ } ++ ++ if (granularity && !add) { ++ error_report("granularity only supported with --add"); ++ goto out; ++ } ++ if (src_fmt && !src_filename) { ++ error_report("-F only supported with -b"); ++ goto out; ++ } ++ if (src_filename && !merge) { ++ error_report("Merge bitmap source file only supported with " ++ "--merge"); ++ goto out; ++ } ++ ++ if (optind != argc - 2) { ++ error_report("Expecting filename and bitmap name"); ++ goto out; ++ } ++ ++ filename = argv[optind]; ++ bitmap = argv[optind + 1]; ++ ++ /* ++ * No need to open backing chains; we will be manipulating bitmaps ++ * directly in this image without reference to image contents. ++ */ ++ blk = img_open(image_opts, filename, fmt, BDRV_O_RDWR | BDRV_O_NO_BACKING, ++ false, false, false); ++ if (!blk) { ++ goto out; ++ } ++ bs = blk_bs(blk); ++ if (src_filename) { ++ src = img_open(false, src_filename, src_fmt, BDRV_O_NO_BACKING, ++ false, false, false); ++ if (!src) { ++ goto out; ++ } ++ src_bs = blk_bs(src); ++ } else { ++ src_bs = bs; ++ } ++ ++ QSIMPLEQ_FOREACH_SAFE(act, &actions, next, act_next) { ++ switch (act->act) { ++ case BITMAP_ADD: ++ qmp_block_dirty_bitmap_add(bs->node_name, bitmap, ++ !!granularity, granularity, true, true, ++ false, false, &err); ++ op = "add"; ++ break; ++ case BITMAP_REMOVE: ++ qmp_block_dirty_bitmap_remove(bs->node_name, bitmap, &err); ++ op = "remove"; ++ break; ++ case BITMAP_CLEAR: ++ qmp_block_dirty_bitmap_clear(bs->node_name, bitmap, &err); ++ op = "clear"; ++ break; ++ case BITMAP_ENABLE: ++ qmp_block_dirty_bitmap_enable(bs->node_name, bitmap, &err); ++ op = "enable"; ++ break; ++ case BITMAP_DISABLE: ++ qmp_block_dirty_bitmap_disable(bs->node_name, bitmap, &err); ++ op = "disable"; ++ break; ++ case BITMAP_MERGE: ++ do_dirty_bitmap_merge(bs->node_name, bitmap, src_bs->node_name, ++ act->src, &err); ++ op = "merge"; ++ break; ++ default: ++ g_assert_not_reached(); ++ } ++ ++ if (err) { ++ error_reportf_err(err, "Operation %s on bitmap %s failed: ", ++ op, bitmap); ++ goto out; ++ } ++ g_free(act); ++ } ++ ++ ret = 0; ++ ++ out: ++ /* ++ * Manually inactivate the images first because this way we can know whether ++ * an error occurred. blk_unref() doesn't tell us about failures. ++ */ ++ inactivate_ret = bdrv_inactivate_all(); ++ if (inactivate_ret < 0) { ++ error_report("Error while closing the image: %s", strerror(-inactivate_ret)); ++ ret = 1; ++ } ++ ++ blk_unref(src); ++ blk_unref(blk); ++ qemu_opts_del(opts); ++ return ret; ++} ++ ++#define C_BS 01 ++#define C_COUNT 02 ++#define C_IF 04 ++#define C_OF 010 ++#define C_SKIP 020 ++ ++struct DdInfo { ++ unsigned int flags; ++ int64_t count; ++}; ++ ++struct DdIo { ++ int bsz; /* Block size */ ++ char *filename; ++ uint8_t *buf; ++ int64_t offset; ++}; ++ ++struct DdOpts { ++ const char *name; ++ int (*f)(const char *, struct DdIo *, struct DdIo *, struct DdInfo *); ++ unsigned int flag; ++}; ++ ++static int img_dd_bs(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ int64_t res; ++ ++ res = cvtnum_full("bs", arg, 1, INT_MAX); ++ ++ if (res < 0) { ++ return 1; ++ } ++ in->bsz = out->bsz = res; ++ ++ return 0; ++} ++ ++static int img_dd_count(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ dd->count = cvtnum("count", arg); ++ ++ if (dd->count < 0) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int img_dd_if(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ in->filename = g_strdup(arg); ++ ++ return 0; ++} ++ ++static int img_dd_of(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ out->filename = g_strdup(arg); ++ ++ return 0; ++} ++ ++static int img_dd_skip(const char *arg, ++ struct DdIo *in, struct DdIo *out, ++ struct DdInfo *dd) ++{ ++ in->offset = cvtnum("skip", arg); ++ ++ if (in->offset < 0) { ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static int img_dd(int argc, char **argv) ++{ ++ int ret = 0; ++ char *arg = NULL; ++ char *tmp; ++ BlockDriver *drv = NULL, *proto_drv = NULL; ++ BlockBackend *blk1 = NULL, *blk2 = NULL; ++ QemuOpts *opts = NULL; ++ QemuOptsList *create_opts = NULL; ++ Error *local_err = NULL; ++ bool image_opts = false; ++ int c, i; ++ const char *out_fmt = "raw"; ++ const char *fmt = NULL; ++ int64_t size = 0; ++ int64_t out_pos, in_pos; ++ bool force_share = false; ++ struct DdInfo dd = { ++ .flags = 0, ++ .count = 0, ++ }; ++ struct DdIo in = { ++ .bsz = 512, /* Block size is by default 512 bytes */ ++ .filename = NULL, ++ .buf = NULL, ++ .offset = 0 ++ }; ++ struct DdIo out = { ++ .bsz = 512, ++ .filename = NULL, ++ .buf = NULL, ++ .offset = 0 ++ }; ++ ++ const struct DdOpts options[] = { ++ { "bs", img_dd_bs, C_BS }, ++ { "count", img_dd_count, C_COUNT }, ++ { "if", img_dd_if, C_IF }, ++ { "of", img_dd_of, C_OF }, ++ { "skip", img_dd_skip, C_SKIP }, ++ { NULL, NULL, 0 } ++ }; ++ const struct option long_options[] = { ++ { "help", no_argument, 0, 'h'}, ++ { "object", required_argument, 0, OPTION_OBJECT}, ++ { "image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ { "force-share", no_argument, 0, 'U'}, ++ { 0, 0, 0, 0 } ++ }; ++ ++ while ((c = getopt_long(argc, argv, ":hf:O:U", long_options, NULL))) { ++ if (c == EOF) { ++ break; ++ } ++ switch (c) { ++ case 'O': ++ out_fmt = optarg; ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case ':': ++ missing_argument(argv[optind - 1]); ++ break; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ break; ++ case 'h': ++ help(); ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ } ++ } ++ ++ for (i = optind; i < argc; i++) { ++ int j; ++ arg = g_strdup(argv[i]); ++ ++ tmp = strchr(arg, '='); ++ if (tmp == NULL) { ++ error_report("unrecognized operand %s", arg); ++ ret = -1; ++ goto out; ++ } ++ ++ *tmp++ = '\0'; ++ ++ for (j = 0; options[j].name != NULL; j++) { ++ if (!strcmp(arg, options[j].name)) { ++ break; ++ } ++ } ++ if (options[j].name == NULL) { ++ error_report("unrecognized operand %s", arg); ++ ret = -1; ++ goto out; ++ } ++ ++ if (options[j].f(tmp, &in, &out, &dd) != 0) { ++ ret = -1; ++ goto out; ++ } ++ dd.flags |= options[j].flag; ++ g_free(arg); ++ arg = NULL; ++ } ++ ++ if (!(dd.flags & C_IF && dd.flags & C_OF)) { ++ error_report("Must specify both input and output files"); ++ ret = -1; ++ goto out; ++ } ++ ++ blk1 = img_open(image_opts, in.filename, fmt, 0, false, false, ++ force_share); ++ ++ if (!blk1) { ++ ret = -1; ++ goto out; ++ } ++ ++ drv = bdrv_find_format(out_fmt); ++ if (!drv) { ++ error_report("Unknown file format"); ++ ret = -1; ++ goto out; ++ } ++ proto_drv = bdrv_find_protocol(out.filename, true, &local_err); ++ ++ if (!proto_drv) { ++ error_report_err(local_err); ++ ret = -1; ++ goto out; ++ } ++ if (!drv->create_opts) { ++ error_report("Format driver '%s' does not support image creation", ++ drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ if (!proto_drv->create_opts) { ++ error_report("Protocol driver '%s' does not support image creation", ++ proto_drv->format_name); ++ ret = -1; ++ goto out; ++ } ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); ++ ++ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); ++ ++ size = blk_getlength(blk1); ++ if (size < 0) { ++ error_report("Failed to get size for '%s'", in.filename); ++ ret = -1; ++ goto out; ++ } ++ ++ if (dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz && ++ dd.count * in.bsz < size) { ++ size = dd.count * in.bsz; ++ } ++ ++ /* Overflow means the specified offset is beyond input image's size */ ++ if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || ++ size < in.bsz * in.offset)) { ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort); ++ } else { ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, ++ size - in.bsz * in.offset, &error_abort); ++ } ++ ++ ret = bdrv_create(drv, out.filename, opts, &local_err); ++ if (ret < 0) { ++ error_reportf_err(local_err, ++ "%s: error while creating output image: ", ++ out.filename); ++ ret = -1; ++ goto out; ++ } ++ ++ /* TODO, we can't honour --image-opts for the target, ++ * since it needs to be given in a format compatible ++ * with the bdrv_create() call above which does not ++ * support image-opts style. ++ */ ++ blk2 = img_open_file(out.filename, NULL, out_fmt, BDRV_O_RDWR, ++ false, false, false); ++ ++ if (!blk2) { ++ ret = -1; ++ goto out; ++ } ++ ++ if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || ++ size < in.offset * in.bsz)) { ++ /* We give a warning if the skip option is bigger than the input ++ * size and create an empty output disk image (i.e. like dd(1)). ++ */ ++ error_report("%s: cannot skip to specified offset", in.filename); ++ in_pos = size; ++ } else { ++ in_pos = in.offset * in.bsz; ++ } ++ ++ in.buf = g_new(uint8_t, in.bsz); ++ ++ for (out_pos = 0; in_pos < size; ) { ++ int bytes = (in_pos + in.bsz > size) ? size - in_pos : in.bsz; ++ ++ ret = blk_pread(blk1, in_pos, bytes, in.buf, 0); ++ if (ret < 0) { ++ error_report("error while reading from input image file: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ in_pos += bytes; ++ ++ ret = blk_pwrite(blk2, out_pos, bytes, in.buf, 0); ++ if (ret < 0) { ++ error_report("error while writing to output image file: %s", ++ strerror(-ret)); ++ goto out; ++ } ++ out_pos += bytes; ++ } ++ ++out: ++ g_free(arg); ++ qemu_opts_del(opts); ++ qemu_opts_free(create_opts); ++ blk_unref(blk1); ++ blk_unref(blk2); ++ g_free(in.filename); ++ g_free(out.filename); ++ g_free(in.buf); ++ g_free(out.buf); ++ ++ if (ret) { ++ return 1; ++ } ++ return 0; ++} ++ ++static void dump_json_block_measure_info(BlockMeasureInfo *info) ++{ ++ GString *str; ++ QObject *obj; ++ Visitor *v = qobject_output_visitor_new(&obj); ++ ++ visit_type_BlockMeasureInfo(v, NULL, &info, &error_abort); ++ visit_complete(v, &obj); ++ str = qobject_to_json_pretty(obj, true); ++ assert(str != NULL); ++ printf("%s\n", str->str); ++ qobject_unref(obj); ++ visit_free(v); ++ g_string_free(str, true); ++} ++ ++static int img_measure(int argc, char **argv) ++{ ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, ++ {"object", required_argument, 0, OPTION_OBJECT}, ++ {"output", required_argument, 0, OPTION_OUTPUT}, ++ {"size", required_argument, 0, OPTION_SIZE}, ++ {"force-share", no_argument, 0, 'U'}, ++ {0, 0, 0, 0} ++ }; ++ OutputFormat output_format = OFORMAT_HUMAN; ++ BlockBackend *in_blk = NULL; ++ BlockDriver *drv; ++ const char *filename = NULL; ++ const char *fmt = NULL; ++ const char *out_fmt = "raw"; ++ char *options = NULL; ++ char *snapshot_name = NULL; ++ bool force_share = false; ++ QemuOpts *opts = NULL; ++ QemuOpts *object_opts = NULL; ++ QemuOpts *sn_opts = NULL; ++ QemuOptsList *create_opts = NULL; ++ bool image_opts = false; ++ uint64_t img_size = UINT64_MAX; ++ BlockMeasureInfo *info = NULL; ++ Error *local_err = NULL; ++ int ret = 1; ++ int c; ++ ++ while ((c = getopt_long(argc, argv, "hf:O:o:l:U", ++ long_options, NULL)) != -1) { ++ switch (c) { ++ case '?': ++ case 'h': ++ help(); ++ break; ++ case 'f': ++ fmt = optarg; ++ break; ++ case 'O': ++ out_fmt = optarg; ++ break; ++ case 'o': ++ if (accumulate_options(&options, optarg) < 0) { ++ goto out; ++ } ++ break; ++ case 'l': ++ if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { ++ sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, ++ optarg, false); ++ if (!sn_opts) { ++ error_report("Failed in parsing snapshot param '%s'", ++ optarg); ++ goto out; ++ } ++ } else { ++ snapshot_name = optarg; ++ } ++ break; ++ case 'U': ++ force_share = true; ++ break; ++ case OPTION_OBJECT: ++ user_creatable_process_cmdline(optarg); ++ break; ++ case OPTION_IMAGE_OPTS: ++ image_opts = true; ++ break; ++ case OPTION_OUTPUT: ++ if (!strcmp(optarg, "json")) { ++ output_format = OFORMAT_JSON; ++ } else if (!strcmp(optarg, "human")) { ++ output_format = OFORMAT_HUMAN; ++ } else { ++ error_report("--output must be used with human or json " ++ "as argument."); ++ goto out; ++ } ++ break; ++ case OPTION_SIZE: ++ { ++ int64_t sval; ++ ++ sval = cvtnum("image size", optarg); ++ if (sval < 0) { ++ goto out; ++ } ++ img_size = (uint64_t)sval; ++ } ++ break; ++ } ++ } ++ ++ if (argc - optind > 1) { ++ error_report("At most one filename argument is allowed."); ++ goto out; ++ } else if (argc - optind == 1) { ++ filename = argv[optind]; ++ } ++ ++ if (!filename && (image_opts || fmt || snapshot_name || sn_opts)) { ++ error_report("--image-opts, -f, and -l require a filename argument."); ++ goto out; ++ } ++ if (filename && img_size != UINT64_MAX) { ++ error_report("--size N cannot be used together with a filename."); ++ goto out; ++ } ++ if (!filename && img_size == UINT64_MAX) { ++ error_report("Either --size N or one filename must be specified."); ++ goto out; ++ } ++ ++ if (filename) { ++ in_blk = img_open(image_opts, filename, fmt, 0, ++ false, false, force_share); ++ if (!in_blk) { ++ goto out; ++ } ++ ++ if (sn_opts) { ++ bdrv_snapshot_load_tmp(blk_bs(in_blk), ++ qemu_opt_get(sn_opts, SNAPSHOT_OPT_ID), ++ qemu_opt_get(sn_opts, SNAPSHOT_OPT_NAME), ++ &local_err); ++ } else if (snapshot_name != NULL) { ++ bdrv_snapshot_load_tmp_by_id_or_name(blk_bs(in_blk), ++ snapshot_name, &local_err); ++ } ++ if (local_err) { ++ error_reportf_err(local_err, "Failed to load snapshot: "); ++ goto out; ++ } ++ } ++ ++ drv = bdrv_find_format(out_fmt); ++ if (!drv) { ++ error_report("Unknown file format '%s'", out_fmt); ++ goto out; ++ } ++ if (!drv->create_opts) { ++ error_report("Format driver '%s' does not support image creation", ++ drv->format_name); ++ goto out; ++ } ++ ++ create_opts = qemu_opts_append(create_opts, drv->create_opts); ++ create_opts = qemu_opts_append(create_opts, bdrv_file.create_opts); ++ opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); ++ if (options) { ++ if (!qemu_opts_do_parse(opts, options, NULL, &local_err)) { ++ error_report_err(local_err); ++ error_report("Invalid options for file format '%s'", out_fmt); ++ goto out; ++ } ++ } ++ if (img_size != UINT64_MAX) { ++ qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); ++ } ++ ++ info = bdrv_measure(drv, opts, in_blk ? blk_bs(in_blk) : NULL, &local_err); ++ if (local_err) { ++ error_report_err(local_err); ++ goto out; ++ } ++ ++ if (output_format == OFORMAT_HUMAN) { ++ printf("required size: %" PRIu64 "\n", info->required); ++ printf("fully allocated size: %" PRIu64 "\n", info->fully_allocated); ++ if (info->has_bitmaps) { ++ printf("bitmaps size: %" PRIu64 "\n", info->bitmaps); ++ } ++ } else { ++ dump_json_block_measure_info(info); ++ } ++ ++ ret = 0; ++ ++out: ++ qapi_free_BlockMeasureInfo(info); ++ qemu_opts_del(object_opts); ++ qemu_opts_del(opts); ++ qemu_opts_del(sn_opts); ++ qemu_opts_free(create_opts); ++ g_free(options); ++ blk_unref(in_blk); ++ return ret; ++} ++ ++static const img_cmd_t img_cmds[] = { ++#define DEF(option, callback, arg_string) \ ++ { option, callback }, ++#include "qemu-img-cmds.h" ++#undef DEF ++ { NULL, NULL, }, ++}; ++ ++int main(int argc, char **argv) ++{ ++ const img_cmd_t *cmd; ++ const char *cmdname; ++ int c; ++ static const struct option long_options[] = { ++ {"help", no_argument, 0, 'h'}, ++ {"version", no_argument, 0, 'V'}, ++ {"trace", required_argument, NULL, 'T'}, ++ {0, 0, 0, 0} ++ }; ++ ++#ifdef CONFIG_POSIX ++ signal(SIGPIPE, SIG_IGN); ++#endif ++ ++ socket_init(); ++ error_init(argv[0]); ++ module_call_init(MODULE_INIT_TRACE); ++ qemu_init_exec_dir(argv[0]); ++ ++ qemu_init_main_loop(&error_fatal); ++ ++ qcrypto_init(&error_fatal); ++ ++ module_call_init(MODULE_INIT_QOM); ++ bdrv_init(); ++ if (argc < 2) { ++ error_exit("Not enough arguments"); ++ } ++ ++ qemu_add_opts(&qemu_source_opts); ++ qemu_add_opts(&qemu_trace_opts); ++ ++ while ((c = getopt_long(argc, argv, "+:hVT:", long_options, NULL)) != -1) { ++ switch (c) { ++ case ':': ++ missing_argument(argv[optind - 1]); ++ return 0; ++ case '?': ++ unrecognized_option(argv[optind - 1]); ++ return 0; ++ case 'h': ++ help(); ++ return 0; ++ case 'V': ++ printf(QEMU_IMG_VERSION); ++ return 0; ++ case 'T': ++ trace_opt_parse(optarg); ++ break; ++ } ++ } ++ ++ cmdname = argv[optind]; ++ ++ /* reset getopt_long scanning */ ++ argc -= optind; ++ if (argc < 1) { ++ return 0; ++ } ++ argv += optind; ++ qemu_reset_optind(); ++ ++ if (!trace_init_backends()) { ++ exit(1); ++ } ++ trace_init_file(); ++ qemu_set_log(LOG_TRACE, &error_fatal); ++ ++ /* find the command */ ++ for (cmd = img_cmds; cmd->name != NULL; cmd++) { ++ if (!strcmp(cmdname, cmd->name)) { ++ return cmd->handler(argc, argv); ++ } ++ } ++ ++ /* not found */ ++ error_exit("Command not found: %s", cmdname); ++} +diff --git a/qcow2/qemu-progress.c b/qcow2/qemu-progress.c +new file mode 100644 +index 00000000..aa994668 +--- /dev/null ++++ b/qcow2/qemu-progress.c +@@ -0,0 +1,162 @@ ++/* ++ * QEMU progress printing utility functions ++ * ++ * Copyright (C) 2011 Jes Sorensen ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "qemu/osdep.h" ++#include "qemu/qemu-progress.h" ++ ++struct progress_state { ++ float current; ++ float last_print; ++ float min_skip; ++ void (*print)(void); ++ void (*end)(void); ++}; ++ ++static struct progress_state state; ++static volatile sig_atomic_t print_pending; ++ ++/* ++ * Simple progress print function. ++ * @percent relative percent of current operation ++ * @max percent of total operation ++ */ ++static void progress_simple_print(void) ++{ ++ printf(" (%3.2f/100%%)\r", state.current); ++ fflush(stdout); ++} ++ ++static void progress_simple_end(void) ++{ ++ printf("\n"); ++} ++ ++static void progress_simple_init(void) ++{ ++ state.print = progress_simple_print; ++ state.end = progress_simple_end; ++} ++ ++#ifdef CONFIG_POSIX ++static void sigusr_print(int signal) ++{ ++ print_pending = 1; ++} ++#endif ++ ++static void progress_dummy_print(void) ++{ ++ if (print_pending) { ++ fprintf(stderr, " (%3.2f/100%%)\n", state.current); ++ print_pending = 0; ++ } ++} ++ ++static void progress_dummy_end(void) ++{ ++} ++ ++static void progress_dummy_init(void) ++{ ++#ifdef CONFIG_POSIX ++ struct sigaction action; ++ sigset_t set; ++ ++ memset(&action, 0, sizeof(action)); ++ sigfillset(&action.sa_mask); ++ action.sa_handler = sigusr_print; ++ action.sa_flags = 0; ++ sigaction(SIGUSR1, &action, NULL); ++#ifdef SIGINFO ++ sigaction(SIGINFO, &action, NULL); ++#endif ++ ++ /* ++ * SIGUSR1 is SIG_IPI and gets blocked in qemu_init_main_loop(). In the ++ * tools that use the progress report SIGUSR1 isn't used in this meaning ++ * and instead should print the progress, so reenable it. ++ */ ++ sigemptyset(&set); ++ sigaddset(&set, SIGUSR1); ++ pthread_sigmask(SIG_UNBLOCK, &set, NULL); ++#endif ++ ++ state.print = progress_dummy_print; ++ state.end = progress_dummy_end; ++} ++ ++/* ++ * Initialize progress reporting. ++ * If @enabled is false, actual reporting is suppressed. The user can ++ * still trigger a report by sending a SIGUSR1. ++ * Reports are also suppressed unless we've had at least @min_skip ++ * percent progress since the last report. ++ */ ++void qemu_progress_init(int enabled, float min_skip) ++{ ++ state.min_skip = min_skip; ++ if (enabled) { ++ progress_simple_init(); ++ } else { ++ progress_dummy_init(); ++ } ++} ++ ++void qemu_progress_end(void) ++{ ++ state.end(); ++} ++ ++/* ++ * Report progress. ++ * @delta is how much progress we made. ++ * If @max is zero, @delta is an absolute value of the total job done. ++ * Else, @delta is a progress delta since the last call, as a fraction ++ * of @max. I.e. the delta is @delta * @max / 100. This allows ++ * relative accounting of functions which may be a different fraction of ++ * the full job, depending on the context they are called in. I.e. ++ * a function might be considered 40% of the full job if used from ++ * bdrv_img_create() but only 20% if called from img_convert(). ++ */ ++void qemu_progress_print(float delta, int max) ++{ ++ float current; ++ ++ if (max == 0) { ++ current = delta; ++ } else { ++ current = state.current + delta / 100 * max; ++ } ++ if (current > 100) { ++ current = 100; ++ } ++ state.current = current; ++ ++ if (current > (state.last_print + state.min_skip) || ++ current < (state.last_print - state.min_skip) || ++ current == 100 || current == 0) { ++ state.last_print = state.current; ++ state.print(); ++ } ++} diff --git a/SOURCES/0026-libqcow2-build-qcow2-library-for-tapdisk.patch b/SOURCES/0026-libqcow2-build-qcow2-library-for-tapdisk.patch new file mode 100644 index 0000000..e0d289e --- /dev/null +++ b/SOURCES/0026-libqcow2-build-qcow2-library-for-tapdisk.patch @@ -0,0 +1,9391 @@ +From f1c5e6517749dcf6114595b3c71faf0bd6e48aef Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:28 +0100 +Subject: [PATCH] libqcow2: build qcow2 library for tapdisk + +This commit modify sources from qemu to disable some features +unneeded for tapdisk. + +Signed-off-by: Anthoine Bourgeois +--- + .gitignore | 4 + + Makefile.am | 1 + + configure.ac | 8 + + include/block/aio.h | 9 +- + include/block/block-common.h | 4 +- + include/block/block-global-state.h | 2 + + include/block/block_int-common.h | 2 +- + include/block/block_int-global-state.h | 12 + + include/compiler.h | 2 + + include/crypto/hash.h | 20 +- + include/hw/block/block.h | 2 +- + include/hw/qdev-core.h | 18 +- + include/list.h | 2 + + include/qapi/qapi-commands-block-core.h | 6 +- + include/qapi/qapi-commands-block.h | 2 +- + include/qapi/qapi-events-block-core.h | 4 +- + include/qapi/qapi-types-block-core.h | 76 ++- + include/qapi/qapi-visit-block-core.h | 6 +- + include/qapi/qmp/dispatch.h | 2 +- + include/qcow2.h | 22 +- + include/qemu/atomic.h | 2 +- + include/qemu/bitops.h | 9 +- + include/qemu/config-host.h | 495 ++++++++++++++++++++ + include/qemu/hbitmap.h | 4 +- + include/qemu/host-utils.h | 6 +- + include/qemu/main-loop.h | 12 +- + include/qemu/option_int.h | 2 +- + include/qemu/osdep.h | 2 +- + include/qemu/range.h | 2 + + include/qemu/timer.h | 6 +- + include/sysemu/block-backend-common.h | 4 +- + include/sysemu/block-backend-global-state.h | 2 +- + include/sysemu/block-backend-io.h | 4 + + qcow2/Makefile.am | 21 + + qcow2/lib/Makefile.am | 166 +++++++ + qcow2/lib/authz/base.c | 4 +- + qcow2/lib/block.c | 28 +- + qcow2/lib/block/accounting.c | 3 +- + qcow2/lib/block/block-backend.c | 108 +++-- + qcow2/lib/block/block-gen.c | 2 + + qcow2/lib/block/commit.c | 6 +- + qcow2/lib/block/dirty-bitmap.c | 2 +- + qcow2/lib/block/file-posix.c | 46 +- + qcow2/lib/block/io.c | 60 ++- + qcow2/lib/block/mirror.c | 30 +- + qcow2/lib/block/nbd.c | 31 +- + qcow2/lib/block/qapi.c | 6 +- + qcow2/lib/block/raw-format.c | 4 + + qcow2/lib/blockdev.c | 26 +- + qcow2/lib/blockjob.c | 6 +- + qcow2/lib/crypto/hash.c | 96 +++- + qcow2/lib/crypto/tlscreds.c | 10 +- + qcow2/lib/crypto/tlssession.c | 16 +- + qcow2/lib/hw/block/block.c | 8 +- + qcow2/lib/hw/block/hd-geometry.c | 6 +- + qcow2/lib/io/channel-file.c | 6 +- + qcow2/lib/io/channel-socket.c | 36 +- + qcow2/lib/io/channel-tls.c | 20 +- + qcow2/lib/io/task.c | 18 +- + qcow2/lib/job-qmp.c | 14 +- + qcow2/lib/job.c | 14 +- + qcow2/lib/nbd/client-connection.c | 4 +- + qcow2/lib/nbd/client.c | 83 ++-- + qcow2/lib/nbd/common.c | 4 +- + qcow2/lib/qapi/qapi-events-block-core.c | 24 +- + qcow2/lib/qapi/qapi-events-job.c | 6 +- + qcow2/lib/qapi/qapi-types-block-core.c | 38 +- + qcow2/lib/qapi/qapi-types-common.c | 4 +- + qcow2/lib/qapi/qapi-types-crypto.c | 4 +- + qcow2/lib/qapi/qapi-types-job.c | 4 +- + qcow2/lib/qapi/qapi-types-sockets.c | 4 +- + qcow2/lib/qapi/qapi-types-yank.c | 4 +- + qcow2/lib/qapi/qapi-visit-block-core.c | 80 +++- + qcow2/lib/qapi/qapi-visit-common.c | 2 +- + qcow2/lib/qapi/qapi-visit-core.c | 62 +-- + qcow2/lib/qapi/qapi-visit-crypto.c | 2 +- + qcow2/lib/qapi/qapi-visit-job.c | 2 +- + qcow2/lib/qapi/qapi-visit-sockets.c | 2 +- + qcow2/lib/qapi/qapi-visit-yank.c | 2 +- + qcow2/lib/qapi/qmp-dispatch.c | 2 + + qcow2/lib/qapi/qobject-input-visitor.c | 4 +- + qcow2/lib/qcow2-cache.c | 26 +- + qcow2/lib/qcow2-cluster.c | 34 +- + qcow2/lib/qcow2-refcount.c | 7 +- + qcow2/lib/qcow2-threads.c | 4 +- + qcow2/lib/qcow2.c | 186 +++++--- + qcow2/lib/qobject/block-qdict.c | 2 + + qcow2/lib/qom/object.c | 38 +- + qcow2/lib/system/cpus.c | 44 +- + qcow2/lib/util/aio-posix.c | 20 +- + qcow2/lib/util/async.c | 8 +- + qcow2/lib/util/bufferiszero.c | 9 +- + qcow2/lib/util/cutils.c | 6 +- + qcow2/lib/util/error-report.c | 19 +- + qcow2/lib/util/error.c | 6 +- + qcow2/lib/util/hbitmap.c | 12 +- + qcow2/lib/util/host-utils.c | 2 + + qcow2/lib/util/iov.c | 6 +- + qcow2/lib/util/lockcnt.c | 22 +- + qcow2/lib/util/main-loop.c | 18 +- + qcow2/lib/util/memalign.c | 9 +- + qcow2/lib/util/module.c | 6 +- + qcow2/lib/util/osdep.c | 22 +- + qcow2/lib/util/oslib-posix.c | 34 +- + qcow2/lib/util/qemu-coroutine-lock.c | 12 +- + qcow2/lib/util/qemu-coroutine.c | 8 +- + qcow2/lib/util/qemu-option.c | 12 +- + qcow2/lib/util/qemu-print.c | 6 +- + qcow2/lib/util/qemu-sockets.c | 18 +- + qcow2/lib/util/qemu-thread-common.h | 8 +- + qcow2/lib/util/qemu-thread-posix.c | 8 +- + qcow2/lib/util/qemu-timer.c | 25 +- + qcow2/lib/util/qsp.c | 10 +- + qcow2/lib/util/thread-pool.c | 10 +- + qcow2/lib/util/yank.c | 2 +- + qcow2/qemu-img.c | 76 +-- + 116 files changed, 1991 insertions(+), 628 deletions(-) + create mode 100644 include/qemu/config-host.h + create mode 100644 qcow2/Makefile.am + create mode 100644 qcow2/lib/Makefile.am + +diff --git a/.gitignore b/.gitignore +index eb160772..c673495b 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -12,6 +12,7 @@ + *.trs + .deps + .libs ++.dirstamp + cscope.* + TAGS + VERSION +@@ -51,6 +52,7 @@ Makefile.in + /vhd/vhd-util + /vhd/lib/test/random-copy + /vhd/lib/test/test-snapshot ++/qcow2/qemu-img + /tapback/tapback + /test-driver + +@@ -73,6 +75,8 @@ Makefile.in + /vhd/Makefile + /vhd/lib/Makefile + /vhd/lib/test/Makefile ++/qcow2/Makefile ++/qcow2/lib/Makefile + /tapback/Makefile + mk/config.log + +diff --git a/Makefile.am b/Makefile.am +index 7133a020..78c56ba3 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -5,6 +5,7 @@ + + SUBDIRS = lvm + SUBDIRS += vhd ++SUBDIRS += qcow2 + SUBDIRS += cpumond + SUBDIRS += control + SUBDIRS += drivers +diff --git a/configure.ac b/configure.ac +index a419f8ae..43fc21a6 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -29,6 +29,12 @@ AC_CHECK_LIB([uuid], [main], + [:], + AC_MSG_ERROR([Need uuid-dev])) + ++PKG_PROG_PKG_CONFIG ++PKG_CHECK_MODULES([GLIB], [glib-2.0]) ++PKG_CHECK_MODULES([ZLIB], [zlib]) ++PKG_CHECK_MODULES([GNUTLS], [gnutls]) ++PKG_CHECK_MODULES([LIBZSTD], [libzstd]) ++ + AS_IF([test x$with_libiconv != xno], + [AC_CHECK_LIB([iconv], [main], + [AC_SUBST([LIBICONV], ["-liconv"])], +@@ -64,6 +70,8 @@ cbt/Makefile + vhd/Makefile + vhd/lib/Makefile + vhd/lib/test/Makefile ++qcow2/Makefile ++qcow2/lib/Makefile + control/Makefile + drivers/Makefile + drivers/crypto/Makefile +diff --git a/include/block/aio.h b/include/block/aio.h +index 4ee81936..3776e8bf 100644 +--- a/include/block/aio.h ++++ b/include/block/aio.h +@@ -23,11 +23,15 @@ + #include "qemu/thread.h" + #include "qemu/timer.h" + #include "block/graph-lock.h" +-#include "hw/qdev-core.h" ++//#include "hw/qdev-core.h" + ++typedef struct { ++ bool engaged_in_io; ++} MemReentrancyGuard; + + typedef struct BlockAIOCB BlockAIOCB; + typedef void BlockCompletionFunc(void *opaque, int ret); ++typedef struct AioContext AioContext; + + typedef struct AIOCBInfo { + void (*cancel_async)(BlockAIOCB *acb); +@@ -294,6 +298,7 @@ void aio_bh_schedule_oneshot_full(AioContext *ctx, QEMUBHFunc *cb, void *opaque, + * A convenience wrapper for aio_bh_schedule_oneshot_full() that uses cb as the + * name string. + */ ++#define replay_bh_schedule_oneshot_event(ctx, bh, acb) aio_bh_schedule_oneshot(ctx, bh, acb) + #define aio_bh_schedule_oneshot(ctx, cb, opaque) \ + aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb))) + +@@ -691,6 +696,7 @@ void aio_context_destroy(AioContext *ctx); + /* Used internally, do not call outside AioContext code */ + void aio_context_use_g_source(AioContext *ctx); + ++#if 0 + /** + * aio_context_set_poll_params: + * @ctx: the aio context +@@ -721,3 +727,4 @@ void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch); + void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, + int64_t max, Error **errp); + #endif ++#endif +diff --git a/include/block/block-common.h b/include/block/block-common.h +index 338fe5ff..b07b07e2 100644 +--- a/include/block/block-common.h ++++ b/include/block/block-common.h +@@ -125,6 +125,7 @@ typedef struct BlockZoneDescriptor { + BlockZoneState state; + } BlockZoneDescriptor; + ++#if 0 + /* + * Track write pointers of a zone in bytes. + */ +@@ -132,6 +133,7 @@ typedef struct BlockZoneWps { + CoMutex colock; + uint64_t wp[]; + } BlockZoneWps; ++#endif + + typedef struct BlockDriverInfo { + /* in bytes, 0 if irrelevant */ +@@ -546,7 +548,7 @@ typedef struct HDGeometry { + */ + + char *bdrv_perm_names(uint64_t perm); +-uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); ++//uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); + + void bdrv_init_with_whitelist(void); + bool bdrv_uses_whitelist(void); +diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h +index bd7cecd1..2e54cf5e 100644 +--- a/include/block/block-global-state.h ++++ b/include/block/block-global-state.h +@@ -196,8 +196,10 @@ void bdrv_aio_cancel(BlockAIOCB *acb); + int bdrv_has_zero_init_1(BlockDriverState *bs); + int coroutine_mixed_fn GRAPH_RDLOCK bdrv_has_zero_init(BlockDriverState *bs); + BlockDriverState *bdrv_find_node(const char *node_name); ++#if 0 + BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); + XDbgBlockGraph * GRAPH_RDLOCK bdrv_get_xdbg_block_graph(Error **errp); ++#endif + BlockDriverState *bdrv_lookup_bs(const char *device, + const char *node_name, + Error **errp); +diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h +index ebb4e56a..c6d8b950 100644 +--- a/include/block/block_int-common.h ++++ b/include/block/block_int-common.h +@@ -1253,7 +1253,7 @@ struct BlockDriverState { + BdrvBlockStatusCache *block_status_cache; + + /* array of write pointers' location of each zone in the zoned device. */ +- BlockZoneWps *wps; ++ //BlockZoneWps *wps; + }; + + struct BlockBackendRootState { +diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h +index eb2d92a2..2d91fb96 100644 +--- a/include/block/block_int-global-state.h ++++ b/include/block/block_int-global-state.h +@@ -37,6 +37,7 @@ + * the GS API. + */ + ++#if 0 + /** + * stream_start: + * @job_id: The id of the newly-created job, or %NULL to use the +@@ -72,6 +73,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, + BlockdevOnError on_error, + const char *filter_node_name, + Error **errp); ++#endif + + /** + * commit_start: +@@ -124,6 +126,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, + const char *filter_node_name, + BlockCompletionFunc *cb, void *opaque, + bool auto_complete, Error **errp); ++#if 0 + /* + * mirror_start: + * @job_id: The id of the newly-created job, or %NULL to use the +@@ -201,6 +204,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, + int creation_flags, + BlockCompletionFunc *cb, void *opaque, + JobTxn *txn, Error **errp); ++#endif + + BdrvChild * GRAPH_WRLOCK + bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, +@@ -247,18 +251,24 @@ void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared); + ++#if 0 + void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); ++#endif + bool blk_dev_has_removable_media(BlockBackend *blk); ++#if 0 + void blk_dev_eject_request(BlockBackend *blk, bool force); ++#endif + bool blk_dev_is_medium_locked(BlockBackend *blk); + + void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup); + ++#if 0 + void bdrv_set_monitor_owned(BlockDriverState *bs); + + void blockdev_close_all_bdrv_states(void); + + BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp); ++#endif + + /** + * Simple implementation of bdrv_co_create_opts for protocol drivers +@@ -270,6 +280,7 @@ int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, + QemuOpts *opts, + Error **errp); + ++#if 0 + BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, + const char *name, + BlockDriverState **pbs, +@@ -281,6 +292,7 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, + bool release, + BlockDriverState **bitmap_bs, + Error **errp); ++#endif + + + BlockDriverState * GRAPH_RDLOCK +diff --git a/include/compiler.h b/include/compiler.h +index 57ab2367..9ab73c76 100644 +--- a/include/compiler.h ++++ b/include/compiler.h +@@ -32,9 +32,11 @@ + #define _BLKTAP_COMPILER_H + + #ifdef __GNUC__ ++#ifndef likely + #define likely(_cond) __builtin_expect(!!(_cond), 1) + #define unlikely(_cond) __builtin_expect(!!(_cond), 0) + #endif ++#endif + + #ifndef likely + #define likely(_cond) (_cond) +diff --git a/include/crypto/hash.h b/include/crypto/hash.h +index 54d87aa2..1178ad29 100644 +--- a/include/crypto/hash.h ++++ b/include/crypto/hash.h +@@ -21,9 +21,27 @@ + #ifndef QCRYPTO_HASH_H + #define QCRYPTO_HASH_H + +-#include "qapi/qapi-types-crypto.h" ++//#include "qapi/qapi-types-crypto.h" + + /* See also "QCryptoHashAlgorithm" defined in qapi/crypto.json */ ++typedef enum QCryptoHashAlgorithm { ++ QCRYPTO_HASH_ALG_MD5 = 0, ++ QCRYPTO_HASH_ALG_SHA1 = 1, ++ QCRYPTO_HASH_ALG_SHA224 = 2, ++ QCRYPTO_HASH_ALG_SHA256 = 3, ++ QCRYPTO_HASH_ALG_SHA384 = 4, ++ QCRYPTO_HASH_ALG_SHA512 = 5, ++ QCRYPTO_HASH_ALG_RIPEMD160 = 6, ++ QCRYPTO_HASH_ALG__MAX = 7, ++} QCryptoHashAlgorithm; ++ ++#define QCRYPTO_HASH_DIGEST_LEN_MD5 16 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA1 20 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA224 28 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA256 32 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA384 48 ++#define QCRYPTO_HASH_DIGEST_LEN_SHA512 64 ++#define QCRYPTO_HASH_DIGEST_LEN_RIPEMD160 20 + + /** + * qcrypto_hash_supports: +diff --git a/include/hw/block/block.h b/include/hw/block/block.h +index de3946a5..7d533173 100644 +--- a/include/hw/block/block.h ++++ b/include/hw/block/block.h +@@ -13,7 +13,7 @@ + + #include "exec/hwaddr.h" + #include "qapi/qapi-types-block-core.h" +-#include "hw/qdev-properties-system.h" ++//#include "hw/qdev-properties-system.h" + + /* Configuration */ + +diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h +index 77bfcbdf..f54e6ade 100644 +--- a/include/hw/qdev-core.h ++++ b/include/hw/qdev-core.h +@@ -7,8 +7,8 @@ + #include "qemu/rcu.h" + #include "qemu/rcu_queue.h" + #include "qom/object.h" +-#include "hw/hotplug.h" +-#include "hw/resettable.h" ++//#include "hw/hotplug.h" ++//#include "hw/resettable.h" + + /** + * DOC: The QEMU Device API +@@ -278,10 +278,12 @@ struct DeviceState { + * needed for migration + */ + int alias_required_for_version; ++#if 0 + /** + * @reset: ResettableState for the device; handled by Resettable interface. + */ + ResettableState reset; ++#endif + /** + * @unplug_blockers: list of reasons to block unplugging of device + */ +@@ -376,7 +378,7 @@ struct BusState { + /* public: */ + DeviceState *parent; + char *name; +- HotplugHandler *hotplug_handler; ++ //HotplugHandler *hotplug_handler; + int max_index; + bool realized; + bool full; +@@ -394,7 +396,7 @@ struct BusState { + /** + * @reset: ResettableState for the bus; handled by Resettable interface. + */ +- ResettableState reset; ++ //ResettableState reset; + }; + + /** +@@ -531,8 +533,8 @@ bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); + void qdev_unrealize(DeviceState *dev); + void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, + int required_for_version); +-HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); +-HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); ++//HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); ++//HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); + bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); + + /** +@@ -545,10 +547,12 @@ bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); + * Return: pointer to object that implements TYPE_HOTPLUG_HANDLER interface + * or NULL if there aren't any. + */ ++#if 0 + HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); + void qdev_unplug(DeviceState *dev, Error **errp); + void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); ++#endif + void qdev_machine_creation_done(void); + bool qdev_machine_modified(void); + +@@ -1014,6 +1018,7 @@ extern bool qdev_hot_removed; + + char *qdev_get_dev_path(DeviceState *dev); + ++#if 0 + void qbus_set_hotplug_handler(BusState *bus, Object *handler); + void qbus_set_bus_hotplug_handler(BusState *bus); + +@@ -1032,6 +1037,7 @@ static inline bool qbus_is_hotpluggable(BusState *bus) + } + return ret; + } ++#endif + + /** + * qbus_mark_full: Mark this bus as full, so no more devices can be attached +diff --git a/include/list.h b/include/list.h +index 01923d91..874e88a3 100644 +--- a/include/list.h ++++ b/include/list.h +@@ -211,11 +211,13 @@ list_del_init(struct list_head *node) + #define container_off(containing_type, member) \ + offsetof(containing_type, member) + ++#ifndef container_of + #define container_of(member_ptr, containing_type, member) \ + ((containing_type *) \ + ((char *)(member_ptr) \ + - container_off(containing_type, member)) \ + + check_types_match(*(member_ptr), ((containing_type *)0)->member)) ++#endif + + #define list_entry(PTR, TYPE, FIELD) container_of(PTR, TYPE, FIELD) + #define list_first_entry(PTR, TYPE, FIELD) \ +diff --git a/include/qapi/qapi-commands-block-core.h b/include/qapi/qapi-commands-block-core.h +index e698e23c..84c2dfa9 100644 +--- a/include/qapi/qapi-commands-block-core.h ++++ b/include/qapi/qapi-commands-block-core.h +@@ -14,9 +14,9 @@ + #define QAPI_COMMANDS_BLOCK_CORE_H + + #include "qapi-commands-common.h" +-#include "qapi-commands-crypto.h" +-#include "qapi-commands-job.h" +-#include "qapi-commands-sockets.h" ++//#include "qapi-commands-crypto.h" ++//#include "qapi-commands-job.h" ++//#include "qapi-commands-sockets.h" + #include "qapi-types-block-core.h" + + BlockInfoList *qmp_query_block(Error **errp); +diff --git a/include/qapi/qapi-commands-block.h b/include/qapi/qapi-commands-block.h +index 2c1adc60..4f072d47 100644 +--- a/include/qapi/qapi-commands-block.h ++++ b/include/qapi/qapi-commands-block.h +@@ -13,7 +13,7 @@ + #ifndef QAPI_COMMANDS_BLOCK_H + #define QAPI_COMMANDS_BLOCK_H + +-#include "qapi-commands-block-core.h" ++#include "qapi/qapi-commands-block-core.h" + #include "qapi-types-block.h" + + PRManagerInfoList *qmp_query_pr_managers(Error **errp); +diff --git a/include/qapi/qapi-events-block-core.h b/include/qapi/qapi-events-block-core.h +index 7f12beff..e9830b25 100644 +--- a/include/qapi/qapi-events-block-core.h ++++ b/include/qapi/qapi-events-block-core.h +@@ -14,9 +14,9 @@ + #define QAPI_EVENTS_BLOCK_CORE_H + + #include "qapi-events-common.h" +-#include "qapi-events-crypto.h" ++//#include "qapi-events-crypto.h" + #include "qapi-events-job.h" +-#include "qapi-events-sockets.h" ++//#include "qapi-events-sockets.h" + #include "qapi/util.h" + #include "qapi-types-block-core.h" + +diff --git a/include/qapi/qapi-types-block-core.h b/include/qapi/qapi-types-block-core.h +index 26585893..a2c2f2e2 100644 +--- a/include/qapi/qapi-types-block-core.h ++++ b/include/qapi/qapi-types-block-core.h +@@ -14,10 +14,10 @@ + #define QAPI_TYPES_BLOCK_CORE_H + + #include "qapi/qapi-builtin-types.h" +-#include "qapi-types-common.h" +-#include "qapi-types-crypto.h" ++#include "qapi/qapi-types-common.h" ++//#include "qapi-types-crypto.h" + #include "qapi-types-job.h" +-#include "qapi-types-sockets.h" ++//#include "qapi-types-sockets.h" + + typedef struct SnapshotInfo SnapshotInfo; + +@@ -41,9 +41,11 @@ typedef struct ImageInfoSpecificFile ImageInfoSpecificFile; + + typedef enum ImageInfoSpecificKind { + IMAGE_INFO_SPECIFIC_KIND_QCOW2, ++#if 0 + IMAGE_INFO_SPECIFIC_KIND_VMDK, + IMAGE_INFO_SPECIFIC_KIND_LUKS, + IMAGE_INFO_SPECIFIC_KIND_RBD, ++#endif + IMAGE_INFO_SPECIFIC_KIND_FILE, + IMAGE_INFO_SPECIFIC_KIND__MAX, + } ImageInfoSpecificKind; +@@ -1007,6 +1009,7 @@ struct SnapshotInfo { + void qapi_free_SnapshotInfo(SnapshotInfo *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(SnapshotInfo, qapi_free_SnapshotInfo) + ++#if 0 + struct ImageInfoSpecificQCow2EncryptionBase { + BlockdevQcow2EncryptionFormat format; + }; +@@ -1030,6 +1033,7 @@ static inline ImageInfoSpecificQCow2EncryptionBase *qapi_ImageInfoSpecificQCow2E + + void qapi_free_ImageInfoSpecificQCow2Encryption(ImageInfoSpecificQCow2Encryption *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Encryption, qapi_free_ImageInfoSpecificQCow2Encryption) ++#endif + + struct Qcow2BitmapInfoList { + Qcow2BitmapInfoList *next; +@@ -1114,6 +1118,7 @@ struct ImageInfoSpecificQCow2Wrapper { + void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Wrapper, qapi_free_ImageInfoSpecificQCow2Wrapper) + ++#if 0 + struct ImageInfoSpecificVmdkWrapper { + ImageInfoSpecificVmdk *data; + }; +@@ -1134,6 +1139,7 @@ struct ImageInfoSpecificRbdWrapper { + + void qapi_free_ImageInfoSpecificRbdWrapper(ImageInfoSpecificRbdWrapper *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificRbdWrapper, qapi_free_ImageInfoSpecificRbdWrapper) ++#endif + + struct ImageInfoSpecificFileWrapper { + ImageInfoSpecificFile *data; +@@ -1150,9 +1156,11 @@ struct ImageInfoSpecific { + ImageInfoSpecificKind type; + union { /* union tag is @type */ + ImageInfoSpecificQCow2Wrapper qcow2; ++#if 0 + ImageInfoSpecificVmdkWrapper vmdk; + ImageInfoSpecificLUKSWrapper luks; + ImageInfoSpecificRbdWrapper rbd; ++#endif + ImageInfoSpecificFileWrapper file; + } u; + }; +@@ -2186,6 +2194,7 @@ struct BlockJobChangeOptionsMirror { + void qapi_free_BlockJobChangeOptionsMirror(BlockJobChangeOptionsMirror *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobChangeOptionsMirror, qapi_free_BlockJobChangeOptionsMirror) + ++#if 0 + struct q_obj_BlockJobChangeOptions_base { + char *id; + JobType type; +@@ -2201,6 +2210,7 @@ struct BlockJobChangeOptions { + + void qapi_free_BlockJobChangeOptions(BlockJobChangeOptions *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobChangeOptions, qapi_free_BlockJobChangeOptions) ++#endif + + struct BlockdevCacheOptions { + bool has_direct; +@@ -2341,6 +2351,7 @@ struct Qcow2OverlapChecks { + void qapi_free_Qcow2OverlapChecks(Qcow2OverlapChecks *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2OverlapChecks, qapi_free_Qcow2OverlapChecks) + ++#if 0 + struct q_obj_BlockdevQcowEncryption_base { + BlockdevQcowEncryptionFormat format; + }; +@@ -2385,6 +2396,7 @@ struct BlockdevQcow2Encryption { + + void qapi_free_BlockdevQcow2Encryption(BlockdevQcow2Encryption *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevQcow2Encryption, qapi_free_BlockdevQcow2Encryption) ++#endif + + struct BlockdevOptionsPreallocate { + /* Members inherited from BlockdevOptionsGenericFormat: */ +@@ -2442,6 +2454,7 @@ static inline BlockdevOptionsGenericCOWFormat *qapi_BlockdevOptionsQcow2_base(co + void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQcow2, qapi_free_BlockdevOptionsQcow2) + ++#if 0 + struct SshHostKeyHash { + SshHostKeyCheckHashType type; + char *hash; +@@ -2575,6 +2588,7 @@ struct BlockdevOptionsBlkreplay { + + void qapi_free_BlockdevOptionsBlkreplay(BlockdevOptionsBlkreplay *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsBlkreplay, qapi_free_BlockdevOptionsBlkreplay) ++#endif + + struct BlockdevRefList { + BlockdevRefList *next; +@@ -2598,6 +2612,7 @@ struct BlockdevOptionsQuorum { + void qapi_free_BlockdevOptionsQuorum(BlockdevOptionsQuorum *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQuorum, qapi_free_BlockdevOptionsQuorum) + ++#if 0 + struct BlockdevOptionsGluster { + char *volume; + char *path; +@@ -2609,6 +2624,7 @@ struct BlockdevOptionsGluster { + + void qapi_free_BlockdevOptionsGluster(BlockdevOptionsGluster *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsGluster, qapi_free_BlockdevOptionsGluster) ++#endif + + #if defined(CONFIG_BLKIO) + struct BlockdevOptionsIoUring { +@@ -2683,6 +2699,7 @@ struct BlockdevOptionsIscsi { + void qapi_free_BlockdevOptionsIscsi(BlockdevOptionsIscsi *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsIscsi, qapi_free_BlockdevOptionsIscsi) + ++#if 0 + struct RbdEncryptionOptionsLUKSBase { + char *key_secret; + }; +@@ -2848,6 +2865,7 @@ struct BlockdevOptionsReplication { + char *top_id; + }; + #endif /* defined(CONFIG_REPLICATION) */ ++#endif + + #if defined(CONFIG_REPLICATION) + static inline BlockdevOptionsGenericFormat *qapi_BlockdevOptionsReplication_base(const BlockdevOptionsReplication *obj) +@@ -2998,6 +3016,7 @@ static inline BlockdevOptionsCurlBase *qapi_BlockdevOptionsCurlFtps_base(const B + void qapi_free_BlockdevOptionsCurlFtps(BlockdevOptionsCurlFtps *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsCurlFtps, qapi_free_BlockdevOptionsCurlFtps) + ++#if 0 + struct BlockdevOptionsNbd { + SocketAddress *server; + char *export; +@@ -3012,6 +3031,7 @@ struct BlockdevOptionsNbd { + + void qapi_free_BlockdevOptionsNbd(BlockdevOptionsNbd *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsNbd, qapi_free_BlockdevOptionsNbd) ++#endif + + struct BlockdevOptionsRaw { + /* Members inherited from BlockdevOptionsGenericFormat: */ +@@ -3105,26 +3125,31 @@ struct BlockdevOptions { + bool has_detect_zeroes; + BlockdevDetectZeroesOptions detect_zeroes; + union { /* union tag is @driver */ +- BlockdevOptionsBlkdebug blkdebug; +- BlockdevOptionsBlklogwrites blklogwrites; +- BlockdevOptionsBlkverify blkverify; +- BlockdevOptionsBlkreplay blkreplay; +- BlockdevOptionsGenericFormat bochs; +- BlockdevOptionsGenericFormat cloop; +- BlockdevOptionsGenericFormat compress; +- BlockdevOptionsCbw copy_before_write; +- BlockdevOptionsCor copy_on_read; +- BlockdevOptionsGenericFormat dmg; ++#if 0 ++ //BlockdevOptionsBlkdebug blkdebug; ++ //BlockdevOptionsBlklogwrites blklogwrites; ++ //BlockdevOptionsBlkverify blkverify; ++ //BlockdevOptionsBlkreplay blkreplay; ++ //BlockdevOptionsGenericFormat bochs; ++ //BlockdevOptionsGenericFormat cloop; ++ //BlockdevOptionsGenericFormat compress; ++ //BlockdevOptionsCbw copy_before_write; ++ //BlockdevOptionsCor copy_on_read; ++ //BlockdevOptionsGenericFormat dmg; ++#endif + BlockdevOptionsFile file; +- BlockdevOptionsCurlFtp ftp; +- BlockdevOptionsCurlFtps ftps; +- BlockdevOptionsGluster gluster; ++#if 0 ++ //BlockdevOptionsCurlFtp ftp; ++ //BlockdevOptionsCurlFtps ftps; ++ //BlockdevOptionsGluster gluster; ++#endif + #if defined(HAVE_HOST_BLOCK_DEVICE) + BlockdevOptionsFile host_cdrom; + #endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ + #if defined(HAVE_HOST_BLOCK_DEVICE) + BlockdevOptionsFile host_device; + #endif /* defined(HAVE_HOST_BLOCK_DEVICE) */ ++#if 0 + BlockdevOptionsCurlHttp http; + BlockdevOptionsCurlHttps https; + #if defined(CONFIG_BLKIO) +@@ -3141,8 +3166,10 @@ struct BlockdevOptions { + BlockdevOptionsNvmeIoUring nvme_io_uring; + #endif /* defined(CONFIG_BLKIO) */ + BlockdevOptionsGenericFormat parallels; ++#endif + BlockdevOptionsPreallocate preallocate; + BlockdevOptionsQcow2 qcow2; ++#if 0 + BlockdevOptionsQcow qcow; + BlockdevOptionsGenericCOWFormat qed; + BlockdevOptionsQuorum quorum; +@@ -3168,6 +3195,7 @@ struct BlockdevOptions { + BlockdevOptionsGenericCOWFormat vmdk; + BlockdevOptionsGenericFormat vpc; + BlockdevOptionsVVFAT vvfat; ++#endif + } u; + }; + +@@ -3227,6 +3255,7 @@ struct BlockdevCreateOptionsFile { + void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsFile, qapi_free_BlockdevCreateOptionsFile) + ++#if 0 + struct BlockdevCreateOptionsGluster { + BlockdevOptionsGluster *location; + uint64_t size; +@@ -3295,6 +3324,7 @@ struct BlockdevCreateOptionsQcow { + + void qapi_free_BlockdevCreateOptionsQcow(BlockdevCreateOptionsQcow *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevCreateOptionsQcow, qapi_free_BlockdevCreateOptionsQcow) ++#endif + + struct BlockdevCreateOptionsQcow2 { + BlockdevRef *file; +@@ -3309,7 +3339,7 @@ struct BlockdevCreateOptionsQcow2 { + char *backing_file; + bool has_backing_fmt; + BlockdevDriver backing_fmt; +- QCryptoBlockCreateOptions *encrypt; ++ //QCryptoBlockCreateOptions *encrypt; + bool has_cluster_size; + uint64_t cluster_size; + bool has_preallocation; +@@ -3424,12 +3454,15 @@ struct BlockdevCreateOptions { + BlockdevDriver driver; + union { /* union tag is @driver */ + BlockdevCreateOptionsFile file; ++#if 0 + BlockdevCreateOptionsGluster gluster; + BlockdevCreateOptionsLUKS luks; + BlockdevCreateOptionsNfs nfs; + BlockdevCreateOptionsParallels parallels; + BlockdevCreateOptionsQcow qcow; ++#endif + BlockdevCreateOptionsQcow2 qcow2; ++#if 0 + BlockdevCreateOptionsQed qed; + BlockdevCreateOptionsRbd rbd; + BlockdevCreateOptionsSsh ssh; +@@ -3437,6 +3470,7 @@ struct BlockdevCreateOptions { + BlockdevCreateOptionsVhdx vhdx; + BlockdevCreateOptionsVmdk vmdk; + BlockdevCreateOptionsVpc vpc; ++#endif + } u; + }; + +@@ -3448,6 +3482,7 @@ struct q_obj_blockdev_create_arg { + BlockdevCreateOptions *options; + }; + ++#if 0 + struct BlockdevAmendOptionsLUKS { + /* Members inherited from QCryptoBlockAmendOptionsLUKS: */ + QCryptoBlockLUKSKeyslotState state; +@@ -3468,9 +3503,10 @@ static inline QCryptoBlockAmendOptionsLUKS *qapi_BlockdevAmendOptionsLUKS_base(c + + void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptionsLUKS, qapi_free_BlockdevAmendOptionsLUKS) ++#endif + + struct BlockdevAmendOptionsQcow2 { +- QCryptoBlockAmendOptions *encrypt; ++ //QCryptoBlockAmendOptions *encrypt; + }; + + void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj); +@@ -3483,7 +3519,7 @@ struct q_obj_BlockdevAmendOptions_base { + struct BlockdevAmendOptions { + BlockdevDriver driver; + union { /* union tag is @driver */ +- BlockdevAmendOptionsLUKS luks; ++ //BlockdevAmendOptionsLUKS luks; + BlockdevAmendOptionsQcow2 qcow2; + } u; + }; +@@ -3573,12 +3609,14 @@ struct q_obj_x_blockdev_change_arg { + char *node; + }; + ++#if 0 + struct q_obj_x_blockdev_set_iothread_arg { + char *node_name; + StrOrNull *iothread; + bool has_force; + bool force; + }; ++#endif + + struct q_obj_QUORUM_FAILURE_arg { + char *reference; +diff --git a/include/qapi/qapi-visit-block-core.h b/include/qapi/qapi-visit-block-core.h +index 9462b281..e689a244 100644 +--- a/include/qapi/qapi-visit-block-core.h ++++ b/include/qapi/qapi-visit-block-core.h +@@ -17,9 +17,9 @@ + #include "qapi-types-block-core.h" + + #include "qapi-visit-common.h" +-#include "qapi-visit-crypto.h" +-#include "qapi-visit-job.h" +-#include "qapi-visit-sockets.h" ++//#include "qapi-visit-crypto.h" ++#include "qapi/qapi-visit-job.h" ++//#include "qapi-visit-sockets.h" + + bool visit_type_SnapshotInfo_members(Visitor *v, SnapshotInfo *obj, Error **errp); + +diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h +index f2e95681..0a0c8361 100644 +--- a/include/qapi/qmp/dispatch.h ++++ b/include/qapi/qmp/dispatch.h +@@ -14,7 +14,7 @@ + #ifndef QAPI_QMP_DISPATCH_H + #define QAPI_QMP_DISPATCH_H + +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qemu/queue.h" + + typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); +diff --git a/include/qcow2.h b/include/qcow2.h +index a9e3481c..5d606fc9 100644 +--- a/include/qcow2.h ++++ b/include/qcow2.h +@@ -25,7 +25,7 @@ + #ifndef BLOCK_QCOW2_H + #define BLOCK_QCOW2_H + +-#include "crypto/block.h" ++//#include "crypto/block.h" + #include "qemu/coroutine.h" + #include "qemu/units.h" + #include "block/block_int.h" +@@ -231,10 +231,12 @@ typedef struct QCowSnapshot { + struct Qcow2Cache; + typedef struct Qcow2Cache Qcow2Cache; + ++#if 0 + typedef struct Qcow2CryptoHeaderExtension { + uint64_t offset; + uint64_t length; + } QEMU_PACKED Qcow2CryptoHeaderExtension; ++#endif + + typedef struct Qcow2UnknownHeaderExtension { + uint32_t magic; +@@ -359,12 +361,12 @@ typedef struct BDRVQcow2State { + + CoMutex lock; + +- Qcow2CryptoHeaderExtension crypto_header; /* QCow2 header extension */ +- QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ +- QCryptoBlock *crypto; /* Disk encryption format driver */ +- bool crypt_physical_offset; /* Whether to use virtual or physical offset +- for encryption initialization vector tweak */ +- uint32_t crypt_method_header; ++ //Qcow2CryptoHeaderExtension crypto_header; /* QCow2 header extension */ ++ //QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ ++ //QCryptoBlock *crypto; /* Disk encryption format driver */ ++ //bool crypt_physical_offset; /* Whether to use virtual or physical offset ++ // for encryption initialization vector tweak */ ++ //uint32_t crypt_method_header; + uint64_t snapshots_offset; + int snapshots_size; + unsigned int nb_snapshots; +@@ -892,9 +894,11 @@ int coroutine_fn qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *re + + void GRAPH_RDLOCK qcow2_process_discards(BlockDriverState *bs, int ret); + ++#if 0 + int GRAPH_RDLOCK + qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, + int64_t size); ++#endif + int GRAPH_RDLOCK + qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, + int64_t size, bool data_file); +@@ -924,9 +928,11 @@ int coroutine_fn GRAPH_RDLOCK + qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); + + int GRAPH_RDLOCK qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); ++#if 0 + int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, + uint8_t *buf, int nb_sectors, bool enc, Error **errp); + ++#endif + int GRAPH_RDLOCK + qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, + unsigned int *bytes, uint64_t *host_offset, +@@ -1064,11 +1070,13 @@ qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size, + ssize_t coroutine_fn + qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, + const void *src, size_t src_size); ++#if 0 + int coroutine_fn + qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_offset, + uint64_t guest_offset, void *buf, size_t len); + int coroutine_fn + qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset, + uint64_t guest_offset, void *buf, size_t len); ++#endif + + #endif +diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h +index 7a3f2e65..9e392dcd 100644 +--- a/include/qemu/atomic.h ++++ b/include/qemu/atomic.h +@@ -15,7 +15,7 @@ + #ifndef QEMU_ATOMIC_H + #define QEMU_ATOMIC_H + +-#include "compiler.h" ++#include "qemu/compiler.h" + + /* Compiler barrier */ + #define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) +diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h +index 2c0a2fe7..df735fa7 100644 +--- a/include/qemu/bitops.h ++++ b/include/qemu/bitops.h +@@ -12,9 +12,8 @@ + #ifndef BITOPS_H + #define BITOPS_H + +- +-#include "host-utils.h" +-#include "atomic.h" ++#include "qemu/host-utils.h" ++#include "qemu/atomic.h" + + #define BITS_PER_BYTE CHAR_BIT + #define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) +@@ -41,6 +40,7 @@ static inline void set_bit(long nr, unsigned long *addr) + *p |= mask; + } + ++#if 0 + /** + * set_bit_atomic - Set a bit in memory atomically + * @nr: the bit to set +@@ -53,6 +53,7 @@ static inline void set_bit_atomic(long nr, unsigned long *addr) + + qatomic_or(p, mask); + } ++#endif + + /** + * clear_bit - Clears a bit in memory +@@ -67,6 +68,7 @@ static inline void clear_bit(long nr, unsigned long *addr) + *p &= ~mask; + } + ++#if 0 + /** + * clear_bit_atomic - Clears a bit in memory atomically + * @nr: Bit to clear +@@ -79,6 +81,7 @@ static inline void clear_bit_atomic(long nr, unsigned long *addr) + + return qatomic_and(p, ~mask); + } ++#endif + + /** + * change_bit - Toggle a bit in memory +diff --git a/include/qemu/config-host.h b/include/qemu/config-host.h +new file mode 100644 +index 00000000..c0b00bf6 +--- /dev/null ++++ b/include/qemu/config-host.h +@@ -0,0 +1,495 @@ ++/* ++ * Autogenerated by the Meson build system. ++ * Do not edit, your changes will be lost. ++ */ ++ ++#pragma once ++ ++#define CONFIG_ACCEPT4 ++ ++#undef CONFIG_AF_ALG ++ ++#define CONFIG_AF_VSOCK ++ ++#undef CONFIG_AF_XDP ++ ++#undef CONFIG_ALIGNED_MALLOC ++ ++#undef CONFIG_ARM_AES_BUILTIN ++ ++#define CONFIG_ASAN_IFACE_FIBER ++ ++#undef CONFIG_ASM_HWPROBE_H ++ ++#undef CONFIG_ATOMIC128 ++ ++#undef CONFIG_ATOMIC128_OPT ++ ++#define CONFIG_ATOMIC64 ++ ++#define CONFIG_ATTR ++ ++#define CONFIG_AUDIO_ALSA ++ ++#undef CONFIG_AUDIO_COREAUDIO ++ ++#define CONFIG_AUDIO_DRIVERS "pa", "oss", ++ ++#undef CONFIG_AUDIO_DSOUND ++ ++#undef CONFIG_AUDIO_JACK ++ ++#define CONFIG_AUDIO_OSS ++ ++#define CONFIG_AUDIO_PA ++ ++#define CONFIG_AUDIO_PIPEWIRE ++ ++#define CONFIG_AUDIO_SDL ++ ++#undef CONFIG_AUDIO_SNDIO ++ ++#define CONFIG_AVX2_OPT ++ ++#define CONFIG_AVX512BW_OPT ++ ++#define CONFIG_BDRV_RO_WHITELIST ++ ++#define CONFIG_BDRV_RW_WHITELIST ++ ++#undef CONFIG_BDRV_WHITELIST_TOOLS ++ ++#define CONFIG_BINDIR "/usr/local/bin" ++ ++#undef CONFIG_BLKIO ++ ++//#define CONFIG_BLKZONED ++ ++#undef CONFIG_BRLAPI ++ ++#undef CONFIG_BSD ++ ++#define CONFIG_CAPSTONE ++ ++#undef CONFIG_CFI ++ ++#define CONFIG_CLOCK_ADJTIME ++ ++#define CONFIG_CLOSE_RANGE ++ ++#define CONFIG_CMPXCHG128 ++ ++#undef CONFIG_COCOA ++ ++#define CONFIG_COROUTINE_POOL ++ ++#define CONFIG_CPUID_H ++ ++#undef CONFIG_CRYPTO_SM4 ++ ++#define CONFIG_CURL ++ ++#define CONFIG_CURSES ++ ++#undef CONFIG_DARWIN ++ ++#define CONFIG_DBUS_DISPLAY ++ ++#define CONFIG_DEBUG_GRAPH_LOCK ++ ++#define CONFIG_DEBUG_MUTEX ++ ++#undef CONFIG_DEBUG_REMAP ++ ++#undef CONFIG_DEBUG_STACK_USAGE ++ ++//#define CONFIG_DEBUG_TCG ++ ++#define CONFIG_DUP3 ++ ++#undef CONFIG_EBPF ++ ++#undef CONFIG_ELF_AUX_INFO ++ ++#define CONFIG_EPOLL ++ ++#define CONFIG_EPOLL_CREATE1 ++ ++#define CONFIG_EVENTFD ++ ++#define CONFIG_FALLOCATE ++ ++#define CONFIG_FALLOCATE_PUNCH_HOLE ++ ++#define CONFIG_FALLOCATE_ZERO_RANGE ++ ++#define CONFIG_FDATASYNC ++ ++#define CONFIG_FDT ++ ++#define CONFIG_FIEMAP ++ ++#undef CONFIG_FREEBSD ++ ++#define CONFIG_FSFREEZE ++ ++#define CONFIG_FSTRIM ++ ++#define CONFIG_FUSE ++ ++#define CONFIG_FUSE_LSEEK ++ ++#undef CONFIG_FUZZ ++ ++#define CONFIG_GBM ++ ++#undef CONFIG_GCOV ++ ++#undef CONFIG_GCRYPT ++ ++#define CONFIG_GETAUXVAL ++ ++#define CONFIG_GETCPU ++ ++#define CONFIG_GETRANDOM ++ ++#define CONFIG_GETTID ++ ++#define CONFIG_GIO ++ ++#undef CONFIG_GLUSTERFS ++ ++#define CONFIG_GNUTLS ++ ++#define CONFIG_GNUTLS_CRYPTO ++ ++#define CONFIG_GTK ++ ++#undef CONFIG_GTK_CLIPBOARD ++ ++#define CONFIG_HEXAGON_IDEF_PARSER ++ ++#undef CONFIG_HOGWEED ++ ++#define CONFIG_HOST_DSOSUF ".so" ++ ++#define CONFIG_IASL "/usr/bin/iasl" ++ ++#define CONFIG_INOTIFY ++ ++#define CONFIG_INOTIFY1 ++ ++#define CONFIG_INT128 ++ ++#define CONFIG_INT128_TYPE ++ ++#define CONFIG_IOVEC ++ ++#define CONFIG_KEYUTILS ++ ++#define CONFIG_KVM_TARGETS "i386-softmmu" ,"x86_64-softmmu" ++ ++#define CONFIG_L2TPV3 ++ ++#undef CONFIG_LIBATTR ++ ++#define CONFIG_LIBCAP_NG ++ ++#undef CONFIG_LIBDAXCTL ++ ++#define CONFIG_LIBDW ++ ++#undef CONFIG_LIBISCSI ++ ++#undef CONFIG_LIBNFS ++ ++#undef CONFIG_LIBPMEM ++ ++#undef CONFIG_LIBSSH ++ ++#define CONFIG_LIBUDEV ++ ++#define CONFIG_LINUX ++ ++#define CONFIG_LINUX_AIO ++ ++#undef CONFIG_LINUX_IO_URING ++ ++#define CONFIG_LINUX_MAGIC_H ++ ++#define CONFIG_LZO ++ ++#define CONFIG_MADVISE ++ ++#define CONFIG_MALLOC_TRIM ++ ++#define CONFIG_MEMALIGN ++ ++#undef CONFIG_MEMBARRIER ++ ++#define CONFIG_MEMFD ++ ++#undef CONFIG_MODULES ++ ++#undef CONFIG_MODULE_UPGRADES ++ ++#undef CONFIG_MPATH ++ ++#undef CONFIG_NETMAP ++ ++#undef CONFIG_NETTLE ++ ++#undef CONFIG_NUMA ++ ++#define CONFIG_OPENGL ++ ++#define CONFIG_OPEN_BY_HANDLE ++ ++#define CONFIG_PIXMAN ++ ++#define CONFIG_PLUGIN ++ ++#define CONFIG_PNG ++ ++#define CONFIG_POSIX ++ ++#define CONFIG_POSIX_FALLOCATE ++ ++#define CONFIG_POSIX_MADVISE ++ ++#define CONFIG_POSIX_MEMALIGN ++ ++#define CONFIG_PPOLL ++ ++#define CONFIG_PRCTL_PR_SET_TIMERSLACK ++ ++#define CONFIG_PREADV ++ ++#define CONFIG_PREFIX "/usr/local" ++ ++#define CONFIG_PTHREAD_AFFINITY_NP ++ ++#undef CONFIG_PTHREAD_CONDATTR_SETCLOCK ++ ++#undef CONFIG_PTHREAD_FCHDIR_NP ++ ++#undef CONFIG_PTHREAD_SETNAME_NP_WO_TID ++ ++#define CONFIG_PTHREAD_SETNAME_NP_W_TID ++ ++#undef CONFIG_PTHREAD_SET_NAME_NP ++ ++#define CONFIG_QEMU_CONFDIR "/usr/local/etc/qemu" ++ ++#define CONFIG_QEMU_DATADIR "/usr/local/share/qemu" ++ ++#define CONFIG_QEMU_DESKTOPDIR "/usr/local/share/applications" ++ ++#define CONFIG_QEMU_FIRMWAREPATH "/usr/local/share/qemu-firmware", ++ ++#define CONFIG_QEMU_HELPERDIR "/usr/local/libexec" ++ ++#define CONFIG_QEMU_ICONDIR "/usr/local/share/icons" ++ ++#define CONFIG_QEMU_LOCALEDIR "/usr/local/share/locale" ++ ++#define CONFIG_QEMU_LOCALSTATEDIR "/var/local" ++ ++#define CONFIG_QEMU_MODDIR "/usr/local/lib64/qemu" ++ ++#undef CONFIG_QEMU_PRIVATE_XTS ++ ++#define CONFIG_QOM_CAST_DEBUG ++ ++#undef CONFIG_QPL ++ ++#undef CONFIG_RBD ++ ++#undef CONFIG_RDMA ++ ++#define CONFIG_RELOCATABLE ++ ++#define CONFIG_REPLICATION ++ ++#define CONFIG_RTNETLINK ++ ++#undef CONFIG_SAFESTACK ++ ++#undef CONFIG_SCHED_GETCPU ++ ++#define CONFIG_SDL ++ ++#undef CONFIG_SDL_IMAGE ++ ++#define CONFIG_SECCOMP ++ ++#define CONFIG_SECCOMP_SYSRAWRC ++ ++#define CONFIG_SECRET_KEYRING ++ ++#undef CONFIG_SELINUX ++ ++#define CONFIG_SENDFILE ++ ++#define CONFIG_SETNS ++ ++#define CONFIG_SIGNALFD ++ ++#define CONFIG_SLIRP ++ ++#define CONFIG_SMBD_COMMAND "/usr/sbin/smbd" ++ ++#define CONFIG_SNAPPY ++ ++#undef CONFIG_SOLARIS ++ ++#undef CONFIG_SPICE ++ ++#undef CONFIG_SPICE_PROTOCOL ++ ++#define CONFIG_SPLICE ++ ++#define CONFIG_STATX ++ ++#define CONFIG_STATX_MNT_ID ++ ++#define CONFIG_SYNCFS ++ ++#define CONFIG_SYNC_FILE_RANGE ++ ++#define CONFIG_SYSCONFDIR "/usr/local/etc" ++ ++#define CONFIG_SYSMACROS ++ ++#define CONFIG_TASN1 ++ ++//#define CONFIG_TCG 1 ++ ++//#undef CONFIG_TCG_INTERPRETER ++ ++#define CONFIG_TIMERFD ++ ++#define CONFIG_TLS_PRIORITY "NORMAL" ++ ++#define CONFIG_TPM ++ ++#define CONFIG_TRACE_FILE "trace" ++ ++#define CONFIG_TRACE_LOG ++ ++#undef CONFIG_TSAN ++ ++#undef CONFIG_UADK ++ ++#undef CONFIG_USBFS ++ ++#define CONFIG_USB_LIBUSB ++ ++//#define CONFIG_VALGRIND_H ++ ++#define CONFIG_VALLOC ++ ++#undef CONFIG_VDE ++ ++#define CONFIG_VDUSE_BLK_EXPORT ++ ++#define CONFIG_VHOST ++ ++#define CONFIG_VHOST_CRYPTO ++ ++#define CONFIG_VHOST_KERNEL ++ ++#define CONFIG_VHOST_NET ++ ++#define CONFIG_VHOST_NET_USER ++ ++#define CONFIG_VHOST_NET_VDPA ++ ++#define CONFIG_VHOST_USER ++ ++#define CONFIG_VHOST_USER_BLK_SERVER ++ ++#define CONFIG_VHOST_VDPA ++ ++#define CONFIG_VIRTFS ++ ++#undef CONFIG_VMNET ++ ++#define CONFIG_VNC ++ ++#define CONFIG_VNC_JPEG ++ ++#define CONFIG_VNC_SASL ++ ++#define CONFIG_VTE ++ ++#undef CONFIG_WIN32 ++ ++#define CONFIG_X11 ++ ++#define CONFIG_XEN_BACKEND ++ ++#define CONFIG_XEN_CTRL_INTERFACE_VERSION 41700 ++ ++#define CONFIG_XKBCOMMON ++ ++#define CONFIG_ZSTD ++ ++#define HAVE_BLK_ZONE_REP_CAPACITY ++ ++#undef HAVE_BROKEN_SIZE_MAX ++ ++#define HAVE_BTRFS_H ++ ++#define HAVE_COPY_FILE_RANGE ++ ++#define HAVE_DRM_H ++ ++#define HAVE_FSXATTR ++ ++#define HAVE_GETIFADDRS ++ ++#undef HAVE_GLIB_WITH_SLICE_ALLOCATOR ++ ++#define HAVE_HOST_BLOCK_DEVICE ++ ++#undef HAVE_IPPROTO_MPTCP ++ ++#undef HAVE_MADVISE_WITHOUT_PROTOTYPE ++ ++#define HAVE_MLOCKALL ++ ++#define HAVE_OPENPTY ++ ++#undef HAVE_OPTRESET ++ ++#define HAVE_PTY_H ++ ++#undef HAVE_SIGEV_NOTIFY_THREAD_ID ++ ++#define HAVE_STRCHRNUL ++ ++#define HAVE_STRUCT_STAT_ST_ATIM ++ ++#define HAVE_SYSTEM_FUNCTION ++ ++#undef HAVE_SYS_DISK_H ++ ++#undef HAVE_SYS_IOCCOM_H ++ ++#undef HAVE_SYS_KCOV_H ++ ++#define HAVE_UTMPX ++ ++#undef HAVE_VSS_SDK ++ ++#define HOST_X86_64 1 ++ ++#define QEMU_VERSION "9.1.1" ++ ++#define QEMU_VERSION_MAJOR 9 ++ ++#define QEMU_VERSION_MICRO 1 ++ ++#define QEMU_VERSION_MINOR 1 ++ +diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h +index 8136e336..ef8eb355 100644 +--- a/include/qemu/hbitmap.h ++++ b/include/qemu/hbitmap.h +@@ -12,8 +12,8 @@ + #ifndef HBITMAP_H + #define HBITMAP_H + +-#include "bitops.h" +-#include "host-utils.h" ++#include "qemu/bitops.h" ++#include "qemu/host-utils.h" + + typedef struct HBitmap HBitmap; + typedef struct HBitmapIter HBitmapIter; +diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h +index ead97d35..12ae37af 100644 +--- a/include/qemu/host-utils.h ++++ b/include/qemu/host-utils.h +@@ -31,7 +31,7 @@ + #define HOST_UTILS_H + + #include "qemu/bswap.h" +-#include "qemu/int128.h" ++//#include "qemu/int128.h" + + #ifdef CONFIG_INT128 + static inline void mulu64(uint64_t *plow, uint64_t *phigh, +@@ -856,6 +856,6 @@ static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, + #endif + } + +-Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor); +-Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); ++//Int128 divu256(Int128 *plow, Int128 *phigh, Int128 divisor); ++//Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); + #endif +diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h +index 5764db15..e0a0be44 100644 +--- a/include/qemu/main-loop.h ++++ b/include/qemu/main-loop.h +@@ -26,11 +26,12 @@ + #define QEMU_MAIN_LOOP_H + + #include "block/aio.h" +-#include "qom/object.h" +-#include "sysemu/event-loop-base.h" ++//#include "qom/object.h" ++//#include "sysemu/event-loop-base.h" + + #define SIG_IPI SIGUSR1 + ++#if 0 + #define TYPE_MAIN_LOOP "main-loop" + OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP) + +@@ -38,6 +39,9 @@ struct MainLoop { + EventLoopBase parent_obj; + }; + typedef struct MainLoop MainLoop; ++#endif ++ ++void qemu_init_cpu_loop(void); + + /** + * qemu_init_main_loop: Set up the process so that it can run the main loop. +@@ -110,6 +114,7 @@ AioContext *qemu_get_aio_context(void); + */ + void qemu_notify_event(void); + ++#if 0 + #ifdef _WIN32 + /* return TRUE if no sleep should be done afterwards */ + typedef int PollingFunc(void *opaque); +@@ -195,6 +200,7 @@ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); + * the event loop. + */ + typedef int IOCanReadHandler(void *opaque); ++#endif + + /** + * qemu_set_fd_handler: Register a file descriptor with the main loop +@@ -342,6 +348,7 @@ void bql_lock_impl(const char *file, int line); + */ + void bql_unlock(void); + ++#if 0 + /** + * BQL_LOCK_GUARD + * +@@ -369,6 +376,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(BQLLockAuto, bql_auto_unlock) + #define BQL_LOCK_GUARD() \ + g_autoptr(BQLLockAuto) _bql_lock_auto __attribute__((unused)) \ + = bql_auto_lock(__FILE__, __LINE__) ++#endif + + /* + * qemu_cond_wait_bql: Wait on condition for the Big QEMU Lock (BQL) +diff --git a/include/qemu/option_int.h b/include/qemu/option_int.h +index 5dd9a516..f88dd33b 100644 +--- a/include/qemu/option_int.h ++++ b/include/qemu/option_int.h +@@ -46,7 +46,7 @@ struct QemuOpt { + struct QemuOpts { + char *id; + QemuOptsList *list; +- Location loc; ++ //Location loc; + QTAILQ_HEAD(, QemuOpt) head; + QTAILQ_ENTRY(QemuOpts) next; + }; +diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h +index fe7c3c5f..bdc90246 100644 +--- a/include/qemu/osdep.h ++++ b/include/qemu/osdep.h +@@ -35,7 +35,7 @@ + #ifdef COMPILING_PER_TARGET + #include CONFIG_TARGET + #else +-#include "exec/poison.h" ++//#include "exec/poison.h" + #endif + + /* +diff --git a/include/qemu/range.h b/include/qemu/range.h +index d446ad88..8afd93e0 100644 +--- a/include/qemu/range.h ++++ b/include/qemu/range.h +@@ -236,6 +236,7 @@ int range_compare(Range *a, Range *b); + + GList *range_list_insert(GList *list, Range *data); + ++#if 0 + /* + * Inverse an array of sorted ranges over the [low, high] span, ie. + * original ranges becomes holes in the newly allocated inv_ranges +@@ -243,5 +244,6 @@ GList *range_list_insert(GList *list, Range *data); + void range_inverse_array(GList *in_ranges, + GList **out_ranges, + uint64_t low, uint64_t high); ++#endif + + #endif +diff --git a/include/qemu/timer.h b/include/qemu/timer.h +index fa56ec94..f877974c 100644 +--- a/include/qemu/timer.h ++++ b/include/qemu/timer.h +@@ -842,19 +842,21 @@ static inline int64_t get_clock(void) + + #else + +-extern int use_rt_clock; ++//extern int use_rt_clock; + + static inline int64_t get_clock(void) + { ++#if 0 + if (use_rt_clock) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; + } else { ++#endif + /* XXX: using gettimeofday leads to problems if the date + changes, so it should be avoided. */ + return get_clock_realtime(); +- } ++ //} + } + #endif + +diff --git a/include/sysemu/block-backend-common.h b/include/sysemu/block-backend-common.h +index 780cea73..6b77d20d 100644 +--- a/include/sysemu/block-backend-common.h ++++ b/include/sysemu/block-backend-common.h +@@ -14,7 +14,7 @@ + #define BLOCK_BACKEND_COMMON_H + + #include "qemu/iov.h" +-#include "block/throttle-groups.h" ++//#include "block/throttle-groups.h" + + /* + * TODO Have to include block/block.h for a bunch of block layer +@@ -97,7 +97,7 @@ typedef struct BlockDevOps { + * friends so that BlockBackends can be kept in lists outside block-backend.c + */ + typedef struct BlockBackendPublic { +- ThrottleGroupMember throttle_group_member; ++ //ThrottleGroupMember throttle_group_member; + } BlockBackendPublic; + + #endif /* BLOCK_BACKEND_COMMON_H */ +diff --git a/include/sysemu/block-backend-global-state.h b/include/sysemu/block-backend-global-state.h +index 49c12b0f..8c557f0d 100644 +--- a/include/sysemu/block-backend-global-state.h ++++ b/include/sysemu/block-backend-global-state.h +@@ -117,7 +117,7 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); + int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); + int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); + +-void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); ++//void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); + void blk_io_limits_disable(BlockBackend *blk); + void blk_io_limits_enable(BlockBackend *blk, const char *group); + void blk_io_limits_update_group(BlockBackend *blk, const char *group); +diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h +index d174275a..040efc7c 100644 +--- a/include/sysemu/block-backend-io.h ++++ b/include/sysemu/block-backend-io.h +@@ -46,6 +46,7 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, + BlockCompletionFunc *cb, void *opaque); + BlockAIOCB *blk_aio_flush(BlockBackend *blk, + BlockCompletionFunc *cb, void *opaque); ++#if 0 + BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones, +@@ -56,6 +57,7 @@ BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, + BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, + QEMUIOVector *qiov, BdrvRequestFlags flags, + BlockCompletionFunc *cb, void *opaque); ++#endif + BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, + BlockCompletionFunc *cb, void *opaque); + void blk_aio_cancel_async(BlockAIOCB *acb); +@@ -92,11 +94,13 @@ void *blk_blockalign(BlockBackend *blk, size_t size); + bool blk_is_writable(BlockBackend *blk); + bool blk_enable_write_cache(BlockBackend *blk); + BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); ++#if 0 + BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, + int error); + void blk_error_action(BlockBackend *blk, BlockErrorAction action, + bool is_read, int error); + void blk_iostatus_set_err(BlockBackend *blk, int error); ++#endif + int blk_get_max_iov(BlockBackend *blk); + int blk_get_max_hw_iov(BlockBackend *blk); + +diff --git a/qcow2/Makefile.am b/qcow2/Makefile.am +new file mode 100644 +index 00000000..69460e08 +--- /dev/null ++++ b/qcow2/Makefile.am +@@ -0,0 +1,21 @@ ++ ++SUBDIRS = lib ++ ++AM_CFLAGS = -Wall ++AM_CFLAGS += -Werror ++AM_CFLAGS += $(if $(GCOV),-fprofile-dir=/tmp/coverage/blktap/qcow2 -fprofile-arcs -ftest-coverage) ++ ++AM_CPPFLAGS = -D_GNU_SOURCE ++AM_CPPFLAGS += -I$(top_srcdir)/include ++AM_CPPFLAGS += $(GLIB_CFLAGS) ++ ++bin_PROGRAMS = qemu-img ++ ++qemu_img_LDADD = lib/libqcow2.la ++ ++qemu_img_SOURCES = qemu-img.c ++qemu_img_SOURCES += qemu-config.c ++qemu_img_SOURCES += qemu-progress.c ++ ++clean-local: ++ -rm -rf *.gc?? +diff --git a/qcow2/lib/Makefile.am b/qcow2/lib/Makefile.am +new file mode 100644 +index 00000000..fb125aa7 +--- /dev/null ++++ b/qcow2/lib/Makefile.am +@@ -0,0 +1,166 @@ ++AUTOMAKE_OPTIONS = subdir-objects ++ ++SUBDIRS = . ++#$(MAYBE_test) ++ ++AM_CFLAGS = -Wall ++AM_CFLAGS += -Werror ++AM_CFLAGS += $(if $(GCOV),-fprofile-dir=/tmp/coverage/blktap/qcow2/lib -fprofile-arcs -ftest-coverage) ++ ++AM_CPPFLAGS = -D_GNU_SOURCE ++AM_CPPFLAGS += -I$(top_srcdir)/include ++AM_CPPFLAGS += -I$(top_srcdir)/lvm ++AM_CPPFLAGS += -I$(top_srcdir)/part ++AM_CPPFLAGS += $(GLIB_CFLAGS) ++AM_CPPFLAGS += $(ZLIB_CFLAGS) ++AM_CPPFLAGS += $(GNUTLS_CFLAGS) ++ ++lib_LTLIBRARIES = libqcow2.la ++lib_LTLIBRARIES += $(MAYBE_libqcow2io_la) ++ ++libqcow2_la_SOURCES = qcow2.c ++libqcow2_la_SOURCES += qcow2-refcount.c ++libqcow2_la_SOURCES += qcow2-cache.c ++libqcow2_la_SOURCES += qcow2-cluster.c ++libqcow2_la_SOURCES += qcow2-bitmap.c ++libqcow2_la_SOURCES += qcow2-threads.c ++libqcow2_la_SOURCES += qcow2-snapshot.c ++libqcow2_la_SOURCES += authz/base.c ++libqcow2_la_SOURCES += block.c ++libqcow2_la_SOURCES += blockdev.c ++libqcow2_la_SOURCES += blockjob.c ++libqcow2_la_SOURCES += block/accounting.c ++libqcow2_la_SOURCES += block/aio_task.c ++libqcow2_la_SOURCES += block/block-backend.c ++libqcow2_la_SOURCES += block/block-gen.c ++libqcow2_la_SOURCES += block/commit.c ++libqcow2_la_SOURCES += block/dirty-bitmap.c ++libqcow2_la_SOURCES += block/file-posix.c ++libqcow2_la_SOURCES += block/graph-lock.c ++libqcow2_la_SOURCES += block/io.c ++libqcow2_la_SOURCES += block/linux-aio.c ++libqcow2_la_SOURCES += block/mirror.c ++libqcow2_la_SOURCES += block/monitor/bitmap-qmp-cmds.c ++libqcow2_la_SOURCES += block/nbd.c ++libqcow2_la_SOURCES += block/progress_meter.c ++libqcow2_la_SOURCES += block/qapi.c ++libqcow2_la_SOURCES += block/raw-format.c ++libqcow2_la_SOURCES += block/snapshot.c ++libqcow2_la_SOURCES += crypto/hash.c ++libqcow2_la_SOURCES += crypto/tlscreds.c ++libqcow2_la_SOURCES += crypto/tlssession.c ++libqcow2_la_SOURCES += hw/block/block.c ++libqcow2_la_SOURCES += hw/block/hd-geometry.c ++libqcow2_la_SOURCES += io/channel.c ++libqcow2_la_SOURCES += io/channel-file.c ++libqcow2_la_SOURCES += io/channel-socket.c ++libqcow2_la_SOURCES += io/channel-tls.c ++libqcow2_la_SOURCES += io/channel-util.c ++libqcow2_la_SOURCES += io/channel-watch.c ++libqcow2_la_SOURCES += io/task.c ++libqcow2_la_SOURCES += job.c ++libqcow2_la_SOURCES += job-qmp.c ++libqcow2_la_SOURCES += nbd/client.c ++libqcow2_la_SOURCES += nbd/client-connection.c ++libqcow2_la_SOURCES += nbd/common.c ++libqcow2_la_SOURCES += qapi/qapi-clone-visitor.c ++libqcow2_la_SOURCES += qapi/qapi-dealloc-visitor.c ++libqcow2_la_SOURCES += qapi/qapi-events-block-core.c ++libqcow2_la_SOURCES += qapi/qapi-events-job.c ++libqcow2_la_SOURCES += qapi/qapi-types-block-core.c ++libqcow2_la_SOURCES += qapi/qapi-types-crypto.c ++libqcow2_la_SOURCES += qapi/qapi-types-common.c ++libqcow2_la_SOURCES += qapi/qapi-types-job.c ++libqcow2_la_SOURCES += qapi/qapi-types-sockets.c ++libqcow2_la_SOURCES += qapi/qapi-types-yank.c ++libqcow2_la_SOURCES += qapi/qapi-util.c ++libqcow2_la_SOURCES += qapi/qapi-visit-block-core.c ++libqcow2_la_SOURCES += qapi/qapi-visit-common.c ++libqcow2_la_SOURCES += qapi/qapi-visit-crypto.c ++libqcow2_la_SOURCES += qapi/qapi-visit-core.c ++libqcow2_la_SOURCES += qapi/qapi-visit-job.c ++libqcow2_la_SOURCES += qapi/qapi-visit-sockets.c ++libqcow2_la_SOURCES += qapi/qapi-visit-yank.c ++libqcow2_la_SOURCES += qapi/qmp-dispatch.c ++libqcow2_la_SOURCES += qapi/qmp-event.c ++libqcow2_la_SOURCES += qapi/qobject-input-visitor.c ++libqcow2_la_SOURCES += qapi/qobject-output-visitor.c ++libqcow2_la_SOURCES += qapi/string-input-visitor.c ++libqcow2_la_SOURCES += qapi/string-output-visitor.c ++libqcow2_la_SOURCES += qobject/block-qdict.c ++libqcow2_la_SOURCES += qobject/json-lexer.c ++libqcow2_la_SOURCES += qobject/json-parser.c ++libqcow2_la_SOURCES += qobject/json-streamer.c ++libqcow2_la_SOURCES += qobject/json-writer.c ++libqcow2_la_SOURCES += qobject/qbool.c ++libqcow2_la_SOURCES += qobject/qdict.c ++libqcow2_la_SOURCES += qobject/qjson.c ++libqcow2_la_SOURCES += qobject/qlist.c ++libqcow2_la_SOURCES += qobject/qnull.c ++libqcow2_la_SOURCES += qobject/qnum.c ++libqcow2_la_SOURCES += qobject/qobject.c ++libqcow2_la_SOURCES += qobject/qstring.c ++libqcow2_la_SOURCES += qom/object.c ++libqcow2_la_SOURCES += qom/container.c ++libqcow2_la_SOURCES += qom/qom-qobject.c ++libqcow2_la_SOURCES += system/cpus.c ++libqcow2_la_SOURCES += util/aio-posix.c ++libqcow2_la_SOURCES += util/aio-wait.c ++libqcow2_la_SOURCES += util/aiocb.c ++libqcow2_la_SOURCES += util/async.c ++libqcow2_la_SOURCES += util/bitmap.c ++libqcow2_la_SOURCES += util/bitops.c ++libqcow2_la_SOURCES += util/bufferiszero.c ++libqcow2_la_SOURCES += util/compatfd.c ++libqcow2_la_SOURCES += util/coroutine-ucontext.c ++libqcow2_la_SOURCES += util/cutils.c ++libqcow2_la_SOURCES += util/defer-call.c ++libqcow2_la_SOURCES += util/error.c ++libqcow2_la_SOURCES += util/error-report.c ++libqcow2_la_SOURCES += util/event_notifier-posix.c ++libqcow2_la_SOURCES += util/fdmon-epoll.c ++libqcow2_la_SOURCES += util/fdmon-poll.c ++libqcow2_la_SOURCES += util/hbitmap.c ++libqcow2_la_SOURCES += util/host-utils.c ++libqcow2_la_SOURCES += util/id.c ++libqcow2_la_SOURCES += util/iov.c ++libqcow2_la_SOURCES += util/lockcnt.c ++libqcow2_la_SOURCES += util/main-loop.c ++libqcow2_la_SOURCES += util/memalign.c ++libqcow2_la_SOURCES += util/module.c ++libqcow2_la_SOURCES += util/notify.c ++libqcow2_la_SOURCES += util/osdep.c ++libqcow2_la_SOURCES += util/oslib-posix.c ++libqcow2_la_SOURCES += util/qemu-coroutine.c ++libqcow2_la_SOURCES += util/qemu-coroutine-lock.c ++libqcow2_la_SOURCES += util/qemu-option.c ++libqcow2_la_SOURCES += util/qemu-print.c ++libqcow2_la_SOURCES += util/qemu-sockets.c ++libqcow2_la_SOURCES += util/qemu-thread-posix.c ++libqcow2_la_SOURCES += util/qemu-timer.c ++libqcow2_la_SOURCES += util/qsp.c ++libqcow2_la_SOURCES += util/range.c ++libqcow2_la_SOURCES += util/rcu.c ++libqcow2_la_SOURCES += util/stats64.c ++libqcow2_la_SOURCES += util/thread-pool.c ++libqcow2_la_SOURCES += util/timed-average.c ++libqcow2_la_SOURCES += util/transactions.c ++libqcow2_la_SOURCES += util/unicode.c ++libqcow2_la_SOURCES += util/uri.c ++libqcow2_la_SOURCES += util/yank.c ++ ++ ++libqcow2_la_LDFLAGS = -version-info 1:1:1 ++ ++libqcow2_la_LIBADD = -lpthread -luuid -laio -ldl $(LIBICONV) $(top_srcdir)/lvm/liblvmutil.la ++libqcow2_la_LIBADD += $(GLIB_LIBS) ++libqcow2_la_LIBADD += $(ZLIB_LIBS) ++libqcow2_la_LIBADD += $(GNUTLS_LIBS) ++libqcow2_la_LIBADD += $(LIBZSTD_LIBS) ++ ++#if ENABLE_TESTS ++#MAYBE_test = test ++#endif ++ ++clean-local: ++ -rm -rf *.gc?? +diff --git a/qcow2/lib/authz/base.c b/qcow2/lib/authz/base.c +index f2b7fbe9..bd155bea 100644 +--- a/qcow2/lib/authz/base.c ++++ b/qcow2/lib/authz/base.c +@@ -21,7 +21,7 @@ + #include "qemu/osdep.h" + #include "authz/base.h" + #include "qemu/module.h" +-#include "trace.h" ++//#include "trace.h" + + bool qauthz_is_allowed(QAuthZ *authz, + const char *identity, +@@ -31,7 +31,7 @@ bool qauthz_is_allowed(QAuthZ *authz, + bool allowed; + + allowed = cls->is_allowed(authz, identity, errp); +- trace_qauthz_is_allowed(authz, identity, allowed); ++ //trace_qauthz_is_allowed(authz, identity, allowed); + + return allowed; + } +diff --git a/qcow2/lib/block.c b/qcow2/lib/block.c +index c317de9e..e823b462 100644 +--- a/qcow2/lib/block.c ++++ b/qcow2/lib/block.c +@@ -24,12 +24,12 @@ + */ + + #include "qemu/osdep.h" +-#include "block/trace.h" ++//#include "block/trace.h" + #include "block/block_int.h" + #include "block/blockjob.h" + #include "block/dirty-bitmap.h" +-#include "block/fuse.h" +-#include "block/nbd.h" ++//#include "block/fuse.h" ++//#include "block/nbd.h" + #include "block/qdict.h" + #include "qemu/error-report.h" + #include "block/module_block.h" +@@ -1917,8 +1917,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, + goto fail_opts; + } + +- trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, +- drv->format_name); ++ //trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, ++ // drv->format_name); + + ro = bdrv_is_read_only(bs); + +@@ -5114,7 +5114,6 @@ static void GRAPH_UNLOCKED bdrv_reopen_abort(BDRVReopenState *reopen_state) + } + } + +- + static void bdrv_close(BlockDriverState *bs) + { + BdrvAioNotifier *ban, *ban_next; +@@ -5180,6 +5179,7 @@ static void bdrv_close(BlockDriverState *bs) + } + } + ++#if 0 + void bdrv_close_all(void) + { + GLOBAL_STATE_CODE(); +@@ -5194,6 +5194,7 @@ void bdrv_close_all(void) + + assert(QTAILQ_EMPTY(&all_bdrv_states)); + } ++#endif + + static bool GRAPH_RDLOCK should_update_child(BdrvChild *c, BlockDriverState *to) + { +@@ -5423,6 +5424,7 @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, + return bdrv_replace_node_common(from, to, true, false, errp); + } + ++#if 0 + int bdrv_drop_filter(BlockDriverState *bs, Error **errp) + { + BlockDriverState *child_bs; +@@ -5442,6 +5444,7 @@ int bdrv_drop_filter(BlockDriverState *bs, Error **errp) + + return ret; + } ++#endif + + /* + * Add new bs contents at the top of an image chain while the chain is +@@ -5499,6 +5502,7 @@ out: + return ret; + } + ++#if 0 + /* Not for empty child */ + int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, + Error **errp) +@@ -5531,6 +5535,7 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, + + return ret; + } ++#endif + + static void bdrv_delete(BlockDriverState *bs) + { +@@ -5551,7 +5556,7 @@ static void bdrv_delete(BlockDriverState *bs) + g_free(bs); + } + +- ++#if 0 + /* + * Replace @bs by newly created block node. + * +@@ -5625,6 +5630,7 @@ fail: + bdrv_unref(new_node_bs); + return NULL; + } ++#endif + + /* + * Run consistency checks on an image +@@ -6113,6 +6119,7 @@ bool bdrv_is_sg(BlockDriverState *bs) + return bs->sg; + } + ++#if 0 + /** + * Return whether the given node supports compressed writes. + */ +@@ -6136,6 +6143,7 @@ bool bdrv_supports_compressed_writes(BlockDriverState *bs) + + return true; + } ++#endif + + const char *bdrv_get_format_name(BlockDriverState *bs) + { +@@ -6226,6 +6234,7 @@ BlockDriverState *bdrv_find_node(const char *node_name) + return NULL; + } + ++#if 0 + /* Put this QMP function here so it can access the static graph_bdrv_states. */ + BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, + Error **errp) +@@ -6382,6 +6391,7 @@ XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) + + return xdbg_graph_finalize(gr); + } ++#endif + + BlockDriverState *bdrv_lookup_bs(const char *device, + const char *node_name, +@@ -7095,7 +7105,7 @@ void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked) + BlockDriver *drv = bs->drv; + IO_CODE(); + assert_bdrv_graph_readable(); +- trace_bdrv_lock_medium(bs, locked); ++ //trace_bdrv_lock_medium(bs, locked); + + if (drv && drv->bdrv_co_lock_medium) { + drv->bdrv_co_lock_medium(bs, locked); +@@ -7425,6 +7435,7 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs) + return bs ? bs->aio_context : qemu_get_aio_context(); + } + ++#if 0 + AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs) + { + Coroutine *self = qemu_coroutine_self(); +@@ -7449,6 +7460,7 @@ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx) + aio_co_reschedule_self(old_ctx); + bdrv_dec_in_flight(bs); + } ++#endif + + static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban) + { +diff --git a/qcow2/lib/block/accounting.c b/qcow2/lib/block/accounting.c +index 28297453..dea4f667 100644 +--- a/qcow2/lib/block/accounting.c ++++ b/qcow2/lib/block/accounting.c +@@ -27,7 +27,8 @@ + #include "block/accounting.h" + #include "block/block_int.h" + #include "qemu/timer.h" +-#include "sysemu/qtest.h" ++//#include "sysemu/qtest.h" ++#define qtest_enabled() 0 + + static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; + static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; +diff --git a/qcow2/lib/block/block-backend.c b/qcow2/lib/block/block-backend.c +index db6f9b92..d15da172 100644 +--- a/qcow2/lib/block/block-backend.c ++++ b/qcow2/lib/block/block-backend.c +@@ -15,18 +15,18 @@ + #include "block/block_int.h" + #include "block/blockjob.h" + #include "block/coroutines.h" +-#include "block/throttle-groups.h" +-#include "hw/qdev-core.h" +-#include "sysemu/blockdev.h" +-#include "sysemu/runstate.h" +-#include "sysemu/replay.h" ++//#include "block/throttle-groups.h" ++//#include "hw/qdev-core.h" ++//#include "sysemu/blockdev.h" ++//#include "sysemu/runstate.h" ++//#include "sysemu/replay.h" + #include "qapi/error.h" +-#include "qapi/qapi-events-block.h" ++//#include "qapi/qapi-events-block.h" + #include "qemu/id.h" + #include "qemu/main-loop.h" + #include "qemu/option.h" +-#include "trace.h" +-#include "migration/misc.h" ++//#include "trace.h" ++//#include "migration/misc.h" + + /* Number of coroutines to reserve per attached device model */ + #define COROUTINE_POOL_RESERVATION 64 +@@ -48,7 +48,7 @@ struct BlockBackend { + DriveInfo *legacy_dinfo; /* null unless created by drive_new() */ + QTAILQ_ENTRY(BlockBackend) link; /* for block_backends */ + QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ +- BlockBackendPublic public; ++ //BlockBackendPublic public; + + DeviceState *dev; /* attached device model, if any */ + const BlockDevOps *dev_ops; +@@ -163,6 +163,7 @@ static const char *blk_root_get_name(BdrvChild *child) + return blk_name(child->opaque); + } + ++#if 0 + static void blk_vm_state_changed(void *opaque, bool running, RunState state) + { + Error *local_err = NULL; +@@ -179,6 +180,7 @@ static void blk_vm_state_changed(void *opaque, bool running, RunState state) + error_report_err(local_err); + } + } ++#endif + + /* + * Notifies the user of the BlockBackend that migration has completed. qdev +@@ -216,6 +218,7 @@ static void GRAPH_RDLOCK blk_root_activate(BdrvChild *child, Error **errp) + } + blk->shared_perm = saved_shared_perm; + ++#if 0 + if (runstate_check(RUN_STATE_INMIGRATE)) { + /* Activation can happen when migration process is still active, for + * example when nbd_server_add is called during non-shared storage +@@ -226,6 +229,7 @@ static void GRAPH_RDLOCK blk_root_activate(BdrvChild *child, Error **errp) + } + return; + } ++#endif + + blk_set_perm_locked(blk, blk->perm, blk->shared_perm, &local_err); + if (local_err) { +@@ -285,7 +289,7 @@ static void blk_root_attach(BdrvChild *child) + BlockBackend *blk = child->opaque; + BlockBackendAioNotifier *notifier; + +- trace_blk_root_attach(child, blk, child->bs); ++ //trace_blk_root_attach(child, blk, child->bs); + + QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { + bdrv_add_aio_context_notifier(child->bs, +@@ -300,7 +304,7 @@ static void blk_root_detach(BdrvChild *child) + BlockBackend *blk = child->opaque; + BlockBackendAioNotifier *notifier; + +- trace_blk_root_detach(child, blk, child->bs); ++ //trace_blk_root_detach(child, blk, child->bs); + + QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { + bdrv_remove_aio_context_notifier(child->bs, +@@ -480,16 +484,20 @@ static void blk_delete(BlockBackend *blk) + assert(!blk->refcnt); + assert(!blk->name); + assert(!blk->dev); ++#if 0 + if (blk->public.throttle_group_member.throttle_state) { + blk_io_limits_disable(blk); + } ++#endif + if (blk->root) { + blk_remove_bs(blk); + } ++#if 0 + if (blk->vmsh) { + qemu_del_vm_change_state_handler(blk->vmsh); + blk->vmsh = NULL; + } ++#endif + assert(QLIST_EMPTY(&blk->remove_bs_notifiers.notifiers)); + assert(QLIST_EMPTY(&blk->insert_bs_notifiers.notifiers)); + assert(QLIST_EMPTY(&blk->aio_notifiers)); +@@ -506,7 +514,7 @@ static void drive_info_del(DriveInfo *dinfo) + if (!dinfo) { + return; + } +- qemu_opts_del(dinfo->opts); ++ //qemu_opts_del(dinfo->opts); + g_free(dinfo); + } + +@@ -630,7 +638,7 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it) + * BDSes that are attached to a BlockBackend here; they have been handled + * by the above block already */ + do { +- it->bs = bdrv_next_monitor_owned(it->bs); ++ it->bs = NULL /*bdrv_next_monitor_owned(it->bs)*/; + bs = it->bs; + } while (bs && bdrv_has_blk(bs)); + +@@ -672,6 +680,7 @@ void bdrv_next_cleanup(BdrvNextIterator *it) + bdrv_next_reset(it); + } + ++#if 0 + /* + * Add a BlockBackend into the list of backends referenced by the monitor, with + * the given @name acting as the handle for the monitor. +@@ -724,6 +733,7 @@ void monitor_remove_blk(BlockBackend *blk) + g_free(blk->name); + blk->name = NULL; + } ++#endif + + /* + * Return @blk's name, a non-null string. +@@ -806,6 +816,7 @@ bool bdrv_is_root_node(BlockDriverState *bs) + return true; + } + ++#if 0 + /* + * Return @blk's DriveInfo if any, else null. + */ +@@ -861,18 +872,20 @@ BlockBackend *blk_by_public(BlockBackendPublic *public) + GLOBAL_STATE_CODE(); + return container_of(public, BlockBackend, public); + } ++#endif + + /* + * Disassociates the currently associated BlockDriverState from @blk. + */ + void blk_remove_bs(BlockBackend *blk) + { +- ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ //ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + BdrvChild *root; + + GLOBAL_STATE_CODE(); + + notifier_list_notify(&blk->remove_bs_notifiers, blk); ++#if 0 + if (tgm->throttle_state) { + BlockDriverState *bs = blk_bs(blk); + +@@ -887,6 +900,7 @@ void blk_remove_bs(BlockBackend *blk) + bdrv_drained_end(bs); + bdrv_unref(bs); + } ++#endif + + blk_update_root_state(blk); + +@@ -908,7 +922,7 @@ void blk_remove_bs(BlockBackend *blk) + */ + int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) + { +- ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ //ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + + GLOBAL_STATE_CODE(); + bdrv_ref(bs); +@@ -923,14 +937,17 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) + } + + notifier_list_notify(&blk->insert_bs_notifiers, blk); ++#if 0 + if (tgm->throttle_state) { + throttle_group_detach_aio_context(tgm); + throttle_group_attach_aio_context(tgm, bdrv_get_aio_context(bs)); + } ++#endif + + return 0; + } + ++#if 0 + /* + * Change BlockDriverState associated with @blk. + */ +@@ -939,6 +956,7 @@ int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp) + GLOBAL_STATE_CODE(); + return bdrv_replace_child_bs(blk->root, new_bs, errp); + } ++#endif + + /* + * Sets the permission bitmasks that the user of the BlockBackend needs. +@@ -972,6 +990,7 @@ int blk_set_perm(BlockBackend *blk, uint64_t perm, uint64_t shared_perm, + return blk_set_perm_locked(blk, perm, shared_perm, errp); + } + ++#if 0 + void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) + { + GLOBAL_STATE_CODE(); +@@ -1018,6 +1037,7 @@ void blk_detach_dev(BlockBackend *blk, DeviceState *dev) + blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort); + blk_unref(blk); + } ++#endif + + /* + * Return the device model attached to @blk if any, else null. +@@ -1032,18 +1052,19 @@ DeviceState *blk_get_attached_dev(BlockBackend *blk) + * device attached to the BlockBackend. */ + char *blk_get_attached_dev_id(BlockBackend *blk) + { +- DeviceState *dev = blk->dev; ++ //DeviceState *dev = blk->dev; + IO_CODE(); + +- if (!dev) { ++ //if (!dev) { + return g_strdup(""); +- } else if (dev->id) { +- return g_strdup(dev->id); +- } ++ //} else if (dev->id) { ++ // return g_strdup(dev->id); ++ //} + +- return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); ++ //return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); + } + ++#if 0 + /* + * Return the BlockBackend which has the device model @dev attached if it + * exists, else null. +@@ -1082,6 +1103,7 @@ void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, + ops->drained_begin(opaque); + } + } ++#endif + + /* + * Notify @blk's attached device model of media change. +@@ -1111,7 +1133,7 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) + + if (tray_was_open != tray_is_open) { + char *id = blk_get_attached_dev_id(blk); +- qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); ++ //qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); + g_free(id); + } + } +@@ -1190,12 +1212,14 @@ static void blk_root_resize(BdrvChild *child) + } + } + ++#if 0 + void blk_iostatus_enable(BlockBackend *blk) + { + GLOBAL_STATE_CODE(); + blk->iostatus_enabled = true; + blk->iostatus = BLOCK_DEVICE_IO_STATUS_OK; + } ++#endif + + /* The I/O status is only enabled if the drive explicitly + * enables it _and_ the VM is configured to stop on errors */ +@@ -1214,6 +1238,7 @@ BlockDeviceIoStatus blk_iostatus(const BlockBackend *blk) + return blk->iostatus; + } + ++#if 0 + void blk_iostatus_disable(BlockBackend *blk) + { + GLOBAL_STATE_CODE(); +@@ -1237,6 +1262,7 @@ void blk_iostatus_set_err(BlockBackend *blk, int error) + BLOCK_DEVICE_IO_STATUS_FAILED; + } + } ++#endif + + void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow) + { +@@ -1329,7 +1355,7 @@ blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, + + /* Call blk_bs() only after waiting, the graph may have changed */ + bs = blk_bs(blk); +- trace_blk_co_preadv(blk, bs, offset, bytes, flags); ++ //trace_blk_co_preadv(blk, bs, offset, bytes, flags); + + ret = blk_check_byte_request(blk, offset, bytes); + if (ret < 0) { +@@ -1338,11 +1364,13 @@ blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, + + bdrv_inc_in_flight(bs); + ++#if 0 + /* throttling disk I/O */ + if (blk->public.throttle_group_member.throttle_state) { + throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, + bytes, THROTTLE_READ); + } ++#endif + + ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset, + flags); +@@ -1404,7 +1432,7 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, + + /* Call blk_bs() only after waiting, the graph may have changed */ + bs = blk_bs(blk); +- trace_blk_co_pwritev(blk, bs, offset, bytes, flags); ++ //trace_blk_co_pwritev(blk, bs, offset, bytes, flags); + + ret = blk_check_byte_request(blk, offset, bytes); + if (ret < 0) { +@@ -1412,11 +1440,13 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, + } + + bdrv_inc_in_flight(bs); ++#if 0 + /* throttling disk I/O */ + if (blk->public.throttle_group_member.throttle_state) { + throttle_group_co_io_limits_intercept(&blk->public.throttle_group_member, + bytes, THROTTLE_WRITE); + } ++#endif + + if (!blk->enable_write_cache) { + flags |= BDRV_REQ_FUA; +@@ -1861,6 +1891,7 @@ int coroutine_fn blk_co_flush(BlockBackend *blk) + return ret; + } + ++#if 0 + static void coroutine_fn blk_aio_zone_report_entry(void *opaque) + { + BlkAioEmAIOCB *acb = opaque; +@@ -2058,6 +2089,7 @@ int coroutine_fn blk_co_zone_append(BlockBackend *blk, int64_t *offset, + blk_dec_in_flight(blk); + return ret; + } ++#endif + + void blk_drain(BlockBackend *blk) + { +@@ -2109,6 +2141,7 @@ BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read) + return is_read ? blk->on_read_error : blk->on_write_error; + } + ++#if 0 + BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, + int error) + { +@@ -2203,6 +2236,7 @@ bool blk_is_writable(BlockBackend *blk) + IO_CODE(); + return blk->perm & BLK_PERM_WRITE; + } ++#endif + + bool blk_is_sg(BlockBackend *blk) + { +@@ -2228,6 +2262,7 @@ void blk_set_enable_write_cache(BlockBackend *blk, bool wce) + blk->enable_write_cache = wce; + } + ++#if 0 + void blk_activate(BlockBackend *blk, Error **errp) + { + BlockDriverState *bs = blk_bs(blk); +@@ -2249,6 +2284,7 @@ void blk_activate(BlockBackend *blk, Error **errp) + bdrv_activate(bs, errp); + } + } ++#endif + + bool coroutine_fn blk_co_is_inserted(BlockBackend *blk) + { +@@ -2279,7 +2315,7 @@ void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked) + void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) + { + BlockDriverState *bs = blk_bs(blk); +- char *id; ++ //char *id; + IO_CODE(); + GRAPH_RDLOCK_GUARD(); + +@@ -2289,12 +2325,15 @@ void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag) + + /* Whether or not we ejected on the backend, + * the frontend experienced a tray event. */ ++#if 0 + id = blk_get_attached_dev_id(blk); + qapi_event_send_device_tray_moved(blk_name(blk), id, + eject_flag); + g_free(id); ++#endif + } + ++#if 0 + int blk_get_flags(BlockBackend *blk) + { + BlockDriverState *bs = blk_bs(blk); +@@ -2354,6 +2393,7 @@ int blk_get_max_iov(BlockBackend *blk) + IO_CODE(); + return blk->root->bs->bl.max_iov; + } ++#endif + + void *blk_try_blockalign(BlockBackend *blk, size_t size) + { +@@ -2367,6 +2407,7 @@ void *blk_blockalign(BlockBackend *blk, size_t size) + return qemu_blockalign(blk ? blk_bs(blk) : NULL, size); + } + ++#if 0 + bool blk_op_is_blocked(BlockBackend *blk, BlockOpType op, Error **errp) + { + BlockDriverState *bs = blk_bs(blk); +@@ -2409,6 +2450,7 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) + bdrv_op_unblock_all(bs, reason); + } + } ++#endif + + /** + * Return BB's current AioContext. Note that this context may change +@@ -2465,13 +2507,15 @@ static void blk_root_set_aio_ctx_commit(void *opaque) + BdrvStateBlkRootContext *s = opaque; + BlockBackend *blk = s->blk; + AioContext *new_context = s->new_ctx; +- ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ //ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + + qatomic_set(&blk->ctx, new_context); ++#if 0 + if (tgm->throttle_state) { + throttle_group_detach_aio_context(tgm); + throttle_group_attach_aio_context(tgm, new_context); + } ++#endif + } + + static TransactionActionDrv set_blk_root_context = { +@@ -2614,6 +2658,7 @@ int coroutine_fn blk_co_truncate(BlockBackend *blk, int64_t offset, bool exact, + return bdrv_co_truncate(blk->root, offset, exact, prealloc, flags, errp); + } + ++#if 0 + int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf, + int64_t pos, int size) + { +@@ -2645,6 +2690,7 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size) + + return bdrv_load_vmstate(blk_bs(blk), buf, pos, size); + } ++#endif + + int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz) + { +@@ -2681,6 +2727,7 @@ void blk_update_root_state(BlockBackend *blk) + blk->root_state.detect_zeroes = blk->root->bs->detect_zeroes; + } + ++#if 0 + /* + * Returns the detect-zeroes setting to be used for bdrv_open() of a + * BlockDriverState which is supposed to inherit the root state. +@@ -2780,11 +2827,12 @@ void blk_io_limits_update_group(BlockBackend *blk, const char *group) + blk_io_limits_disable(blk); + blk_io_limits_enable(blk, group); + } ++#endif + + static void blk_root_drained_begin(BdrvChild *child) + { + BlockBackend *blk = child->opaque; +- ThrottleGroupMember *tgm = &blk->public.throttle_group_member; ++ //ThrottleGroupMember *tgm = &blk->public.throttle_group_member; + + if (qatomic_fetch_inc(&blk->quiesce_counter) == 0) { + if (blk->dev_ops && blk->dev_ops->drained_begin) { +@@ -2795,9 +2843,11 @@ static void blk_root_drained_begin(BdrvChild *child) + /* Note that blk->root may not be accessible here yet if we are just + * attaching to a BlockDriverState that is drained. Use child instead. */ + ++#if 0 + if (qatomic_fetch_inc(&tgm->io_limits_disabled) == 0) { + throttle_group_restart_tgm(tgm); + } ++#endif + } + + static bool blk_root_drained_poll(BdrvChild *child) +@@ -2817,8 +2867,8 @@ static void blk_root_drained_end(BdrvChild *child) + BlockBackend *blk = child->opaque; + assert(qatomic_read(&blk->quiesce_counter)); + +- assert(blk->public.throttle_group_member.io_limits_disabled); +- qatomic_dec(&blk->public.throttle_group_member.io_limits_disabled); ++ //assert(blk->public.throttle_group_member.io_limits_disabled); ++ //qatomic_dec(&blk->public.throttle_group_member.io_limits_disabled); + + if (qatomic_fetch_dec(&blk->quiesce_counter) == 1) { + if (blk->dev_ops && blk->dev_ops->drained_end) { +diff --git a/qcow2/lib/block/block-gen.c b/qcow2/lib/block/block-gen.c +index ff2832fa..8d4f3294 100644 +--- a/qcow2/lib/block/block-gen.c ++++ b/qcow2/lib/block/block-gen.c +@@ -2190,6 +2190,7 @@ int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequ + } + + ++#if 0 + /* + * Wrappers for blk_co_zone_report + */ +@@ -2335,6 +2336,7 @@ int blk_zone_append(BlockBackend *blk, int64_t *offset, QEMUIOVector *qiov, Bdrv + return s.ret; + } + } ++#endif + + + /* +diff --git a/qcow2/lib/block/commit.c b/qcow2/lib/block/commit.c +index 7c3fdcb0..6b96a4c6 100644 +--- a/qcow2/lib/block/commit.c ++++ b/qcow2/lib/block/commit.c +@@ -14,7 +14,7 @@ + + #include "qemu/osdep.h" + #include "qemu/cutils.h" +-#include "trace.h" ++//#include "trace.h" + #include "block/block_int.h" + #include "block/blockjob_int.h" + #include "qapi/error.h" +@@ -170,7 +170,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp) + ret = blk_co_is_allocated_above(s->top, s->base_overlay, true, + offset, COMMIT_BUFFER_SIZE, &n); + copy = (ret > 0); +- trace_commit_one_iteration(s, offset, n, ret); ++ //trace_commit_one_iteration(s, offset, n, ret); + if (copy) { + assert(n < SIZE_MAX); + +@@ -414,7 +414,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, + s->backing_mask_protocol = backing_mask_protocol; + s->on_error = on_error; + +- trace_commit_start(bs, base, top, s); ++ //trace_commit_start(bs, base, top, s); + job_start(&s->common.job); + return; + +diff --git a/qcow2/lib/block/dirty-bitmap.c b/qcow2/lib/block/dirty-bitmap.c +index 13a19797..df6a3c30 100644 +--- a/qcow2/lib/block/dirty-bitmap.c ++++ b/qcow2/lib/block/dirty-bitmap.c +@@ -23,7 +23,7 @@ + */ + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "trace.h" ++//#include "trace.h" + #include "block/block-io.h" + #include "block/block_int.h" + #include "block/blockjob.h" +diff --git a/qcow2/lib/block/file-posix.c b/qcow2/lib/block/file-posix.c +index ff928b5e..852f0222 100644 +--- a/qcow2/lib/block/file-posix.c ++++ b/qcow2/lib/block/file-posix.c +@@ -32,15 +32,15 @@ + #include "qemu/option.h" + #include "qemu/units.h" + #include "qemu/memalign.h" +-#include "trace.h" ++//#include "trace.h" + #include "block/thread-pool.h" + #include "qemu/iov.h" + #include "block/raw-aio.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qstring.h" + +-#include "scsi/pr-manager.h" +-#include "scsi/constants.h" ++//#include "scsi/pr-manager.h" ++//#include "scsi/constants.h" + + #if defined(__APPLE__) && (__MACH__) + #include +@@ -173,7 +173,7 @@ typedef struct BDRVRawState { + uint64_t discard_bytes_ok; + } stats; + +- PRManager *pr_mgr; ++ //PRManager *pr_mgr; + } BDRVRawState; + + typedef struct BDRVRawReopenState { +@@ -317,6 +317,7 @@ static int probe_logical_blocksize(int fd, unsigned int *sector_size_p) + return success ? 0 : -errno; + } + ++#if defined(HAVE_HOST_BLOCK_DEVICE) + /** + * Get physical block size of @fd. + * On success, store it in @blk_size and return 0. +@@ -333,6 +334,7 @@ static int probe_physical_blocksize(int fd, unsigned int *blk_size) + return -ENOTSUP; + #endif + } ++#endif + + /* + * Returns true if no alignment restrictions are necessary even for files +@@ -597,7 +599,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + QemuOpts *opts; + Error *local_err = NULL; + const char *filename = NULL; +- const char *str; ++ //const char *str; + BlockdevAioOptions aio, aio_default; + int fd, ret; + struct stat st; +@@ -670,6 +672,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + abort(); + } + ++#if 0 + str = qemu_opt_get(opts, "pr-manager"); + if (str) { + s->pr_mgr = pr_manager_lookup(str, &local_err); +@@ -679,6 +682,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + goto fail; + } + } ++#endif + + s->drop_cache = qemu_opt_get_bool(opts, "drop-cache", true); + s->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", +@@ -1522,6 +1526,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) + raw_refresh_zoned_limits(bs, &st, errp); + } + ++#if defined(HAVE_HOST_BLOCK_DEVICE) + static int check_for_dasd(int fd) + { + #ifdef BIODASDINFO2 +@@ -1597,8 +1602,9 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) + return -ENOTSUP; + } + #endif ++#endif + +-#if defined(__linux__) ++#if defined(__linux__) && defined(HAVE_HOST_BLOCK_DEVICE) + static int handle_aiocb_ioctl(void *opaque) + { + RawPosixAIOData *aiocb = opaque; +@@ -1627,7 +1633,7 @@ static int handle_aiocb_flush(void *opaque) + + ret = qemu_fdatasync(aiocb->aio_fildes); + if (ret == -1) { +- trace_file_flush_fdatasync_failed(errno); ++ //trace_file_flush_fdatasync_failed(errno); + + /* There is no clear definition of the semantics of a failing fsync(), + * so we may have to assume the worst. The sad truth is that this +@@ -2168,9 +2174,9 @@ static int handle_aiocb_copy_range(void *opaque) + ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, + aiocb->copy_range.aio_fd2, &out_off, + bytes, 0); +- trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, +- aiocb->copy_range.aio_fd2, out_off, bytes, +- 0, ret); ++ //trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, ++ // aiocb->copy_range.aio_fd2, out_off, bytes, ++ // 0, ret); + if (ret == 0) { + /* No progress (e.g. when beyond EOF), let the caller fall back to + * buffer I/O. */ +@@ -2546,8 +2552,8 @@ out: + if (!BDRV_ZT_IS_CONV(*wp)) { + if (type & QEMU_AIO_ZONE_APPEND) { + *offset_ptr = *wp; +- trace_zbd_zone_append_complete(bs, *offset_ptr +- >> BDRV_SECTOR_BITS); ++ //trace_zbd_zone_append_complete(bs, *offset_ptr ++ // >> BDRV_SECTOR_BITS); + } + /* Advance the wp if needed */ + if (offset + bytes > *wp) { +@@ -3404,7 +3410,7 @@ static int coroutine_fn raw_co_zone_report(BlockDriverState *bs, int64_t offset, + }, + }; + +- trace_zbd_zone_report(bs, *nr_zones, offset >> BDRV_SECTOR_BITS); ++ //trace_zbd_zone_report(bs, *nr_zones, offset >> BDRV_SECTOR_BITS); + return raw_thread_pool_submit(handle_aiocb_zone_report, &acb); + } + #endif +@@ -3480,8 +3486,8 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, + }, + }; + +- trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, +- len >> BDRV_SECTOR_BITS); ++ //trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, ++ // len >> BDRV_SECTOR_BITS); + ret = raw_thread_pool_submit(handle_aiocb_zone_mgmt, &acb); + if (ret != 0) { + update_zones_wp(bs, s->fd, offset, nrz); +@@ -3539,7 +3545,7 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, + len += iov_len; + } + +- trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); ++ //trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); + return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND); + } + #endif +@@ -3962,7 +3968,7 @@ static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) + + /* If a match was found, leave the loop */ + if (*mediaIterator != 0) { +- trace_file_FindEjectableOpticalMedia(matching_array[index]); ++ //trace_file_FindEjectableOpticalMedia(matching_array[index]); + mediaType = g_strdup(matching_array[index]); + break; + } +@@ -4022,7 +4028,7 @@ static bool setup_cdrom(char *bsd_path, Error **errp) + if (partition_found == false) { + error_setg(errp, "Failed to find a working partition on disc"); + } else { +- trace_file_setup_cdrom(test_partition); ++ //trace_file_setup_cdrom(test_partition); + pstrcpy(bsd_path, MAXPATHLEN, test_partition); + } + return partition_found; +@@ -4084,7 +4090,7 @@ static bool hdev_is_sg(BlockDriverState *bs) + + ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); + if (ret >= 0) { +- trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); ++ //trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); + return true; + } + +@@ -4194,6 +4200,7 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) + return ret; + } + ++#if 0 + if (req == SG_IO && s->pr_mgr) { + struct sg_io_hdr *io_hdr = buf; + if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT || +@@ -4202,6 +4209,7 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) + s->fd, io_hdr); + } + } ++#endif + + acb = (RawPosixAIOData) { + .bs = bs, +diff --git a/qcow2/lib/block/io.c b/qcow2/lib/block/io.c +index 301514c8..ae2adc51 100644 +--- a/qcow2/lib/block/io.c ++++ b/qcow2/lib/block/io.c +@@ -23,21 +23,23 @@ + */ + + #include "qemu/osdep.h" +-#include "trace.h" ++//#include "trace.h" + #include "sysemu/block-backend.h" + #include "block/aio-wait.h" + #include "block/blockjob.h" +-#include "block/blockjob_int.h" ++//#include "block/blockjob_int.h" + #include "block/block_int.h" + #include "block/coroutines.h" + #include "block/dirty-bitmap.h" +-#include "block/write-threshold.h" ++//#include "block/write-threshold.h" + #include "qemu/cutils.h" + #include "qemu/memalign.h" + #include "qapi/error.h" + #include "qemu/error-report.h" + #include "qemu/main-loop.h" +-#include "sysemu/replay.h" ++//#include "sysemu/replay.h" ++ ++#define replay_events_enabled() 0 + + /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ + #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) +@@ -1209,8 +1211,8 @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, + bdrv_round_to_subclusters(bs, offset, bytes, &align_offset, &align_bytes); + skip_bytes = offset - align_offset; + +- trace_bdrv_co_do_copy_on_readv(bs, offset, bytes, +- align_offset, align_bytes); ++ //trace_bdrv_co_do_copy_on_readv(bs, offset, bytes, ++ // align_offset, align_bytes); + + while (align_bytes) { + int64_t pnum; +@@ -1263,7 +1265,7 @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, + goto err; + } + +- bdrv_co_debug_event(bs, BLKDBG_COR_WRITE); ++ //bdrv_co_debug_event(bs, BLKDBG_COR_WRITE); + if (drv->bdrv_co_pwrite_zeroes && + buffer_is_zero(bounce_buffer, pnum)) { + /* FIXME: Should we (perhaps conditionally) be setting +@@ -1524,10 +1526,10 @@ bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + qemu_iovec_init_buf(&local_qiov, pad->buf, bytes); + + if (pad->head) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); + } + if (pad->merge_reads && pad->tail) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + } + ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes, + align, &local_qiov, 0, 0); +@@ -1535,10 +1537,10 @@ bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + return ret; + } + if (pad->head) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD); + } + if (pad->merge_reads && pad->tail) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + } + + if (pad->merge_reads) { +@@ -1549,7 +1551,7 @@ bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + if (pad->tail) { + qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align); + +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); + ret = bdrv_aligned_preadv( + child, req, + req->overlap_offset + req->overlap_bytes - align, +@@ -1557,7 +1559,7 @@ bdrv_padding_rmw_read(BdrvChild *child, BdrvTrackedRequest *req, + if (ret < 0) { + return ret; + } +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL); + } + + zero_mem: +@@ -1779,7 +1781,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, + int ret; + IO_CODE(); + +- trace_bdrv_co_preadv_part(bs, offset, bytes, flags); ++ //trace_bdrv_co_preadv_part(bs, offset, bytes, flags); + + if (!bdrv_co_is_inserted(bs)) { + return -ENOMEDIUM; +@@ -2001,7 +2003,7 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, + } else { + assert(child->perm & BLK_PERM_WRITE); + } +- bdrv_write_threshold_check_write(bs, offset, bytes); ++ //bdrv_write_threshold_check_write(bs, offset, bytes); + return 0; + case BDRV_TRACKED_TRUNCATE: + assert(child->perm & BLK_PERM_RESIZE); +@@ -2101,16 +2103,16 @@ bdrv_aligned_pwritev(BdrvChild *child, BdrvTrackedRequest *req, + if (ret < 0) { + /* Do nothing, write notifier decided to fail this request */ + } else if (flags & BDRV_REQ_ZERO_WRITE) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO); + ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags); + } else if (flags & BDRV_REQ_WRITE_COMPRESSED) { + ret = bdrv_driver_pwritev_compressed(bs, offset, bytes, + qiov, qiov_offset); + } else if (bytes <= max_transfer) { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV); + ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags); + } else { +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV); + while (bytes_remaining) { + int num = MIN(bytes_remaining, max_transfer); + int local_flags = flags; +@@ -2133,7 +2135,7 @@ bdrv_aligned_pwritev(BdrvChild *child, BdrvTrackedRequest *req, + bytes_remaining -= num; + } + } +- bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE); ++ //bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE); + + if (ret >= 0) { + ret = 0; +@@ -2233,7 +2235,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, + bool padded = false; + IO_CODE(); + +- trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); ++ //trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); + + if (!bdrv_co_is_inserted(bs)) { + return -ENOMEDIUM; +@@ -2317,13 +2319,14 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, + int64_t bytes, BdrvRequestFlags flags) + { + IO_CODE(); +- trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); ++ //trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); + assert_bdrv_graph_readable(); + + return bdrv_co_pwritev(child, offset, bytes, NULL, + BDRV_REQ_ZERO_WRITE | flags); + } + ++#if 0 + /* + * Flush ALL BDSes regardless of if they are reachable via a BlkBackend or not. + */ +@@ -2354,6 +2357,7 @@ int bdrv_flush_all(void) + + return result; + } ++#endif + + /* + * Returns the allocation status of the specified sectors. +@@ -2892,6 +2896,7 @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) + return ret; + } + ++#if 0 + int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, + int64_t pos, int size) + { +@@ -2911,6 +2916,7 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, + + return ret < 0 ? ret : size; + } ++#endif + + /**************************************************************/ + /* async I/Os */ +@@ -3221,6 +3227,7 @@ out: + return co.ret; + } + ++#if 0 + int coroutine_fn bdrv_co_zone_report(BlockDriverState *bs, int64_t offset, + unsigned int *nr_zones, + BlockZoneDescriptor *zones) +@@ -3288,6 +3295,7 @@ out: + bdrv_dec_in_flight(bs); + return co.ret; + } ++#endif + + void *qemu_blockalign(BlockDriverState *bs, size_t size) + { +@@ -3478,8 +3486,8 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, + { + IO_CODE(); + assert_bdrv_graph_readable(); +- trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, +- read_flags, write_flags); ++ //trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, ++ // read_flags, write_flags); + return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, + bytes, read_flags, write_flags, true); + } +@@ -3496,8 +3504,8 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, + { + IO_CODE(); + assert_bdrv_graph_readable(); +- trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, +- read_flags, write_flags); ++ //trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, ++ // read_flags, write_flags); + return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, + bytes, read_flags, write_flags, false); + } +@@ -3679,6 +3687,7 @@ void bdrv_cancel_in_flight(BlockDriverState *bs) + } + } + ++#if 0 + int coroutine_fn + bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, size_t qiov_offset) +@@ -3753,3 +3762,4 @@ bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes) + + return ret; + } ++#endif +diff --git a/qcow2/lib/block/mirror.c b/qcow2/lib/block/mirror.c +index 61f0a717..4b75f105 100644 +--- a/qcow2/lib/block/mirror.c ++++ b/qcow2/lib/block/mirror.c +@@ -15,7 +15,7 @@ + #include "qemu/cutils.h" + #include "qemu/coroutine.h" + #include "qemu/range.h" +-#include "trace.h" ++//#include "trace.h" + #include "block/blockjob_int.h" + #include "block/block_int.h" + #include "block/dirty-bitmap.h" +@@ -201,7 +201,9 @@ static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) + int64_t chunk_num; + int i, nb_chunks; + ++#if 0 + trace_mirror_iteration_done(s, op->offset, op->bytes, ret); ++#endif + + s->in_flight--; + s->bytes_in_flight -= op->bytes; +@@ -375,7 +377,9 @@ static void coroutine_fn mirror_co_read(void *opaque) + nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); + + while (s->buf_free_count < nb_chunks) { ++#if 0 + trace_mirror_yield_in_flight(s, op->offset, s->in_flight); ++#endif + mirror_wait_for_free_in_flight_slot(s); + } + +@@ -396,7 +400,9 @@ static void coroutine_fn mirror_co_read(void *opaque) + s->in_flight++; + s->bytes_in_flight += op->bytes; + op->is_in_flight = true; ++#if 0 + trace_mirror_one_iteration(s, op->offset, op->bytes); ++#endif + + WITH_GRAPH_RDLOCK_GUARD() { + ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes, +@@ -499,7 +505,9 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) + if (offset < 0) { + bdrv_set_dirty_iter(s->dbi, 0); + offset = bdrv_dirty_iter_next(s->dbi); ++#if 0 + trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); ++#endif + assert(offset >= 0); + } + bdrv_dirty_bitmap_unlock(s->dirty_bitmap); +@@ -601,7 +609,9 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) + } + + while (s->in_flight >= MAX_IN_FLIGHT) { ++#if 0 + trace_mirror_yield_in_flight(s, offset, s->in_flight); ++#endif + mirror_wait_for_free_in_flight_slot(s); + } + +@@ -867,8 +877,10 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) + } + + if (s->in_flight >= MAX_IN_FLIGHT) { ++#if 0 + trace_mirror_yield(s, UINT64_MAX, s->buf_free_count, + s->in_flight); ++#endif + mirror_wait_for_free_in_flight_slot(s); + continue; + } +@@ -1071,7 +1083,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) + iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || + (cnt == 0 && s->in_flight > 0)) { ++#if 0 + trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); ++#endif + mirror_wait_for_free_in_flight_slot(s); + continue; + } else if (cnt != 0) { +@@ -1081,7 +1095,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) + + should_complete = false; + if (s->in_flight == 0 && cnt == 0) { ++#if 0 + trace_mirror_before_flush(s); ++#endif + if (!job_is_ready(&s->common.job)) { + if (mirror_flush(s) < 0) { + /* Go check s->ret. */ +@@ -1114,7 +1130,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) + * whether to switch to target check one last time if I/O has + * come in the meanwhile, and if not flush the data to disk. + */ ++#if 0 + trace_mirror_before_drain(s, cnt); ++#endif + + s->in_drain = true; + bdrv_drained_begin(bs); +@@ -1139,8 +1157,10 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) + + if (job_is_ready(&s->common.job) && !should_complete) { + if (s->in_flight == 0 && cnt == 0) { ++#if 0 + trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), + BLOCK_JOB_SLICE_TIME); ++#endif + job_sleep_ns(&s->common.job, BLOCK_JOB_SLICE_TIME); + } + } else { +@@ -1235,6 +1255,7 @@ static bool mirror_drained_poll(BlockJob *job) + return !!s->in_flight; + } + ++#if 0 + static bool mirror_cancel(Job *job, bool force) + { + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); +@@ -1251,6 +1272,7 @@ static bool mirror_cancel(Job *job, bool force) + } + return force; + } ++#endif + + static bool commit_active_cancel(Job *job, bool force) + { +@@ -1258,6 +1280,7 @@ static bool commit_active_cancel(Job *job, bool force) + return force || !job_is_ready(job); + } + ++#if 0 + static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) + { +@@ -1317,6 +1340,7 @@ static const BlockJobDriver mirror_job_driver = { + .change = mirror_change, + .query = mirror_query, + }; ++#endif + + static const BlockJobDriver commit_active_job_driver = { + .job_driver = { +@@ -1969,7 +1993,7 @@ static BlockJob *mirror_start_job( + + QTAILQ_INIT(&s->ops_in_flight); + +- trace_mirror_start(bs, s, opaque); ++ //trace_mirror_start(bs, s, opaque); + job_start(&s->common.job); + + return &s->common; +@@ -2004,6 +2028,7 @@ fail: + return NULL; + } + ++#if 0 + void mirror_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *target, const char *replaces, + int creation_flags, int64_t speed, +@@ -2038,6 +2063,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, + &mirror_job_driver, is_none_mode, base, false, + filter_node_name, true, copy_mode, false, errp); + } ++#endif + + BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, + BlockDriverState *base, int creation_flags, +diff --git a/qcow2/lib/block/nbd.c b/qcow2/lib/block/nbd.c +index 2c65cdcf..2710edce 100644 +--- a/qcow2/lib/block/nbd.c ++++ b/qcow2/lib/block/nbd.c +@@ -30,7 +30,7 @@ + + #include "qemu/osdep.h" + +-#include "trace.h" ++//#include "trace.h" + #include "qemu/uri.h" + #include "qemu/option.h" + #include "qemu/cutils.h" +@@ -311,7 +311,7 @@ nbd_handle_updated_info(BlockDriverState *bs, Error **errp) + } + } + +- trace_nbd_client_handshake_success(s->export); ++ //trace_nbd_client_handshake_success(s->export); + + return 0; + } +@@ -373,7 +373,7 @@ static bool nbd_client_connecting(BDRVNBDState *s) + /* Called with s->requests_lock taken. */ + static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) + { +- int ret; ++ //int ret; + bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT; + + /* +@@ -383,7 +383,7 @@ static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) + assert(nbd_client_connecting(s)); + assert(s->in_flight == 1); + +- trace_nbd_reconnect_attempt(s->bs->in_flight); ++ //trace_nbd_reconnect_attempt(s->bs->in_flight); + + if (blocking && !s->reconnect_delay_timer) { + /* +@@ -405,8 +405,9 @@ static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) + } + + qemu_mutex_unlock(&s->requests_lock); +- ret = nbd_co_do_establish_connection(s->bs, blocking, NULL); +- trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight); ++ nbd_co_do_establish_connection(s->bs, blocking, NULL); ++ //ret = nbd_co_do_establish_connection(s->bs, blocking, NULL); ++ //trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight); + qemu_mutex_lock(&s->requests_lock); + + /* +@@ -601,7 +602,7 @@ static int nbd_parse_offset_hole_payload(BDRVNBDState *s, + } + if (s->info.min_block && + !QEMU_IS_ALIGNED(hole_size, s->info.min_block)) { +- trace_nbd_structured_read_compliance("hole"); ++ //trace_nbd_structured_read_compliance("hole"); + } + + qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size); +@@ -671,7 +672,7 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, + */ + if (s->info.min_block && !QEMU_IS_ALIGNED(extent->length, + s->info.min_block)) { +- trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); ++ //trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); + if (extent->length > s->info.min_block) { + extent->length = QEMU_ALIGN_DOWN(extent->length, + s->info.min_block); +@@ -692,11 +693,11 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, + * the length of our request. + */ + if (count != wide || chunk->length > pay_len) { +- trace_nbd_parse_blockstatus_compliance("unexpected extent count"); ++ //trace_nbd_parse_blockstatus_compliance("unexpected extent count"); + } + if (extent->length > orig_length) { + extent->length = orig_length; +- trace_nbd_parse_blockstatus_compliance("extent length too large"); ++ //trace_nbd_parse_blockstatus_compliance("extent length too large"); + } + + /* +@@ -786,7 +787,7 @@ nbd_co_receive_offset_data_payload(BDRVNBDState *s, uint64_t orig_offset, + return -EINVAL; + } + if (s->info.min_block && !QEMU_IS_ALIGNED(data_size, s->info.min_block)) { +- trace_nbd_structured_read_compliance("data"); ++ //trace_nbd_structured_read_compliance("data"); + } + + qemu_iovec_init(&sub_qiov, qiov->niov); +@@ -1161,7 +1162,7 @@ nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, + case NBD_REPLY_TYPE_BLOCK_STATUS: + wide = chunk->type == NBD_REPLY_TYPE_BLOCK_STATUS_EXT; + if ((s->info.mode >= NBD_MODE_EXTENDED) != wide) { +- trace_nbd_extended_headers_compliance("block_status"); ++ //trace_nbd_extended_headers_compliance("block_status"); + } + if (received) { + nbd_channel_error(s, -EINVAL); +@@ -1228,11 +1229,13 @@ nbd_co_request(BlockDriverState *bs, NBDRequest *request, + ret = nbd_co_receive_return_code(s, request->cookie, + &request_ret, &local_err); + if (local_err) { ++#if 0 + trace_nbd_co_request_fail(request->from, request->len, + request->cookie, request->flags, + request->type, + nbd_cmd_lookup(request->type), + ret, error_get_pretty(local_err)); ++#endif + error_free(local_err); + local_err = NULL; + } +@@ -1287,10 +1290,12 @@ nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + ret = nbd_co_receive_cmdread_reply(s, request.cookie, offset, qiov, + &request_ret, &local_err); + if (local_err) { ++#if 0 + trace_nbd_co_request_fail(request.from, request.len, request.cookie, + request.flags, request.type, + nbd_cmd_lookup(request.type), + ret, error_get_pretty(local_err)); ++#endif + error_free(local_err); + local_err = NULL; + } +@@ -1451,10 +1456,12 @@ static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( + &extent, &request_ret, + &local_err); + if (local_err) { ++#if 0 + trace_nbd_co_request_fail(request.from, request.len, request.cookie, + request.flags, request.type, + nbd_cmd_lookup(request.type), + ret, error_get_pretty(local_err)); ++#endif + error_free(local_err); + local_err = NULL; + } +diff --git a/qcow2/lib/block/qapi.c b/qcow2/lib/block/qapi.c +index 2b5793f1..82e12895 100644 +--- a/qcow2/lib/block/qapi.c ++++ b/qcow2/lib/block/qapi.c +@@ -27,8 +27,8 @@ + #include "block/qapi.h" + #include "block/block_int.h" + #include "block/dirty-bitmap.h" +-#include "block/throttle-groups.h" +-#include "block/write-threshold.h" ++//#include "block/throttle-groups.h" ++//#include "block/write-threshold.h" + #include "qapi/error.h" + #include "qapi/qapi-commands-block-core.h" + #include "qapi/qobject-output-visitor.h" +@@ -88,6 +88,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, + + info->detect_zeroes = bs->detect_zeroes; + ++#if 0 + if (blk && blk_get_public(blk)->throttle_group_member.throttle_state) { + ThrottleConfig cfg; + BlockBackendPublic *blkp = blk_get_public(blk); +@@ -144,6 +145,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, + } + + info->write_threshold = bdrv_write_threshold_get(bs); ++#endif + + p_image_info = &info->image; + info->backing_file_depth = 0; +diff --git a/qcow2/lib/block/raw-format.c b/qcow2/lib/block/raw-format.c +index ac7e8495..f3343e98 100644 +--- a/qcow2/lib/block/raw-format.c ++++ b/qcow2/lib/block/raw-format.c +@@ -319,6 +319,7 @@ raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) + return bdrv_co_pdiscard(bs->file, offset, bytes); + } + ++#if 0 + static int coroutine_fn GRAPH_RDLOCK + raw_co_zone_report(BlockDriverState *bs, int64_t offset, + unsigned int *nr_zones, +@@ -340,6 +341,7 @@ raw_co_zone_append(BlockDriverState *bs,int64_t *offset, QEMUIOVector *qiov, + { + return bdrv_co_zone_append(bs->file->bs, offset, qiov, flags); + } ++#endif + + static int64_t coroutine_fn GRAPH_RDLOCK + raw_co_getlength(BlockDriverState *bs) +@@ -650,9 +652,11 @@ BlockDriver bdrv_raw = { + .bdrv_co_pwritev = &raw_co_pwritev, + .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, + .bdrv_co_pdiscard = &raw_co_pdiscard, ++#if 0 + .bdrv_co_zone_report = &raw_co_zone_report, + .bdrv_co_zone_mgmt = &raw_co_zone_mgmt, + .bdrv_co_zone_append = &raw_co_zone_append, ++#endif + .bdrv_co_block_status = &raw_co_block_status, + .bdrv_co_copy_range_from = &raw_co_copy_range_from, + .bdrv_co_copy_range_to = &raw_co_copy_range_to, +diff --git a/qcow2/lib/blockdev.c b/qcow2/lib/blockdev.c +index 835064ed..53279249 100644 +--- a/qcow2/lib/blockdev.c ++++ b/qcow2/lib/blockdev.c +@@ -37,14 +37,14 @@ + #include "block/blockjob.h" + #include "block/dirty-bitmap.h" + #include "block/qdict.h" +-#include "block/throttle-groups.h" +-#include "monitor/monitor.h" ++//#include "block/throttle-groups.h" ++//#include "monitor/monitor.h" + #include "qemu/error-report.h" + #include "qemu/option.h" + #include "qemu/qemu-print.h" + #include "qemu/config-file.h" + #include "qapi/qapi-commands-block.h" +-#include "qapi/qapi-commands-transaction.h" ++//#include "qapi/qapi-commands-transaction.h" + #include "qapi/qapi-visit-block-core.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qnum.h" +@@ -53,17 +53,18 @@ + #include "qapi/qmp/qerror.h" + #include "qapi/qmp/qlist.h" + #include "qapi/qobject-output-visitor.h" +-#include "sysemu/sysemu.h" +-#include "sysemu/iothread.h" ++//#include "sysemu/sysemu.h" ++//#include "sysemu/iothread.h" + #include "block/block_int.h" +-#include "block/trace.h" +-#include "sysemu/runstate.h" +-#include "sysemu/replay.h" ++//#include "block/trace.h" ++//#include "sysemu/runstate.h" ++//#include "sysemu/replay.h" + #include "qemu/cutils.h" + #include "qemu/help_option.h" + #include "qemu/main-loop.h" +-#include "qemu/throttle-options.h" ++//#include "qemu/throttle-options.h" + ++#if 0 + /* Protected by BQL */ + QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = + QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); +@@ -1035,6 +1036,7 @@ fail: + qobject_unref(bs_opts); + return dinfo; + } ++#endif + + static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) + { +@@ -1060,6 +1062,7 @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp) + return bs; + } + ++#if 0 + static void blockdev_do_action(TransactionAction *action, Error **errp) + { + TransactionActionList list; +@@ -2422,6 +2425,7 @@ void qmp_block_stream(const char *job_id, const char *device, + out_rdlock: + bdrv_graph_rdunlock_main_loop(); + } ++#endif + + void qmp_block_commit(const char *job_id, const char *device, + const char *base_node, +@@ -2614,6 +2618,7 @@ void qmp_block_commit(const char *job_id, const char *device, + } + } + ++#if 0 + /* Common QMP interface for drive-backup and blockdev-backup */ + static BlockJob *do_backup_common(BackupCommon *backup, + BlockDriverState *bs, +@@ -3510,6 +3515,7 @@ void qmp_x_blockdev_change(const char *parent, const char *child, + out: + bdrv_graph_wrunlock(); + } ++#endif + + BlockJobInfoList *qmp_query_block_jobs(Error **errp) + { +@@ -3536,6 +3542,7 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp) + return head; + } + ++#if 0 + void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, + bool has_force, bool force, Error **errp) + { +@@ -3647,3 +3654,4 @@ QemuOptsList qemu_drive_opts = { + { /* end of list */ } + }, + }; ++#endif +diff --git a/qcow2/lib/blockjob.c b/qcow2/lib/blockjob.c +index d5f29e14..b739a1ca 100644 +--- a/qcow2/lib/blockjob.c ++++ b/qcow2/lib/blockjob.c +@@ -28,7 +28,7 @@ + #include "block/block.h" + #include "block/blockjob_int.h" + #include "block/block_int.h" +-#include "block/trace.h" ++//#include "block/trace.h" + #include "sysemu/block-backend.h" + #include "qapi/error.h" + #include "qapi/qapi-events-block-core.h" +@@ -213,6 +213,7 @@ void block_job_remove_all_bdrv(BlockJob *job) + bdrv_graph_wrunlock(); + } + ++#if 0 + bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) + { + GSList *el; +@@ -227,6 +228,7 @@ bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) + + return false; + } ++#endif + + int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, + uint64_t perm, uint64_t shared_perm, Error **errp) +@@ -312,6 +314,7 @@ static bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) + return block_job_set_speed_locked(job, speed, errp); + } + ++#if 0 + void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + Error **errp) + { +@@ -331,6 +334,7 @@ void block_job_change_locked(BlockJob *job, BlockJobChangeOptions *opts, + error_setg(errp, "Job type does not support change"); + } + } ++#endif + + void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) + { +diff --git a/qcow2/lib/crypto/hash.c b/qcow2/lib/crypto/hash.c +index b0f8228b..a282cf74 100644 +--- a/qcow2/lib/crypto/hash.c ++++ b/qcow2/lib/crypto/hash.c +@@ -18,20 +18,92 @@ + * + */ + ++#include ++ + #include "qemu/osdep.h" + #include "crypto/hash.h" +-#include "hashpriv.h" ++//#include "hashpriv.h" + + static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { +- [QCRYPTO_HASH_ALG_MD5] = 16, +- [QCRYPTO_HASH_ALG_SHA1] = 20, +- [QCRYPTO_HASH_ALG_SHA224] = 28, +- [QCRYPTO_HASH_ALG_SHA256] = 32, +- [QCRYPTO_HASH_ALG_SHA384] = 48, +- [QCRYPTO_HASH_ALG_SHA512] = 64, +- [QCRYPTO_HASH_ALG_RIPEMD160] = 20, ++ [QCRYPTO_HASH_ALG_MD5] = QCRYPTO_HASH_DIGEST_LEN_MD5, ++ [QCRYPTO_HASH_ALG_SHA1] = QCRYPTO_HASH_DIGEST_LEN_SHA1, ++ [QCRYPTO_HASH_ALG_SHA224] = QCRYPTO_HASH_DIGEST_LEN_SHA224, ++ [QCRYPTO_HASH_ALG_SHA256] = QCRYPTO_HASH_DIGEST_LEN_SHA256, ++ [QCRYPTO_HASH_ALG_SHA384] = QCRYPTO_HASH_DIGEST_LEN_SHA384, ++ [QCRYPTO_HASH_ALG_SHA512] = QCRYPTO_HASH_DIGEST_LEN_SHA512, ++ [QCRYPTO_HASH_ALG_RIPEMD160] = QCRYPTO_HASH_DIGEST_LEN_RIPEMD160, ++}; ++ ++static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { ++ [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, ++ [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, ++ [QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224, ++ [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, ++ [QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384, ++ [QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512, ++ [QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160, + }; + ++gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) ++{ ++ size_t i; ++ const gnutls_digest_algorithm_t *algs; ++ if (alg >= QCRYPTO_HASH_ALG__MAX || ++ qcrypto_hash_alg_map[alg] == GNUTLS_DIG_UNKNOWN) { ++ return false; ++ } ++ algs = gnutls_digest_list(); ++ for (i = 0; algs[i] != GNUTLS_DIG_UNKNOWN; i++) { ++ if (algs[i] == qcrypto_hash_alg_map[alg]) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++static int ++qcrypto_gnutls_hash_bytesv(QCryptoHashAlgorithm alg, ++ const struct iovec *iov, ++ size_t niov, ++ uint8_t **result, ++ size_t *resultlen, ++ Error **errp) ++{ ++ int i, ret; ++ gnutls_hash_hd_t hash; ++ ++ if (!qcrypto_hash_supports(alg)) { ++ fprintf(stderr, "Unknown hash algorithm %d.\n", alg); ++ return -1; ++ } ++ ++ ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]); ++ if (*resultlen == 0) { ++ *resultlen = ret; ++ *result = calloc(sizeof(uint8_t), *resultlen); ++ } else if (*resultlen != ret) { ++ fprintf(stderr, ++ "Result buffer size %zu is smaller than hash %d.\n", ++ *resultlen, ret); ++ return -1; ++ } ++ ++ ret = gnutls_hash_init(&hash, qcrypto_hash_alg_map[alg]); ++ if (ret < 0) { ++ fprintf(stderr, ++ "Unable to initialize hash algorithm: %s.\n", ++ gnutls_strerror(ret)); ++ return -1; ++ } ++ ++ for (i = 0; i < niov; i++) { ++ gnutls_hash(hash, iov[i].iov_base, iov[i].iov_len); ++ } ++ ++ gnutls_hash_deinit(hash, *result); ++ return 0; ++} ++ + size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) + { + assert(alg < G_N_ELEMENTS(qcrypto_hash_alg_size)); +@@ -59,9 +131,9 @@ int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, + } + #endif + +- return qcrypto_hash_lib_driver.hash_bytesv(alg, iov, niov, +- result, resultlen, +- errp); ++ return qcrypto_gnutls_hash_bytesv(alg, iov, niov, ++ result, resultlen, ++ errp); + } + + +@@ -114,6 +186,7 @@ int qcrypto_hash_digest(QCryptoHashAlgorithm alg, + return qcrypto_hash_digestv(alg, &iov, 1, digest, errp); + } + ++#if 0 + int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, + const struct iovec *iov, + size_t niov, +@@ -142,3 +215,4 @@ int qcrypto_hash_base64(QCryptoHashAlgorithm alg, + + return qcrypto_hash_base64v(alg, &iov, 1, base64, errp); + } ++#endif +diff --git a/qcow2/lib/crypto/tlscreds.c b/qcow2/lib/crypto/tlscreds.c +index 084ce0d5..eb4a1684 100644 +--- a/qcow2/lib/crypto/tlscreds.c ++++ b/qcow2/lib/crypto/tlscreds.c +@@ -20,10 +20,10 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-types-crypto.h" ++#include "qapi/qapi-types-crypto.h" + #include "qemu/module.h" + #include "tlscredspriv.h" +-#include "trace.h" ++//#include "trace.h" + + #define DH_BITS 2048 + +@@ -36,7 +36,7 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, + { + int ret; + +- trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : ""); ++ //trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : ""); + + if (filename == NULL) { + ret = gnutls_dh_params_init(dh_params); +@@ -129,8 +129,8 @@ qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, + + ret = 0; + cleanup: +- trace_qcrypto_tls_creds_get_path(creds, filename, +- *cred ? *cred : ""); ++ //trace_qcrypto_tls_creds_get_path(creds, filename, ++ // *cred ? *cred : ""); + return ret; + } + +diff --git a/qcow2/lib/crypto/tlssession.c b/qcow2/lib/crypto/tlssession.c +index 77286e23..83aa84dc 100644 +--- a/qcow2/lib/crypto/tlssession.c ++++ b/qcow2/lib/crypto/tlssession.c +@@ -26,7 +26,7 @@ + #include "qapi/error.h" + #include "authz/base.h" + #include "tlscredspriv.h" +-#include "trace.h" ++//#include "trace.h" + + #ifdef CONFIG_GNUTLS + +@@ -140,9 +140,11 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, + int ret; + + session = g_new0(QCryptoTLSSession, 1); ++#if 0 + trace_qcrypto_tls_session_new( + session, creds, hostname ? hostname : "", + authzid ? authzid : "", endpoint); ++#endif + + if (hostname) { + session->hostname = g_strdup(hostname); +@@ -435,26 +437,26 @@ qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, + { + if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_ANON)) { +- trace_qcrypto_tls_session_check_creds(session, "nop"); ++ //trace_qcrypto_tls_session_check_creds(session, "nop"); + return 0; + } else if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_PSK)) { +- trace_qcrypto_tls_session_check_creds(session, "nop"); ++ //trace_qcrypto_tls_session_check_creds(session, "nop"); + return 0; + } else if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_X509)) { + if (session->creds->verifyPeer) { + int ret = qcrypto_tls_session_check_certificate(session, + errp); +- trace_qcrypto_tls_session_check_creds(session, +- ret == 0 ? "pass" : "fail"); ++ //trace_qcrypto_tls_session_check_creds(session, ++ // ret == 0 ? "pass" : "fail"); + return ret; + } else { +- trace_qcrypto_tls_session_check_creds(session, "skip"); ++ //trace_qcrypto_tls_session_check_creds(session, "skip"); + return 0; + } + } else { +- trace_qcrypto_tls_session_check_creds(session, "error"); ++ //trace_qcrypto_tls_session_check_creds(session, "error"); + error_setg(errp, "Unexpected credential type %s", + object_get_typename(OBJECT(session->creds))); + return -1; +diff --git a/qcow2/lib/hw/block/block.c b/qcow2/lib/hw/block/block.c +index 3ceca7dc..9a0da1e6 100644 +--- a/qcow2/lib/hw/block/block.c ++++ b/qcow2/lib/hw/block/block.c +@@ -9,12 +9,13 @@ + + #include "qemu/osdep.h" + #include "block/block_int-common.h" +-#include "sysemu/blockdev.h" ++//#include "sysemu/blockdev.h" + #include "sysemu/block-backend.h" + #include "hw/block/block.h" + #include "qapi/error.h" + #include "qapi/qapi-types-block.h" + ++#if 0 + /* + * Read the non-zeroes parts of @blk into @buf + * Reading all of the @blk is expensive if the zeroes parts of @blk +@@ -99,6 +100,7 @@ bool blk_check_size_and_read_all(BlockBackend *blk, DeviceState *dev, + } + return true; + } ++#endif + + bool blkconf_blocksizes(BlockConf *conf, Error **errp) + { +@@ -257,8 +259,8 @@ bool blkconf_geometry(BlockConf *conf, int *ptrans, + hd_geometry_guess(conf->blk, + &conf->cyls, &conf->heads, &conf->secs, + ptrans); +- } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { +- *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); ++ //} else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { ++ // *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); + } + if (conf->cyls || conf->heads || conf->secs) { + if (conf->cyls < 1 || conf->cyls > cyls_max) { +diff --git a/qcow2/lib/hw/block/hd-geometry.c b/qcow2/lib/hw/block/hd-geometry.c +index 2b0af443..498f7a81 100644 +--- a/qcow2/lib/hw/block/hd-geometry.c ++++ b/qcow2/lib/hw/block/hd-geometry.c +@@ -35,7 +35,7 @@ + #include "qapi/qapi-types-block.h" + #include "qemu/bswap.h" + #include "hw/block/block.h" +-#include "trace.h" ++//#include "trace.h" + + struct partition { + uint8_t boot_ind; /* 0x80 - active */ +@@ -88,7 +88,7 @@ static int guess_disk_lchs(BlockBackend *blk, + *pheads = heads; + *psectors = sectors; + *pcylinders = cylinders; +- trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors); ++ //trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors); + return 0; + } + } +@@ -157,7 +157,7 @@ void hd_geometry_guess(BlockBackend *blk, + translation = *ptrans; + } + } +- trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); ++ //trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); + } + + int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs) +diff --git a/qcow2/lib/io/channel-file.c b/qcow2/lib/io/channel-file.c +index 2ea8d083..dcd512dd 100644 +--- a/qcow2/lib/io/channel-file.c ++++ b/qcow2/lib/io/channel-file.c +@@ -25,7 +25,7 @@ + #include "qapi/error.h" + #include "qemu/module.h" + #include "qemu/sockets.h" +-#include "trace.h" ++//#include "trace.h" + + QIOChannelFile * + qio_channel_file_new_fd(int fd) +@@ -40,7 +40,7 @@ qio_channel_file_new_fd(int fd) + qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); + } + +- trace_qio_channel_file_new_fd(ioc, fd); ++ //trace_qio_channel_file_new_fd(ioc, fd); + + return ioc; + } +@@ -82,7 +82,7 @@ qio_channel_file_new_path(const char *path, + qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); + } + +- trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd); ++ //trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd); + + return ioc; + } +diff --git a/qcow2/lib/io/channel-socket.c b/qcow2/lib/io/channel-socket.c +index 608bcf06..7081d0d3 100644 +--- a/qcow2/lib/io/channel-socket.c ++++ b/qcow2/lib/io/channel-socket.c +@@ -24,7 +24,7 @@ + #include "io/channel-socket.h" + #include "io/channel-util.h" + #include "io/channel-watch.h" +-#include "trace.h" ++//#include "trace.h" + #include "qapi/clone-visitor.h" + #ifdef CONFIG_LINUX + #include +@@ -73,7 +73,7 @@ qio_channel_socket_new(void) + ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL); + #endif + +- trace_qio_channel_socket_new(sioc); ++ //trace_qio_channel_socket_new(sioc); + + return sioc; + } +@@ -139,7 +139,7 @@ qio_channel_socket_new_fd(int fd, + return NULL; + } + +- trace_qio_channel_socket_new_fd(ioc, fd); ++ //trace_qio_channel_socket_new_fd(ioc, fd); + + return ioc; + } +@@ -151,14 +151,14 @@ int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, + { + int fd; + +- trace_qio_channel_socket_connect_sync(ioc, addr); ++ //trace_qio_channel_socket_connect_sync(ioc, addr); + fd = socket_connect(addr, errp); + if (fd < 0) { +- trace_qio_channel_socket_connect_fail(ioc); ++ //trace_qio_channel_socket_connect_fail(ioc); + return -1; + } + +- trace_qio_channel_socket_connect_complete(ioc, fd); ++ //trace_qio_channel_socket_connect_complete(ioc, fd); + if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { + close(fd); + return -1; +@@ -209,7 +209,7 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc, + + /* socket_connect() does a non-blocking connect(), but it + * still blocks in DNS lookups, so we must use a thread */ +- trace_qio_channel_socket_connect_async(ioc, addr); ++ //trace_qio_channel_socket_connect_async(ioc, addr); + qio_task_run_in_thread(task, + qio_channel_socket_connect_worker, + addrCopy, +@@ -225,14 +225,14 @@ int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, + { + int fd; + +- trace_qio_channel_socket_listen_sync(ioc, addr, num); ++ //trace_qio_channel_socket_listen_sync(ioc, addr, num); + fd = socket_listen(addr, num, errp); + if (fd < 0) { +- trace_qio_channel_socket_listen_fail(ioc); ++ //trace_qio_channel_socket_listen_fail(ioc); + return -1; + } + +- trace_qio_channel_socket_listen_complete(ioc, fd); ++ //trace_qio_channel_socket_listen_complete(ioc, fd); + if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { + close(fd); + return -1; +@@ -286,7 +286,7 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc, + data->num = num; + + /* socket_listen() blocks in DNS lookups, so we must use a thread */ +- trace_qio_channel_socket_listen_async(ioc, addr, num); ++ //trace_qio_channel_socket_listen_async(ioc, addr, num); + qio_task_run_in_thread(task, + qio_channel_socket_listen_worker, + data, +@@ -302,14 +302,14 @@ int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, + { + int fd; + +- trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr); ++ //trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr); + fd = socket_dgram(remoteAddr, localAddr, errp); + if (fd < 0) { +- trace_qio_channel_socket_dgram_fail(ioc); ++ //trace_qio_channel_socket_dgram_fail(ioc); + return -1; + } + +- trace_qio_channel_socket_dgram_complete(ioc, fd); ++ //trace_qio_channel_socket_dgram_complete(ioc, fd); + if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { + close(fd); + return -1; +@@ -364,7 +364,7 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, + data->localAddr = QAPI_CLONE(SocketAddress, localAddr); + data->remoteAddr = QAPI_CLONE(SocketAddress, remoteAddr); + +- trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); ++ //trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); + qio_task_run_in_thread(task, + qio_channel_socket_dgram_worker, + data, +@@ -384,7 +384,7 @@ qio_channel_socket_accept(QIOChannelSocket *ioc, + cioc->localAddrLen = sizeof(ioc->localAddr); + + retry: +- trace_qio_channel_socket_accept(ioc); ++ //trace_qio_channel_socket_accept(ioc); + cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr, + &cioc->remoteAddrLen); + if (cioc->fd < 0) { +@@ -392,7 +392,7 @@ qio_channel_socket_accept(QIOChannelSocket *ioc, + goto retry; + } + error_setg_errno(errp, errno, "Unable to accept connection"); +- trace_qio_channel_socket_accept_fail(ioc); ++ //trace_qio_channel_socket_accept_fail(ioc); + goto error; + } + +@@ -413,7 +413,7 @@ qio_channel_socket_accept(QIOChannelSocket *ioc, + qio_channel_set_feature(QIO_CHANNEL(cioc), + QIO_CHANNEL_FEATURE_READ_MSG_PEEK); + +- trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); ++ //trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); + return cioc; + + error: +diff --git a/qcow2/lib/io/channel-tls.c b/qcow2/lib/io/channel-tls.c +index aab630e5..dd347704 100644 +--- a/qcow2/lib/io/channel-tls.c ++++ b/qcow2/lib/io/channel-tls.c +@@ -22,7 +22,7 @@ + #include "qapi/error.h" + #include "qemu/module.h" + #include "io/channel-tls.h" +-#include "trace.h" ++//#include "trace.h" + #include "qemu/atomic.h" + + +@@ -96,7 +96,7 @@ qio_channel_tls_new_server(QIOChannel *master, + qio_channel_tls_read_handler, + tioc); + +- trace_qio_channel_tls_new_server(tioc, master, creds, aclname); ++ //trace_qio_channel_tls_new_server(tioc, master, creds, aclname); + return tioc; + + error: +@@ -139,7 +139,7 @@ qio_channel_tls_new_client(QIOChannel *master, + qio_channel_tls_read_handler, + tioc); + +- trace_qio_channel_tls_new_client(tioc, master, creds, hostname); ++ //trace_qio_channel_tls_new_client(tioc, master, creds, hostname); + return tioc; + + error: +@@ -165,7 +165,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, + QCryptoTLSSessionHandshakeStatus status; + + if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { +- trace_qio_channel_tls_handshake_fail(ioc); ++ //trace_qio_channel_tls_handshake_fail(ioc); + qio_task_set_error(task, err); + qio_task_complete(task); + return; +@@ -173,13 +173,13 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, + + status = qcrypto_tls_session_get_handshake_status(ioc->session); + if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { +- trace_qio_channel_tls_handshake_complete(ioc); ++ //trace_qio_channel_tls_handshake_complete(ioc); + if (qcrypto_tls_session_check_credentials(ioc->session, + &err) < 0) { +- trace_qio_channel_tls_credentials_deny(ioc); ++ //trace_qio_channel_tls_credentials_deny(ioc); + qio_task_set_error(task, err); + } else { +- trace_qio_channel_tls_credentials_allow(ioc); ++ //trace_qio_channel_tls_credentials_allow(ioc); + } + qio_task_complete(task); + } else { +@@ -199,7 +199,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, + condition = G_IO_IN; + } + +- trace_qio_channel_tls_handshake_pending(ioc, status); ++ //trace_qio_channel_tls_handshake_pending(ioc, status); + ioc->hs_ioc_tag = + qio_channel_add_watch_full(ioc->master, + condition, +@@ -243,7 +243,7 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, + task = qio_task_new(OBJECT(ioc), + func, opaque, destroy); + +- trace_qio_channel_tls_handshake_start(ioc); ++ //trace_qio_channel_tls_handshake_start(ioc); + qio_channel_tls_handshake_task(ioc, task, context); + } + +@@ -375,7 +375,7 @@ static int qio_channel_tls_close(QIOChannel *ioc, + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + + if (tioc->hs_ioc_tag) { +- trace_qio_channel_tls_handshake_cancel(ioc); ++ //trace_qio_channel_tls_handshake_cancel(ioc); + g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); + } + +diff --git a/qcow2/lib/io/task.c b/qcow2/lib/io/task.c +index 451f26f8..f48832e9 100644 +--- a/qcow2/lib/io/task.c ++++ b/qcow2/lib/io/task.c +@@ -23,7 +23,7 @@ + #include "qapi/error.h" + #include "qemu/thread.h" + #include "qom/object.h" +-#include "trace.h" ++//#include "trace.h" + + struct QIOTaskThreadData { + QIOTaskWorker worker; +@@ -65,7 +65,7 @@ QIOTask *qio_task_new(Object *source, + qemu_mutex_init(&task->thread_lock); + qemu_cond_init(&task->thread_cond); + +- trace_qio_task_new(task, source, func, opaque); ++ //trace_qio_task_new(task, source, func, opaque); + + return task; + } +@@ -108,7 +108,7 @@ static gboolean qio_task_thread_result(gpointer opaque) + { + QIOTask *task = opaque; + +- trace_qio_task_thread_result(task); ++ //trace_qio_task_thread_result(task); + qio_task_complete(task); + + return FALSE; +@@ -119,7 +119,7 @@ static gpointer qio_task_thread_worker(gpointer opaque) + { + QIOTask *task = opaque; + +- trace_qio_task_thread_run(task); ++ //trace_qio_task_thread_run(task); + + task->thread->worker(task, task->thread->opaque); + +@@ -128,7 +128,7 @@ static gpointer qio_task_thread_worker(gpointer opaque) + * thread. So we schedule an idle callback to report + * the worker results + */ +- trace_qio_task_thread_exit(task); ++ //trace_qio_task_thread_exit(task); + + qemu_mutex_lock(&task->thread_lock); + +@@ -138,7 +138,7 @@ static gpointer qio_task_thread_worker(gpointer opaque) + g_source_attach(task->thread->completion, + task->thread->context); + g_source_unref(task->thread->completion); +- trace_qio_task_thread_source_attach(task, task->thread->completion); ++ //trace_qio_task_thread_source_attach(task, task->thread->completion); + + qemu_cond_signal(&task->thread_cond); + qemu_mutex_unlock(&task->thread_lock); +@@ -167,7 +167,7 @@ void qio_task_run_in_thread(QIOTask *task, + + task->thread = data; + +- trace_qio_task_thread_start(task, worker, opaque); ++ //trace_qio_task_thread_start(task, worker, opaque); + qemu_thread_create(&thread, + "io-task-worker", + qio_task_thread_worker, +@@ -184,7 +184,7 @@ void qio_task_wait_thread(QIOTask *task) + qemu_cond_wait(&task->thread_cond, &task->thread_lock); + } + +- trace_qio_task_thread_source_cancel(task, task->thread->completion); ++ //trace_qio_task_thread_source_cancel(task, task->thread->completion); + g_source_destroy(task->thread->completion); + qemu_mutex_unlock(&task->thread_lock); + +@@ -195,7 +195,7 @@ void qio_task_wait_thread(QIOTask *task) + void qio_task_complete(QIOTask *task) + { + task->func(task, task->opaque); +- trace_qio_task_complete(task); ++ //trace_qio_task_complete(task); + qio_task_free(task); + } + +diff --git a/qcow2/lib/job-qmp.c b/qcow2/lib/job-qmp.c +index 9e26fa89..3b9cb0aa 100644 +--- a/qcow2/lib/job-qmp.c ++++ b/qcow2/lib/job-qmp.c +@@ -27,7 +27,7 @@ + #include "qemu/job.h" + #include "qapi/qapi-commands-job.h" + #include "qapi/error.h" +-#include "trace/trace-root.h" ++//#include "trace/trace-root.h" + + /* + * Get a job using its ID. Called with job_mutex held. +@@ -56,7 +56,7 @@ void qmp_job_cancel(const char *id, Error **errp) + return; + } + +- trace_qmp_job_cancel(job); ++ //trace_qmp_job_cancel(job); + job_user_cancel_locked(job, true, errp); + } + +@@ -71,7 +71,7 @@ void qmp_job_pause(const char *id, Error **errp) + return; + } + +- trace_qmp_job_pause(job); ++ //trace_qmp_job_pause(job); + job_user_pause_locked(job, errp); + } + +@@ -86,7 +86,7 @@ void qmp_job_resume(const char *id, Error **errp) + return; + } + +- trace_qmp_job_resume(job); ++ //trace_qmp_job_resume(job); + job_user_resume_locked(job, errp); + } + +@@ -101,7 +101,7 @@ void qmp_job_complete(const char *id, Error **errp) + return; + } + +- trace_qmp_job_complete(job); ++ //trace_qmp_job_complete(job); + job_complete_locked(job, errp); + } + +@@ -116,7 +116,7 @@ void qmp_job_finalize(const char *id, Error **errp) + return; + } + +- trace_qmp_job_finalize(job); ++ //trace_qmp_job_finalize(job); + job_ref_locked(job); + job_finalize_locked(job, errp); + +@@ -134,7 +134,7 @@ void qmp_job_dismiss(const char *id, Error **errp) + return; + } + +- trace_qmp_job_dismiss(job); ++ //trace_qmp_job_dismiss(job); + job_dismiss_locked(&job, errp); + } + +diff --git a/qcow2/lib/job.c b/qcow2/lib/job.c +index 660ce22c..63223a10 100644 +--- a/qcow2/lib/job.c ++++ b/qcow2/lib/job.c +@@ -29,7 +29,7 @@ + #include "qemu/id.h" + #include "qemu/main-loop.h" + #include "block/aio-wait.h" +-#include "trace/trace-root.h" ++//#include "trace/trace-root.h" + #include "qapi/qapi-events-job.h" + + /* +@@ -209,9 +209,9 @@ static void job_state_transition_locked(Job *job, JobStatus s1) + { + JobStatus s0 = job->status; + assert(s1 >= 0 && s1 < JOB_STATUS__MAX); +- trace_job_state_transition(job, job->ret, +- JobSTT[s0][s1] ? "allowed" : "disallowed", +- JobStatus_str(s0), JobStatus_str(s1)); ++ //trace_job_state_transition(job, job->ret, ++ // JobSTT[s0][s1] ? "allowed" : "disallowed", ++ // JobStatus_str(s0), JobStatus_str(s1)); + assert(JobSTT[s0][s1]); + job->status = s1; + +@@ -224,8 +224,8 @@ int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) + { + JobStatus s0 = job->status; + assert(verb >= 0 && verb < JOB_VERB__MAX); +- trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), +- JobVerbTable[verb][s0] ? "allowed" : "prohibited"); ++ //trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), ++ // JobVerbTable[verb][s0] ? "allowed" : "prohibited"); + if (JobVerbTable[verb][s0]) { + return 0; + } +@@ -1060,7 +1060,7 @@ static void job_completed_locked(Job *job) + assert(job && job->txn && !job_is_completed_locked(job)); + + job_update_rc_locked(job); +- trace_job_completed(job, job->ret); ++ //trace_job_completed(job, job->ret); + if (job->ret) { + job_completed_txn_abort_locked(job); + } else { +diff --git a/qcow2/lib/nbd/client-connection.c b/qcow2/lib/nbd/client-connection.c +index f9da67c8..c834ae9c 100644 +--- a/qcow2/lib/nbd/client-connection.c ++++ b/qcow2/lib/nbd/client-connection.c +@@ -23,7 +23,7 @@ + */ + + #include "qemu/osdep.h" +-#include "trace.h" ++//#include "trace.h" + + #include "block/nbd.h" + +@@ -211,7 +211,7 @@ static void *connect_thread_func(void *opaque) + object_unref(OBJECT(conn->sioc)); + conn->sioc = NULL; + if (conn->do_retry && !conn->detached) { +- trace_nbd_connect_thread_sleep(timeout); ++ //trace_nbd_connect_thread_sleep(timeout); + qemu_mutex_unlock(&conn->mutex); + + sleep(timeout); +diff --git a/qcow2/lib/nbd/client.c b/qcow2/lib/nbd/client.c +index c89c7504..3e5c5828 100644 +--- a/qcow2/lib/nbd/client.c ++++ b/qcow2/lib/nbd/client.c +@@ -20,7 +20,7 @@ + #include "qemu/osdep.h" + #include "qapi/error.h" + #include "qemu/queue.h" +-#include "trace.h" ++//#include "trace.h" + #include "nbd-internal.h" + #include "qemu/cutils.h" + +@@ -75,7 +75,7 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, + if (len == -1) { + req.length = len = strlen(data); + } +- trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); ++ //trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); + + stq_be_p(&req.magic, NBD_OPTS_MAGIC); + stl_be_p(&req.option, opt); +@@ -124,9 +124,11 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, + reply->type = be32_to_cpu(reply->type); + reply->length = be32_to_cpu(reply->length); + ++#if 0 + trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), + reply->type, nbd_rep_lookup(reply->type), + reply->length); ++#endif + + if (reply->magic != NBD_REP_MAGIC) { + error_setg(errp, "Unexpected option reply magic"); +@@ -176,14 +178,18 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, + goto err; + } + msg[reply->length] = '\0'; ++#if 0 + trace_nbd_server_error_msg(reply->type, + nbd_reply_type_lookup(reply->type), msg); ++#endif + } + + if (reply->type == NBD_REP_ERR_UNSUP || !strict) { ++#if 0 + trace_nbd_reply_err_ignored(reply->option, + nbd_opt_lookup(reply->option), + reply->type, nbd_rep_lookup(reply->type)); ++#endif + return 0; + } + +@@ -319,7 +325,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, + local_desc[len] = '\0'; + } + +- trace_nbd_receive_list(local_name, local_desc ?: ""); ++ //trace_nbd_receive_list(local_name, local_desc ?: ""); + *name = g_steal_pointer(&local_name); + if (description) { + *description = g_steal_pointer(&local_desc); +@@ -352,7 +358,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + info->flags = 0; + + assert(opt == NBD_OPT_GO || opt == NBD_OPT_INFO); +- trace_nbd_opt_info_go_start(nbd_opt_lookup(opt), info->name); ++ //trace_nbd_opt_info_go_start(nbd_opt_lookup(opt), info->name); + buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); + stl_be_p(buf, len); + memcpy(buf + 4, info->name, len); +@@ -392,7 +398,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); + return -1; + } +- trace_nbd_opt_info_go_success(nbd_opt_lookup(opt)); ++ //trace_nbd_opt_info_go_success(nbd_opt_lookup(opt)); + return 1; + } + if (reply.type != NBD_REP_INFO) { +@@ -437,7 +443,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + nbd_send_opt_abort(ioc); + return -1; + } +- trace_nbd_receive_negotiate_size_flags(info->size, info->flags); ++ //trace_nbd_receive_negotiate_size_flags(info->size, info->flags); + break; + + case NBD_INFO_BLOCK_SIZE: +@@ -483,8 +489,8 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + nbd_send_opt_abort(ioc); + return -1; + } +- trace_nbd_opt_info_block_size(info->min_block, info->opt_block, +- info->max_block); ++ //trace_nbd_opt_info_block_size(info->min_block, info->opt_block, ++ // info->max_block); + break; + + default: +@@ -492,7 +498,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + * Not worth the bother to check if NBD_INFO_NAME or + * NBD_INFO_DESCRIPTION exceed NBD_MAX_STRING_SIZE. + */ +- trace_nbd_opt_info_unknown(type, nbd_info_lookup(type)); ++ //trace_nbd_opt_info_unknown(type, nbd_info_lookup(type)); + if (nbd_drop(ioc, len, errp) < 0) { + error_prepend(errp, "Failed to read info payload: "); + nbd_send_opt_abort(ioc); +@@ -511,7 +517,7 @@ static int nbd_receive_query_exports(QIOChannel *ioc, + bool list_empty = true; + bool found_export = false; + +- trace_nbd_receive_query_exports_start(wantname); ++ //trace_nbd_receive_query_exports_start(wantname); + if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) { + return -1; + } +@@ -541,7 +547,7 @@ static int nbd_receive_query_exports(QIOChannel *ioc, + nbd_send_opt_abort(ioc); + return -1; + } +- trace_nbd_receive_query_exports_success(wantname); ++ //trace_nbd_receive_query_exports_success(wantname); + return 0; + } + list_empty = false; +@@ -631,13 +637,13 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, + return NULL; + } + +- trace_nbd_receive_starttls_new_client(); ++ //trace_nbd_receive_starttls_new_client(); + tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp); + if (!tioc) { + return NULL; + } + qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls"); +- trace_nbd_receive_starttls_tls_handshake(); ++ //trace_nbd_receive_starttls_tls_handshake(); + qio_channel_tls_handshake(tioc, + nbd_client_tls_handshake, + &data, +@@ -689,7 +695,7 @@ static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt, + } + p = data = g_malloc(data_len); + +- trace_nbd_opt_meta_request(nbd_opt_lookup(opt), query ?: "(all)", export); ++ //trace_nbd_opt_meta_request(nbd_opt_lookup(opt), query ?: "(all)", export); + stl_be_p(p, export_len); + memcpy(p += sizeof(export_len), export, export_len); + stl_be_p(p += export_len, queries); +@@ -767,7 +773,7 @@ static int nbd_receive_one_meta_context(QIOChannel *ioc, + return -1; + } + local_name[reply.length] = '\0'; +- trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id); ++ //trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id); + + if (name) { + *name = local_name; +@@ -905,7 +911,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + ERRP_GUARD(); + uint64_t magic; + +- trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); ++ //trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); + + if (zeroes) { + *zeroes = true; +@@ -921,7 +927,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + if (nbd_read64(ioc, &magic, "initial magic", errp) < 0) { + return -EINVAL; + } +- trace_nbd_receive_negotiate_magic(magic); ++ //trace_nbd_receive_negotiate_magic(magic); + + if (magic != NBD_INIT_MAGIC) { + error_setg(errp, "Bad initial magic received: 0x%" PRIx64, magic); +@@ -931,7 +937,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + if (nbd_read64(ioc, &magic, "server magic", errp) < 0) { + return -EINVAL; + } +- trace_nbd_receive_negotiate_magic(magic); ++ //trace_nbd_receive_negotiate_magic(magic); + + if (magic == NBD_OPTS_MAGIC) { + uint32_t clientflags = 0; +@@ -941,7 +947,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + if (nbd_read16(ioc, &globalflags, "server flags", errp) < 0) { + return -EINVAL; + } +- trace_nbd_receive_negotiate_server_flags(globalflags); ++ //trace_nbd_receive_negotiate_server_flags(globalflags); + if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { + fixedNewStyle = true; + clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; +@@ -1047,7 +1053,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + bool base_allocation = info->base_allocation; + + assert(info->name && strlen(info->name) <= NBD_MAX_STRING_SIZE); +- trace_nbd_receive_negotiate_name(info->name); ++ //trace_nbd_receive_negotiate_name(info->name); + + result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, + info->mode, &zeroes, errp); +@@ -1124,7 +1130,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + g_assert_not_reached(); + } + +- trace_nbd_receive_negotiate_size_flags(info->size, info->flags); ++ //trace_nbd_receive_negotiate_size_flags(info->size, info->flags); + if (zeroes && nbd_drop(ioc, 124, errp) < 0) { + error_prepend(errp, "Failed to read reserved block: "); + return -EINVAL; +@@ -1286,7 +1292,7 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, + return -E2BIG; + } + +- trace_nbd_init_set_socket(); ++ //trace_nbd_init_set_socket(); + + if (ioctl(fd, NBD_SET_SOCK, (unsigned long) sioc->fd) < 0) { + int serrno = errno; +@@ -1294,7 +1300,7 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, + return -serrno; + } + +- trace_nbd_init_set_block_size(sector_size); ++ //trace_nbd_init_set_block_size(sector_size); + + if (ioctl(fd, NBD_SET_BLKSIZE, sector_size) < 0) { + int serrno = errno; +@@ -1302,9 +1308,9 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, + return -serrno; + } + +- trace_nbd_init_set_size(sectors); ++ //trace_nbd_init_set_size(sectors); + if (info->size % sector_size) { +- trace_nbd_init_trailing_bytes(info->size % sector_size); ++ //trace_nbd_init_trailing_bytes(info->size % sector_size); + } + + if (ioctl(fd, NBD_SET_SIZE_BLOCKS, sectors) < 0) { +@@ -1316,7 +1322,7 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, + if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) info->flags) < 0) { + if (errno == ENOTTY) { + int read_only = (info->flags & NBD_FLAG_READ_ONLY) != 0; +- trace_nbd_init_set_readonly(); ++ //trace_nbd_init_set_readonly(); + + if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { + int serrno = errno; +@@ -1330,7 +1336,7 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, + } + } + +- trace_nbd_init_finish(); ++ //trace_nbd_init_finish(); + + return 0; + } +@@ -1340,7 +1346,7 @@ int nbd_client(int fd) + int ret; + int serrno; + +- trace_nbd_client_loop(); ++ //trace_nbd_client_loop(); + + ret = ioctl(fd, NBD_DO_IT); + if (ret < 0 && errno == EPIPE) { +@@ -1352,12 +1358,12 @@ int nbd_client(int fd) + } + serrno = errno; + +- trace_nbd_client_loop_ret(ret, strerror(serrno)); ++ //trace_nbd_client_loop_ret(ret, strerror(serrno)); + +- trace_nbd_client_clear_queue(); ++ //trace_nbd_client_clear_queue(); + ioctl(fd, NBD_CLEAR_QUE); + +- trace_nbd_client_clear_socket(); ++ //trace_nbd_client_clear_socket(); + ioctl(fd, NBD_CLEAR_SOCK); + + errno = serrno; +@@ -1379,9 +1385,11 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request) + uint8_t buf[NBD_EXTENDED_REQUEST_SIZE]; + size_t len; + ++#if 0 + trace_nbd_send_request(request->from, request->len, request->cookie, + request->flags, request->type, + nbd_cmd_lookup(request->type)); ++#endif + + stw_be_p(buf + 4, request->flags); + stw_be_p(buf + 6, request->type); +@@ -1535,7 +1543,7 @@ int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, + NBDReply *reply, NBDMode mode, Error **errp) + { + int ret; +- const char *type; ++ //const char *type; + uint32_t expected; + + ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp); +@@ -1549,37 +1557,46 @@ int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, + switch (reply->magic) { + case NBD_SIMPLE_REPLY_MAGIC: + if (mode >= NBD_MODE_EXTENDED) { ++#if 0 + trace_nbd_receive_wrong_header(reply->magic, + nbd_mode_lookup(mode)); ++#endif + } + ret = nbd_receive_simple_reply(ioc, &reply->simple, errp); + if (ret < 0) { + return ret; + } ++#if 0 + trace_nbd_receive_simple_reply(reply->simple.error, + nbd_err_lookup(reply->simple.error), + reply->cookie); ++#endif + break; + case NBD_STRUCTURED_REPLY_MAGIC: + case NBD_EXTENDED_REPLY_MAGIC: + expected = mode >= NBD_MODE_EXTENDED ? NBD_EXTENDED_REPLY_MAGIC + : NBD_STRUCTURED_REPLY_MAGIC; + if (reply->magic != expected) { ++#if 0 + trace_nbd_receive_wrong_header(reply->magic, + nbd_mode_lookup(mode)); ++#endif + } + ret = nbd_receive_reply_chunk_header(ioc, reply, errp); + if (ret < 0) { + return ret; + } ++ nbd_reply_type_lookup(reply->structured.type); ++#if 0 + type = nbd_reply_type_lookup(reply->structured.type); + trace_nbd_receive_reply_chunk_header(reply->structured.flags, + reply->structured.type, type, + reply->structured.cookie, + reply->structured.length); ++#endif + break; + default: +- trace_nbd_receive_wrong_header(reply->magic, nbd_mode_lookup(mode)); ++ //trace_nbd_receive_wrong_header(reply->magic, nbd_mode_lookup(mode)); + error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", reply->magic); + return -EINVAL; + } +diff --git a/qcow2/lib/nbd/common.c b/qcow2/lib/nbd/common.c +index 589a748c..891b7f6b 100644 +--- a/qcow2/lib/nbd/common.c ++++ b/qcow2/lib/nbd/common.c +@@ -17,7 +17,7 @@ + */ + + #include "qemu/osdep.h" +-#include "trace.h" ++//#include "trace.h" + #include "nbd-internal.h" + + /* Discard length bytes from channel. Return -errno on failure and 0 on +@@ -237,7 +237,7 @@ int nbd_errno_to_system_errno(int err) + ret = ESHUTDOWN; + break; + default: +- trace_nbd_unknown_error(err); ++ //trace_nbd_unknown_error(err); + /* fallthrough */ + case NBD_EINVAL: + ret = EINVAL; +diff --git a/qcow2/lib/qapi/qapi-events-block-core.c b/qcow2/lib/qapi/qapi-events-block-core.c +index a08a710c..2c8a538c 100644 +--- a/qcow2/lib/qapi/qapi-events-block-core.c ++++ b/qcow2/lib/qapi/qapi-events-block-core.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi-emit-events.h" +-#include "qapi-events-block-core.h" +-#include "qapi-visit-block-core.h" ++#include "qapi/qapi-events-block-core.h" ++#include "qapi/qapi-visit-block-core.h" + #include "qapi/compat-policy.h" + #include "qapi/error.h" + #include "qapi/qmp/qdict.h" +@@ -43,7 +43,7 @@ void qapi_event_send_block_image_corrupted(const char *device, const char *node_ + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_IMAGE_CORRUPTED, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_IMAGE_CORRUPTED, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -73,7 +73,7 @@ void qapi_event_send_block_io_error(const char *device, const char *node_name, I + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_IO_ERROR, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_IO_ERROR, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -103,7 +103,7 @@ void qapi_event_send_block_job_completed(JobType type, const char *device, int64 + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_COMPLETED, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_COMPLETED, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -133,7 +133,7 @@ void qapi_event_send_block_job_cancelled(JobType type, const char *device, int64 + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_CANCELLED, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_CANCELLED, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -163,7 +163,7 @@ void qapi_event_send_block_job_error(const char *device, IoOperationType operati + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_ERROR, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_ERROR, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -193,7 +193,7 @@ void qapi_event_send_block_job_ready(JobType type, const char *device, int64_t l + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_READY, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_READY, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -223,7 +223,7 @@ void qapi_event_send_block_job_pending(JobType type, const char *id) + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_JOB_PENDING, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_JOB_PENDING, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -253,7 +253,7 @@ void qapi_event_send_block_write_threshold(const char *node_name, uint64_t amoun + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_BLOCK_WRITE_THRESHOLD, qmp); ++ //qapi_event_emit(QAPI_EVENT_BLOCK_WRITE_THRESHOLD, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -283,7 +283,7 @@ void qapi_event_send_quorum_failure(const char *reference, int64_t sector_num, i + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_QUORUM_FAILURE, qmp); ++ //qapi_event_emit(QAPI_EVENT_QUORUM_FAILURE, qmp); + + visit_free(v); + qobject_unref(qmp); +@@ -313,7 +313,7 @@ void qapi_event_send_quorum_report_bad(QuorumOpType type, const char *error, con + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_QUORUM_REPORT_BAD, qmp); ++ //qapi_event_emit(QAPI_EVENT_QUORUM_REPORT_BAD, qmp); + + visit_free(v); + qobject_unref(qmp); +diff --git a/qcow2/lib/qapi/qapi-events-job.c b/qcow2/lib/qapi/qapi-events-job.c +index cb5c3b45..f96b8a30 100644 +--- a/qcow2/lib/qapi/qapi-events-job.c ++++ b/qcow2/lib/qapi/qapi-events-job.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi-emit-events.h" +-#include "qapi-events-job.h" +-#include "qapi-visit-job.h" ++#include "qapi/qapi-events-job.h" ++#include "qapi/qapi-visit-job.h" + #include "qapi/compat-policy.h" + #include "qapi/error.h" + #include "qapi/qmp/qdict.h" +@@ -43,7 +43,7 @@ void qapi_event_send_job_status_change(const char *id, JobStatus status) + } else { + qobject_unref(obj); + } +- qapi_event_emit(QAPI_EVENT_JOB_STATUS_CHANGE, qmp); ++ //qapi_event_emit(QAPI_EVENT_JOB_STATUS_CHANGE, qmp); + + visit_free(v); + qobject_unref(qmp); +diff --git a/qcow2/lib/qapi/qapi-types-block-core.c b/qcow2/lib/qapi/qapi-types-block-core.c +index f4ee0d21..7e9cf7c7 100644 +--- a/qcow2/lib/qapi/qapi-types-block-core.c ++++ b/qcow2/lib/qapi/qapi-types-block-core.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-block-core.h" +-#include "qapi-visit-block-core.h" ++#include "qapi/qapi-types-block-core.h" ++#include "qapi/qapi-visit-block-core.h" + + void qapi_free_SnapshotInfo(SnapshotInfo *obj) + { +@@ -28,6 +28,7 @@ void qapi_free_SnapshotInfo(SnapshotInfo *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_ImageInfoSpecificQCow2EncryptionBase(ImageInfoSpecificQCow2EncryptionBase *obj) + { + Visitor *v; +@@ -53,6 +54,7 @@ void qapi_free_ImageInfoSpecificQCow2Encryption(ImageInfoSpecificQCow2Encryption + visit_type_ImageInfoSpecificQCow2Encryption(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_Qcow2BitmapInfoList(Qcow2BitmapInfoList *obj) + { +@@ -80,6 +82,7 @@ void qapi_free_ImageInfoSpecificQCow2(ImageInfoSpecificQCow2 *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_VmdkExtentInfoList(VmdkExtentInfoList *obj) + { + Visitor *v; +@@ -131,6 +134,7 @@ void qapi_free_ImageInfoSpecificRbd(ImageInfoSpecificRbd *obj) + visit_type_ImageInfoSpecificRbd(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_ImageInfoSpecificFile(ImageInfoSpecificFile *obj) + { +@@ -148,14 +152,17 @@ void qapi_free_ImageInfoSpecificFile(ImageInfoSpecificFile *obj) + const QEnumLookup ImageInfoSpecificKind_lookup = { + .array = (const char *const[]) { + [IMAGE_INFO_SPECIFIC_KIND_QCOW2] = "qcow2", ++#if 0 + [IMAGE_INFO_SPECIFIC_KIND_VMDK] = "vmdk", + [IMAGE_INFO_SPECIFIC_KIND_LUKS] = "luks", + [IMAGE_INFO_SPECIFIC_KIND_RBD] = "rbd", ++#endif + [IMAGE_INFO_SPECIFIC_KIND_FILE] = "file", + }, + .size = IMAGE_INFO_SPECIFIC_KIND__MAX + }; + ++#if 0 + void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj) + { + Visitor *v; +@@ -220,6 +227,7 @@ void qapi_free_ImageInfoSpecificFileWrapper(ImageInfoSpecificFileWrapper *obj) + visit_type_ImageInfoSpecificFileWrapper(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_ImageInfoSpecific(ImageInfoSpecific *obj) + { +@@ -433,6 +441,7 @@ void qapi_free_Qcow2BitmapInfo(Qcow2BitmapInfo *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockLatencyHistogramInfo(BlockLatencyHistogramInfo *obj) + { + Visitor *v; +@@ -445,6 +454,7 @@ void qapi_free_BlockLatencyHistogramInfo(BlockLatencyHistogramInfo *obj) + visit_type_BlockLatencyHistogramInfo(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockInfo(BlockInfo *obj) + { +@@ -485,6 +495,7 @@ void qapi_free_BlockInfoList(BlockInfoList *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockDeviceTimedStats(BlockDeviceTimedStats *obj) + { + Visitor *v; +@@ -640,6 +651,7 @@ void qapi_free_BlockJobInfoMirror(BlockJobInfoMirror *obj) + visit_type_BlockJobInfoMirror(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockJobInfo(BlockJobInfo *obj) + { +@@ -667,6 +679,7 @@ void qapi_free_BlockJobInfoList(BlockJobInfoList *obj) + visit_free(v); + } + ++#if 0 + const QEnumLookup NewImageMode_lookup = { + .array = (const char *const[]) { + [NEW_IMAGE_MODE_EXISTING] = "existing", +@@ -752,6 +765,7 @@ void qapi_free_BlockdevBackup(BlockdevBackup *obj) + visit_type_BlockdevBackup(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockDeviceInfoList(BlockDeviceInfoList *obj) + { +@@ -766,6 +780,7 @@ void qapi_free_BlockDeviceInfoList(BlockDeviceInfoList *obj) + visit_free(v); + } + ++#if 0 + const QEnumLookup XDbgBlockGraphNodeType_lookup = { + .array = (const char *const[]) { + [X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND] = "block-backend", +@@ -875,6 +890,7 @@ void qapi_free_DriveMirror(DriveMirror *obj) + visit_type_DriveMirror(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockDirtyBitmap(BlockDirtyBitmap *obj) + { +@@ -928,6 +944,7 @@ void qapi_free_BlockDirtyBitmapOrStrList(BlockDirtyBitmapOrStrList *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockDirtyBitmapMerge(BlockDirtyBitmapMerge *obj) + { + Visitor *v; +@@ -1018,6 +1035,7 @@ void qapi_free_BlockJobChangeOptions(BlockJobChangeOptions *obj) + visit_type_BlockJobChangeOptions(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + const QEnumLookup BlockdevDiscardOptions_lookup = { + .array = (const char *const[]) { +@@ -1142,6 +1160,7 @@ void qapi_free_BlockdevOptionsFile(BlockdevOptionsFile *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevOptionsNull(BlockdevOptionsNull *obj) + { + Visitor *v; +@@ -1206,6 +1225,7 @@ void qapi_free_BlockdevOptionsLUKS(BlockdevOptionsLUKS *obj) + visit_type_BlockdevOptionsLUKS(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevOptionsGenericCOWFormat(BlockdevOptionsGenericCOWFormat *obj) + { +@@ -1256,6 +1276,7 @@ void qapi_free_Qcow2OverlapChecks(Qcow2OverlapChecks *obj) + visit_free(v); + } + ++#if 0 + const QEnumLookup BlockdevQcowEncryptionFormat_lookup = { + .array = (const char *const[]) { + [BLOCKDEV_QCOW_ENCRYPTION_FORMAT_AES] = "aes", +@@ -1322,6 +1343,7 @@ void qapi_free_BlockdevOptionsPreallocate(BlockdevOptionsPreallocate *obj) + visit_type_BlockdevOptionsPreallocate(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj) + { +@@ -1336,6 +1358,7 @@ void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj) + visit_free(v); + } + ++#if 0 + const QEnumLookup SshHostKeyCheckMode_lookup = { + .array = (const char *const[]) { + [SSH_HOST_KEY_CHECK_MODE_NONE] = "none", +@@ -2071,6 +2094,7 @@ void qapi_free_BlockdevOptionsCbw(BlockdevOptionsCbw *obj) + visit_type_BlockdevOptionsCbw(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevOptions(BlockdevOptions *obj) + { +@@ -2111,6 +2135,7 @@ void qapi_free_BlockdevRefOrNull(BlockdevRefOrNull *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevOptionsList(BlockdevOptionsList *obj) + { + Visitor *v; +@@ -2123,6 +2148,7 @@ void qapi_free_BlockdevOptionsList(BlockdevOptionsList *obj) + visit_type_BlockdevOptionsList(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj) + { +@@ -2137,6 +2163,7 @@ void qapi_free_BlockdevCreateOptionsFile(BlockdevCreateOptionsFile *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevCreateOptionsGluster(BlockdevCreateOptionsGluster *obj) + { + Visitor *v; +@@ -2201,6 +2228,7 @@ void qapi_free_BlockdevCreateOptionsQcow(BlockdevCreateOptionsQcow *obj) + visit_type_BlockdevCreateOptionsQcow(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + const QEnumLookup BlockdevQcow2Version_lookup = { + .array = (const char *const[]) { +@@ -2233,6 +2261,7 @@ void qapi_free_BlockdevCreateOptionsQcow2(BlockdevCreateOptionsQcow2 *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevCreateOptionsQed(BlockdevCreateOptionsQed *obj) + { + Visitor *v; +@@ -2360,6 +2389,7 @@ void qapi_free_BlockdevCreateOptionsVpc(BlockdevCreateOptionsVpc *obj) + visit_type_BlockdevCreateOptionsVpc(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevCreateOptions(BlockdevCreateOptions *obj) + { +@@ -2374,6 +2404,7 @@ void qapi_free_BlockdevCreateOptions(BlockdevCreateOptions *obj) + visit_free(v); + } + ++#if 0 + void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj) + { + Visitor *v; +@@ -2386,6 +2417,7 @@ void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj) + visit_type_BlockdevAmendOptionsLUKS(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj) + { +@@ -2441,6 +2473,7 @@ const QEnumLookup QuorumOpType_lookup = { + .size = QUORUM_OP_TYPE__MAX + }; + ++#if 0 + void qapi_free_BlockdevSnapshotInternal(BlockdevSnapshotInternal *obj) + { + Visitor *v; +@@ -2453,6 +2486,7 @@ void qapi_free_BlockdevSnapshotInternal(BlockdevSnapshotInternal *obj) + visit_type_BlockdevSnapshotInternal(v, NULL, &obj, NULL); + visit_free(v); + } ++#endif + + void qapi_free_BlockGraphInfoList(BlockGraphInfoList *obj) + { +diff --git a/qcow2/lib/qapi/qapi-types-common.c b/qcow2/lib/qapi/qapi-types-common.c +index 16045e73..f00b36f1 100644 +--- a/qcow2/lib/qapi/qapi-types-common.c ++++ b/qcow2/lib/qapi/qapi-types-common.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-common.h" +-#include "qapi-visit-common.h" ++#include "qapi/qapi-types-common.h" ++#include "qapi/qapi-visit-common.h" + + const QEnumLookup IoOperationType_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-types-crypto.c b/qcow2/lib/qapi/qapi-types-crypto.c +index cb8f701a..509b0ac5 100644 +--- a/qcow2/lib/qapi/qapi-types-crypto.c ++++ b/qcow2/lib/qapi/qapi-types-crypto.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-crypto.h" +-#include "qapi-visit-crypto.h" ++#include "qapi/qapi-types-crypto.h" ++#include "qapi/qapi-visit-crypto.h" + + const QEnumLookup QCryptoTLSCredsEndpoint_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-types-job.c b/qcow2/lib/qapi/qapi-types-job.c +index eba27079..64010acb 100644 +--- a/qcow2/lib/qapi/qapi-types-job.c ++++ b/qcow2/lib/qapi/qapi-types-job.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-job.h" +-#include "qapi-visit-job.h" ++#include "qapi/qapi-types-job.h" ++#include "qapi/qapi-visit-job.h" + + const QEnumLookup JobType_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-types-sockets.c b/qcow2/lib/qapi/qapi-types-sockets.c +index 568f49e6..1d5556ca 100644 +--- a/qcow2/lib/qapi/qapi-types-sockets.c ++++ b/qcow2/lib/qapi/qapi-types-sockets.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-sockets.h" +-#include "qapi-visit-sockets.h" ++#include "qapi/qapi-types-sockets.h" ++#include "qapi/qapi-visit-sockets.h" + + const QEnumLookup NetworkAddressFamily_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-types-yank.c b/qcow2/lib/qapi/qapi-types-yank.c +index e75e13a2..09c68f5a 100644 +--- a/qcow2/lib/qapi/qapi-types-yank.c ++++ b/qcow2/lib/qapi/qapi-types-yank.c +@@ -12,8 +12,8 @@ + + #include "qemu/osdep.h" + #include "qapi/dealloc-visitor.h" +-#include "qapi-types-yank.h" +-#include "qapi-visit-yank.h" ++#include "qapi/qapi-types-yank.h" ++#include "qapi/qapi-visit-yank.h" + + const QEnumLookup YankInstanceType_lookup = { + .array = (const char *const[]) { +diff --git a/qcow2/lib/qapi/qapi-visit-block-core.c b/qcow2/lib/qapi/qapi-visit-block-core.c +index b8d709cb..7e63d3f5 100644 +--- a/qcow2/lib/qapi/qapi-visit-block-core.c ++++ b/qcow2/lib/qapi/qapi-visit-block-core.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-block-core.h" ++#include "qapi/qapi-visit-block-core.h" + + bool visit_type_SnapshotInfo_members(Visitor *v, SnapshotInfo *obj, Error **errp) + { +@@ -72,6 +72,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_ImageInfoSpecificQCow2EncryptionBase_members(Visitor *v, ImageInfoSpecificQCow2EncryptionBase *obj, Error **errp) + { + if (!visit_type_BlockdevQcow2EncryptionFormat(v, "format", &obj->format, errp)) { +@@ -149,6 +150,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_Qcow2BitmapInfoList(Visitor *v, const char *name, + Qcow2BitmapInfoList **obj, Error **errp) +@@ -181,7 +183,7 @@ out_obj: + bool visit_type_ImageInfoSpecificQCow2_members(Visitor *v, ImageInfoSpecificQCow2 *obj, Error **errp) + { + bool has_data_file = !!obj->data_file; +- bool has_encrypt = !!obj->encrypt; ++ //bool has_encrypt = !!obj->encrypt; + + if (!visit_type_str(v, "compat", &obj->compat, errp)) { + return false; +@@ -214,11 +216,13 @@ bool visit_type_ImageInfoSpecificQCow2_members(Visitor *v, ImageInfoSpecificQCow + if (!visit_type_int(v, "refcount-bits", &obj->refcount_bits, errp)) { + return false; + } ++#if 0 + if (visit_optional(v, "encrypt", &has_encrypt)) { + if (!visit_type_ImageInfoSpecificQCow2Encryption(v, "encrypt", &obj->encrypt, errp)) { + return false; + } + } ++#endif + if (visit_optional(v, "bitmaps", &obj->has_bitmaps)) { + if (!visit_type_Qcow2BitmapInfoList(v, "bitmaps", &obj->bitmaps, errp)) { + return false; +@@ -257,6 +261,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_VmdkExtentInfoList(Visitor *v, const char *name, + VmdkExtentInfoList **obj, Error **errp) + { +@@ -416,6 +421,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_ImageInfoSpecificFile_members(Visitor *v, ImageInfoSpecificFile *obj, Error **errp) + { +@@ -471,6 +477,7 @@ bool visit_type_ImageInfoSpecificQCow2Wrapper_members(Visitor *v, ImageInfoSpeci + return true; + } + ++#if 0 + bool visit_type_ImageInfoSpecificQCow2Wrapper(Visitor *v, const char *name, + ImageInfoSpecificQCow2Wrapper **obj, Error **errp) + { +@@ -602,6 +609,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_ImageInfoSpecificFileWrapper_members(Visitor *v, ImageInfoSpecificFileWrapper *obj, Error **errp) + { +@@ -611,6 +619,7 @@ bool visit_type_ImageInfoSpecificFileWrapper_members(Visitor *v, ImageInfoSpecif + return true; + } + ++#if 0 + bool visit_type_ImageInfoSpecificFileWrapper(Visitor *v, const char *name, + ImageInfoSpecificFileWrapper **obj, Error **errp) + { +@@ -637,6 +646,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_q_obj_ImageInfoSpecific_base_members(Visitor *v, q_obj_ImageInfoSpecific_base *obj, Error **errp) + { +@@ -654,12 +664,14 @@ bool visit_type_ImageInfoSpecific_members(Visitor *v, ImageInfoSpecific *obj, Er + switch (obj->type) { + case IMAGE_INFO_SPECIFIC_KIND_QCOW2: + return visit_type_ImageInfoSpecificQCow2Wrapper_members(v, &obj->u.qcow2, errp); ++#if 0 + case IMAGE_INFO_SPECIFIC_KIND_VMDK: + return visit_type_ImageInfoSpecificVmdkWrapper_members(v, &obj->u.vmdk, errp); + case IMAGE_INFO_SPECIFIC_KIND_LUKS: + return visit_type_ImageInfoSpecificLUKSWrapper_members(v, &obj->u.luks, errp); + case IMAGE_INFO_SPECIFIC_KIND_RBD: + return visit_type_ImageInfoSpecificRbdWrapper_members(v, &obj->u.rbd, errp); ++#endif + case IMAGE_INFO_SPECIFIC_KIND_FILE: + return visit_type_ImageInfoSpecificFileWrapper_members(v, &obj->u.file, errp); + default: +@@ -1497,6 +1509,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockLatencyHistogramInfo_members(Visitor *v, BlockLatencyHistogramInfo *obj, Error **errp) + { + if (!visit_type_uint64List(v, "boundaries", &obj->boundaries, errp)) { +@@ -1534,6 +1547,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockInfo_members(Visitor *v, BlockInfo *obj, Error **errp) + { +@@ -1673,6 +1687,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockDeviceTimedStats_members(Visitor *v, BlockDeviceTimedStats *obj, Error **errp) + { + if (!visit_type_int(v, "interval_length", &obj->interval_length, errp)) { +@@ -2323,6 +2338,7 @@ bool visit_type_MirrorCopyMode(Visitor *v, const char *name, + *obj = value; + return ok; + } ++#endif + + bool visit_type_BlockJobInfoMirror_members(Visitor *v, BlockJobInfoMirror *obj, Error **errp) + { +@@ -2332,6 +2348,7 @@ bool visit_type_BlockJobInfoMirror_members(Visitor *v, BlockJobInfoMirror *obj, + return true; + } + ++#if 0 + bool visit_type_BlockJobInfoMirror(Visitor *v, const char *name, + BlockJobInfoMirror **obj, Error **errp) + { +@@ -2358,6 +2375,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_q_obj_BlockJobInfo_base_members(Visitor *v, q_obj_BlockJobInfo_base *obj, Error **errp) + { +@@ -2492,6 +2510,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_q_obj_block_resize_arg_members(Visitor *v, q_obj_block_resize_arg *obj, Error **errp) + { + bool has_device = !!obj->device; +@@ -2977,6 +2996,7 @@ bool visit_type_q_obj_query_named_block_nodes_arg_members(Visitor *v, q_obj_quer + } + return true; + } ++#endif + + bool visit_type_BlockDeviceInfoList(Visitor *v, const char *name, + BlockDeviceInfoList **obj, Error **errp) +@@ -3006,6 +3026,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_XDbgBlockGraphNodeType(Visitor *v, const char *name, + XDbgBlockGraphNodeType *obj, Error **errp) + { +@@ -3349,6 +3370,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockDirtyBitmap_members(Visitor *v, BlockDirtyBitmap *obj, Error **errp) + { +@@ -3517,6 +3539,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockDirtyBitmapMerge_members(Visitor *v, BlockDirtyBitmapMerge *obj, Error **errp) + { + if (!visit_type_str(v, "node", &obj->node, errp)) { +@@ -4397,6 +4420,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevDiscardOptions(Visitor *v, const char *name, + BlockdevDiscardOptions *obj, Error **errp) +@@ -4550,6 +4574,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevOptionsNull_members(Visitor *v, BlockdevOptionsNull *obj, Error **errp) + { + if (visit_optional(v, "size", &obj->has_size)) { +@@ -4691,6 +4716,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevOptionsGenericFormat_members(Visitor *v, BlockdevOptionsGenericFormat *obj, Error **errp) + { +@@ -4700,6 +4726,7 @@ bool visit_type_BlockdevOptionsGenericFormat_members(Visitor *v, BlockdevOptions + return true; + } + ++#if 0 + bool visit_type_BlockdevOptionsGenericFormat(Visitor *v, const char *name, + BlockdevOptionsGenericFormat **obj, Error **errp) + { +@@ -4774,6 +4801,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevOptionsGenericCOWFormat_members(Visitor *v, BlockdevOptionsGenericCOWFormat *obj, Error **errp) + { +@@ -4956,6 +4984,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevQcowEncryptionFormat(Visitor *v, const char *name, + BlockdevQcowEncryptionFormat *obj, Error **errp) + { +@@ -5160,11 +5189,12 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevOptionsQcow2_members(Visitor *v, BlockdevOptionsQcow2 *obj, Error **errp) + { + bool has_overlap_check = !!obj->overlap_check; +- bool has_encrypt = !!obj->encrypt; ++ //bool has_encrypt = !!obj->encrypt; + bool has_data_file = !!obj->data_file; + + if (!visit_type_BlockdevOptionsGenericCOWFormat_members(v, (BlockdevOptionsGenericCOWFormat *)obj, errp)) { +@@ -5225,11 +5255,13 @@ bool visit_type_BlockdevOptionsQcow2_members(Visitor *v, BlockdevOptionsQcow2 *o + return false; + } + } ++#if 0 + if (visit_optional(v, "encrypt", &has_encrypt)) { + if (!visit_type_BlockdevQcow2Encryption(v, "encrypt", &obj->encrypt, errp)) { + return false; + } + } ++#endif + if (visit_optional(v, "data-file", &has_data_file)) { + if (!visit_type_BlockdevRef(v, "data-file", &obj->data_file, errp)) { + return false; +@@ -5265,6 +5297,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_SshHostKeyCheckMode(Visitor *v, const char *name, + SshHostKeyCheckMode *obj, Error **errp) + { +@@ -5915,6 +5948,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevOptionsGluster_members(Visitor *v, BlockdevOptionsGluster *obj, Error **errp) + { + bool has_logfile = !!obj->logfile; +@@ -5967,6 +6001,7 @@ out_obj: + } + return ok; + } ++#endif + + #if defined(CONFIG_BLKIO) + bool visit_type_BlockdevOptionsIoUring_members(Visitor *v, BlockdevOptionsIoUring *obj, Error **errp) +@@ -6153,6 +6188,7 @@ out_obj: + } + #endif /* defined(CONFIG_BLKIO) */ + ++#if 0 + bool visit_type_IscsiTransport(Visitor *v, const char *name, + IscsiTransport *obj, Error **errp) + { +@@ -6739,6 +6775,7 @@ out_obj: + } + return ok; + } ++#endif + + #if defined(CONFIG_REPLICATION) + bool visit_type_ReplicationMode(Visitor *v, const char *name, +@@ -7159,6 +7196,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevOptionsNbd_members(Visitor *v, BlockdevOptionsNbd *obj, Error **errp) + { + bool has_export = !!obj->export; +@@ -7233,6 +7271,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevOptionsRaw_members(Visitor *v, BlockdevOptionsRaw *obj, Error **errp) + { +@@ -7422,6 +7461,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_q_obj_BlockdevOptions_base_members(Visitor *v, q_obj_BlockdevOptions_base *obj, Error **errp) + { +@@ -7475,6 +7515,7 @@ bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error + return false; + } + switch (obj->driver) { ++#if 0 + case BLOCKDEV_DRIVER_BLKDEBUG: + return visit_type_BlockdevOptionsBlkdebug_members(v, &obj->u.blkdebug, errp); + case BLOCKDEV_DRIVER_BLKLOGWRITES: +@@ -7495,8 +7536,10 @@ bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error + return visit_type_BlockdevOptionsCor_members(v, &obj->u.copy_on_read, errp); + case BLOCKDEV_DRIVER_DMG: + return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.dmg, errp); ++#endif + case BLOCKDEV_DRIVER_FILE: + return visit_type_BlockdevOptionsFile_members(v, &obj->u.file, errp); ++#if 0 + case BLOCKDEV_DRIVER_FTP: + return visit_type_BlockdevOptionsCurlFtp_members(v, &obj->u.ftp, errp); + case BLOCKDEV_DRIVER_FTPS: +@@ -7541,8 +7584,10 @@ bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error + return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.parallels, errp); + case BLOCKDEV_DRIVER_PREALLOCATE: + return visit_type_BlockdevOptionsPreallocate_members(v, &obj->u.preallocate, errp); ++#endif + case BLOCKDEV_DRIVER_QCOW2: + return visit_type_BlockdevOptionsQcow2_members(v, &obj->u.qcow2, errp); ++#if 0 + case BLOCKDEV_DRIVER_QCOW: + return visit_type_BlockdevOptionsQcow_members(v, &obj->u.qcow, errp); + case BLOCKDEV_DRIVER_QED: +@@ -7585,6 +7630,7 @@ bool visit_type_BlockdevOptions_members(Visitor *v, BlockdevOptions *obj, Error + return visit_type_BlockdevOptionsGenericFormat_members(v, &obj->u.vpc, errp); + case BLOCKDEV_DRIVER_VVFAT: + return visit_type_BlockdevOptionsVVFAT_members(v, &obj->u.vvfat, errp); ++#endif + default: + abort(); + } +@@ -7717,6 +7763,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevOptionsList(Visitor *v, const char *name, + BlockdevOptionsList **obj, Error **errp) + { +@@ -7760,6 +7807,7 @@ bool visit_type_q_obj_blockdev_del_arg_members(Visitor *v, q_obj_blockdev_del_ar + } + return true; + } ++#endif + + bool visit_type_BlockdevCreateOptionsFile_members(Visitor *v, BlockdevCreateOptionsFile *obj, Error **errp) + { +@@ -7814,6 +7862,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevCreateOptionsGluster_members(Visitor *v, BlockdevCreateOptionsGluster *obj, Error **errp) + { + if (!visit_type_BlockdevOptionsGluster(v, "location", &obj->location, errp)) { +@@ -8044,6 +8093,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevQcow2Version(Visitor *v, const char *name, + BlockdevQcow2Version *obj, Error **errp) +@@ -8067,7 +8117,7 @@ bool visit_type_BlockdevCreateOptionsQcow2_members(Visitor *v, BlockdevCreateOpt + { + bool has_data_file = !!obj->data_file; + bool has_backing_file = !!obj->backing_file; +- bool has_encrypt = !!obj->encrypt; ++ //bool has_encrypt = !!obj->encrypt; + + if (!visit_type_BlockdevRef(v, "file", &obj->file, errp)) { + return false; +@@ -8105,11 +8155,13 @@ bool visit_type_BlockdevCreateOptionsQcow2_members(Visitor *v, BlockdevCreateOpt + return false; + } + } ++#if 0 + if (visit_optional(v, "encrypt", &has_encrypt)) { + if (!visit_type_QCryptoBlockCreateOptions(v, "encrypt", &obj->encrypt, errp)) { + return false; + } + } ++#endif + if (visit_optional(v, "cluster-size", &obj->has_cluster_size)) { + if (!visit_type_size(v, "cluster-size", &obj->cluster_size, errp)) { + return false; +@@ -8165,6 +8217,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_BlockdevCreateOptionsQed_members(Visitor *v, BlockdevCreateOptionsQed *obj, Error **errp) + { + bool has_backing_file = !!obj->backing_file; +@@ -8574,6 +8627,7 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_q_obj_BlockdevCreateOptions_base_members(Visitor *v, q_obj_BlockdevCreateOptions_base *obj, Error **errp) + { +@@ -8591,6 +8645,7 @@ bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions + switch (obj->driver) { + case BLOCKDEV_DRIVER_FILE: + return visit_type_BlockdevCreateOptionsFile_members(v, &obj->u.file, errp); ++#if 0 + case BLOCKDEV_DRIVER_GLUSTER: + return visit_type_BlockdevCreateOptionsGluster_members(v, &obj->u.gluster, errp); + case BLOCKDEV_DRIVER_LUKS: +@@ -8601,8 +8656,10 @@ bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions + return visit_type_BlockdevCreateOptionsParallels_members(v, &obj->u.parallels, errp); + case BLOCKDEV_DRIVER_QCOW: + return visit_type_BlockdevCreateOptionsQcow_members(v, &obj->u.qcow, errp); ++#endif + case BLOCKDEV_DRIVER_QCOW2: + return visit_type_BlockdevCreateOptionsQcow2_members(v, &obj->u.qcow2, errp); ++#if 0 + case BLOCKDEV_DRIVER_QED: + return visit_type_BlockdevCreateOptionsQed_members(v, &obj->u.qed, errp); + case BLOCKDEV_DRIVER_RBD: +@@ -8699,6 +8756,7 @@ bool visit_type_BlockdevCreateOptions_members(Visitor *v, BlockdevCreateOptions + #endif /* defined(CONFIG_BLKIO) */ + case BLOCKDEV_DRIVER_VVFAT: + break; ++#endif + default: + abort(); + } +@@ -8732,6 +8790,7 @@ out_obj: + return ok; + } + ++#if 0 + bool visit_type_q_obj_blockdev_create_arg_members(Visitor *v, q_obj_blockdev_create_arg *obj, Error **errp) + { + if (!visit_type_str(v, "job-id", &obj->job_id, errp)) { +@@ -8777,9 +8836,11 @@ out_obj: + } + return ok; + } ++#endif + + bool visit_type_BlockdevAmendOptionsQcow2_members(Visitor *v, BlockdevAmendOptionsQcow2 *obj, Error **errp) + { ++#if 0 + bool has_encrypt = !!obj->encrypt; + + if (visit_optional(v, "encrypt", &has_encrypt)) { +@@ -8787,6 +8848,7 @@ bool visit_type_BlockdevAmendOptionsQcow2_members(Visitor *v, BlockdevAmendOptio + return false; + } + } ++#endif + return true; + } + +@@ -8831,10 +8893,13 @@ bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *o + return false; + } + switch (obj->driver) { ++#if 0 + case BLOCKDEV_DRIVER_LUKS: + return visit_type_BlockdevAmendOptionsLUKS_members(v, &obj->u.luks, errp); ++#endif + case BLOCKDEV_DRIVER_QCOW2: + return visit_type_BlockdevAmendOptionsQcow2_members(v, &obj->u.qcow2, errp); ++#if 0 + case BLOCKDEV_DRIVER_BLKDEBUG: + break; + case BLOCKDEV_DRIVER_BLKLOGWRITES: +@@ -8855,8 +8920,10 @@ bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *o + break; + case BLOCKDEV_DRIVER_DMG: + break; ++#endif + case BLOCKDEV_DRIVER_FILE: + break; ++#if 0 + case BLOCKDEV_DRIVER_SNAPSHOT_ACCESS: + break; + case BLOCKDEV_DRIVER_FTP: +@@ -8941,6 +9008,7 @@ bool visit_type_BlockdevAmendOptions_members(Visitor *v, BlockdevAmendOptions *o + break; + case BLOCKDEV_DRIVER_VVFAT: + break; ++#endif + default: + abort(); + } +@@ -9177,6 +9245,7 @@ bool visit_type_q_obj_BLOCK_WRITE_THRESHOLD_arg_members(Visitor *v, q_obj_BLOCK_ + return true; + } + ++#if 0 + bool visit_type_q_obj_block_set_write_threshold_arg_members(Visitor *v, q_obj_block_set_write_threshold_arg *obj, Error **errp) + { + if (!visit_type_str(v, "node-name", &obj->node_name, errp)) { +@@ -9224,6 +9293,7 @@ bool visit_type_q_obj_x_blockdev_set_iothread_arg_members(Visitor *v, q_obj_x_bl + } + return true; + } ++#endif + + bool visit_type_QuorumOpType(Visitor *v, const char *name, + QuorumOpType *obj, Error **errp) +@@ -9272,6 +9342,7 @@ bool visit_type_q_obj_QUORUM_REPORT_BAD_arg_members(Visitor *v, q_obj_QUORUM_REP + return true; + } + ++#if 0 + bool visit_type_BlockdevSnapshotInternal_members(Visitor *v, BlockdevSnapshotInternal *obj, Error **errp) + { + if (!visit_type_str(v, "device", &obj->device, errp)) { +@@ -9330,6 +9401,7 @@ bool visit_type_q_obj_blockdev_snapshot_delete_internal_sync_arg_members(Visitor + } + return true; + } ++#endif + + bool visit_type_BlockGraphInfoList(Visitor *v, const char *name, + BlockGraphInfoList **obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-common.c b/qcow2/lib/qapi/qapi-visit-common.c +index cacb730a..96e8d05f 100644 +--- a/qcow2/lib/qapi/qapi-visit-common.c ++++ b/qcow2/lib/qapi/qapi-visit-common.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-common.h" ++#include "qapi/qapi-visit-common.h" + + bool visit_type_IoOperationType(Visitor *v, const char *name, + IoOperationType *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-core.c b/qcow2/lib/qapi/qapi-visit-core.c +index 6c13510a..895f89fd 100644 +--- a/qcow2/lib/qapi/qapi-visit-core.c ++++ b/qcow2/lib/qapi/qapi-visit-core.c +@@ -18,7 +18,7 @@ + #include "qapi/qmp/qerror.h" + #include "qapi/visitor.h" + #include "qapi/visitor-impl.h" +-#include "trace.h" ++//#include "trace.h" + + /* Zero-initialization must result in default policy */ + QEMU_BUILD_BUG_ON(COMPAT_POLICY_INPUT_ACCEPT || COMPAT_POLICY_OUTPUT_ACCEPT); +@@ -27,7 +27,7 @@ QEMU_BUILD_BUG_ON(COMPAT_POLICY_INPUT_ACCEPT || COMPAT_POLICY_OUTPUT_ACCEPT); + void visit_complete(Visitor *v, void *opaque) + { + assert(v->type != VISITOR_OUTPUT || v->complete); +- trace_visit_complete(v, opaque); ++ //trace_visit_complete(v, opaque); + if (v->complete) { + v->complete(v, opaque); + } +@@ -35,7 +35,7 @@ void visit_complete(Visitor *v, void *opaque) + + void visit_free(Visitor *v) + { +- trace_visit_free(v); ++ //trace_visit_free(v); + if (v) { + v->free(v); + } +@@ -46,7 +46,7 @@ bool visit_start_struct(Visitor *v, const char *name, void **obj, + { + bool ok; + +- trace_visit_start_struct(v, name, obj, size); ++ //trace_visit_start_struct(v, name, obj, size); + if (obj) { + assert(size); + assert(!(v->type & VISITOR_OUTPUT) || *obj); +@@ -60,13 +60,13 @@ bool visit_start_struct(Visitor *v, const char *name, void **obj, + + bool visit_check_struct(Visitor *v, Error **errp) + { +- trace_visit_check_struct(v); ++ //trace_visit_check_struct(v); + return v->check_struct ? v->check_struct(v, errp) : true; + } + + void visit_end_struct(Visitor *v, void **obj) + { +- trace_visit_end_struct(v, obj); ++ //trace_visit_end_struct(v, obj); + v->end_struct(v, obj); + } + +@@ -76,7 +76,7 @@ bool visit_start_list(Visitor *v, const char *name, GenericList **list, + bool ok; + + assert(!list || size >= sizeof(GenericList)); +- trace_visit_start_list(v, name, list, size); ++ //trace_visit_start_list(v, name, list, size); + ok = v->start_list(v, name, list, size, errp); + if (list && (v->type & VISITOR_INPUT)) { + assert(ok || !*list); +@@ -87,19 +87,19 @@ bool visit_start_list(Visitor *v, const char *name, GenericList **list, + GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size) + { + assert(tail && size >= sizeof(GenericList)); +- trace_visit_next_list(v, tail, size); ++ //trace_visit_next_list(v, tail, size); + return v->next_list(v, tail, size); + } + + bool visit_check_list(Visitor *v, Error **errp) + { +- trace_visit_check_list(v); ++ //trace_visit_check_list(v); + return v->check_list ? v->check_list(v, errp) : true; + } + + void visit_end_list(Visitor *v, void **obj) + { +- trace_visit_end_list(v, obj); ++ //trace_visit_end_list(v, obj); + v->end_list(v, obj); + } + +@@ -111,7 +111,7 @@ bool visit_start_alternate(Visitor *v, const char *name, + + assert(obj && size >= sizeof(GenericAlternate)); + assert(!(v->type & VISITOR_OUTPUT) || *obj); +- trace_visit_start_alternate(v, name, obj, size); ++ //trace_visit_start_alternate(v, name, obj, size); + if (!v->start_alternate) { + assert(!(v->type & VISITOR_INPUT)); + return true; +@@ -125,7 +125,7 @@ bool visit_start_alternate(Visitor *v, const char *name, + + void visit_end_alternate(Visitor *v, void **obj) + { +- trace_visit_end_alternate(v, obj); ++ //trace_visit_end_alternate(v, obj); + if (v->end_alternate) { + v->end_alternate(v, obj); + } +@@ -133,7 +133,7 @@ void visit_end_alternate(Visitor *v, void **obj) + + bool visit_optional(Visitor *v, const char *name, bool *present) + { +- trace_visit_optional(v, name, present); ++ //trace_visit_optional(v, name, present); + if (v->optional) { + v->optional(v, name, present); + } +@@ -143,7 +143,7 @@ bool visit_optional(Visitor *v, const char *name, bool *present) + bool visit_policy_reject(Visitor *v, const char *name, + unsigned special_features, Error **errp) + { +- trace_visit_policy_reject(v, name); ++ //trace_visit_policy_reject(v, name); + if (v->policy_reject) { + return v->policy_reject(v, name, special_features, errp); + } +@@ -153,7 +153,7 @@ bool visit_policy_reject(Visitor *v, const char *name, + bool visit_policy_skip(Visitor *v, const char *name, + unsigned special_features) + { +- trace_visit_policy_skip(v, name); ++ //trace_visit_policy_skip(v, name); + if (v->policy_skip) { + return v->policy_skip(v, name, special_features); + } +@@ -178,7 +178,7 @@ bool visit_is_dealloc(Visitor *v) + bool visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp) + { + assert(obj); +- trace_visit_type_int(v, name, obj); ++ //trace_visit_type_int(v, name, obj); + return v->type_int64(v, name, obj, errp); + } + +@@ -208,7 +208,7 @@ bool visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, + uint64_t value; + bool ok; + +- trace_visit_type_uint8(v, name, obj); ++ //trace_visit_type_uint8(v, name, obj); + value = *obj; + ok = visit_type_uintN(v, &value, name, UINT8_MAX, "uint8_t", errp); + *obj = value; +@@ -221,7 +221,7 @@ bool visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, + uint64_t value; + bool ok; + +- trace_visit_type_uint16(v, name, obj); ++ //trace_visit_type_uint16(v, name, obj); + value = *obj; + ok = visit_type_uintN(v, &value, name, UINT16_MAX, "uint16_t", errp); + *obj = value; +@@ -234,7 +234,7 @@ bool visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, + uint64_t value; + bool ok; + +- trace_visit_type_uint32(v, name, obj); ++ //trace_visit_type_uint32(v, name, obj); + value = *obj; + ok = visit_type_uintN(v, &value, name, UINT32_MAX, "uint32_t", errp); + *obj = value; +@@ -245,7 +245,7 @@ bool visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp) + { + assert(obj); +- trace_visit_type_uint64(v, name, obj); ++ //trace_visit_type_uint64(v, name, obj); + return v->type_uint64(v, name, obj, errp); + } + +@@ -275,7 +275,7 @@ bool visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp) + int64_t value; + bool ok; + +- trace_visit_type_int8(v, name, obj); ++ //trace_visit_type_int8(v, name, obj); + value = *obj; + ok = visit_type_intN(v, &value, name, INT8_MIN, INT8_MAX, "int8_t", errp); + *obj = value; +@@ -288,7 +288,7 @@ bool visit_type_int16(Visitor *v, const char *name, int16_t *obj, + int64_t value; + bool ok; + +- trace_visit_type_int16(v, name, obj); ++ //trace_visit_type_int16(v, name, obj); + value = *obj; + ok = visit_type_intN(v, &value, name, INT16_MIN, INT16_MAX, "int16_t", + errp); +@@ -302,7 +302,7 @@ bool visit_type_int32(Visitor *v, const char *name, int32_t *obj, + int64_t value; + bool ok; + +- trace_visit_type_int32(v, name, obj); ++ //trace_visit_type_int32(v, name, obj); + value = *obj; + ok = visit_type_intN(v, &value, name, INT32_MIN, INT32_MAX, "int32_t", + errp); +@@ -314,7 +314,7 @@ bool visit_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) + { + assert(obj); +- trace_visit_type_int64(v, name, obj); ++ //trace_visit_type_int64(v, name, obj); + return v->type_int64(v, name, obj, errp); + } + +@@ -322,7 +322,7 @@ bool visit_type_size(Visitor *v, const char *name, uint64_t *obj, + Error **errp) + { + assert(obj); +- trace_visit_type_size(v, name, obj); ++ //trace_visit_type_size(v, name, obj); + if (v->type_size) { + return v->type_size(v, name, obj, errp); + } +@@ -332,7 +332,7 @@ bool visit_type_size(Visitor *v, const char *name, uint64_t *obj, + bool visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) + { + assert(obj); +- trace_visit_type_bool(v, name, obj); ++ //trace_visit_type_bool(v, name, obj); + return v->type_bool(v, name, obj, errp); + } + +@@ -345,7 +345,7 @@ bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) + * can enable: + assert(!(v->type & VISITOR_OUTPUT) || *obj); + */ +- trace_visit_type_str(v, name, obj); ++ //trace_visit_type_str(v, name, obj); + ok = v->type_str(v, name, obj, errp); + if (v->type & VISITOR_INPUT) { + assert(ok != !*obj); +@@ -357,7 +357,7 @@ bool visit_type_number(Visitor *v, const char *name, double *obj, + Error **errp) + { + assert(obj); +- trace_visit_type_number(v, name, obj); ++ //trace_visit_type_number(v, name, obj); + return v->type_number(v, name, obj, errp); + } + +@@ -367,7 +367,7 @@ bool visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) + + assert(obj); + assert(v->type != VISITOR_OUTPUT || *obj); +- trace_visit_type_any(v, name, obj); ++ //trace_visit_type_any(v, name, obj); + ok = v->type_any(v, name, obj, errp); + if (v->type == VISITOR_INPUT) { + assert(ok != !*obj); +@@ -378,7 +378,7 @@ bool visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) + bool visit_type_null(Visitor *v, const char *name, QNull **obj, + Error **errp) + { +- trace_visit_type_null(v, name, obj); ++ //trace_visit_type_null(v, name, obj); + return v->type_null(v, name, obj, errp); + } + +@@ -425,7 +425,7 @@ bool visit_type_enum(Visitor *v, const char *name, int *obj, + const QEnumLookup *lookup, Error **errp) + { + assert(obj && lookup); +- trace_visit_type_enum(v, name, obj); ++ //trace_visit_type_enum(v, name, obj); + switch (v->type) { + case VISITOR_INPUT: + return input_type_enum(v, name, obj, lookup, errp); +diff --git a/qcow2/lib/qapi/qapi-visit-crypto.c b/qcow2/lib/qapi/qapi-visit-crypto.c +index 09eab7e3..b616ccbc 100644 +--- a/qcow2/lib/qapi/qapi-visit-crypto.c ++++ b/qcow2/lib/qapi/qapi-visit-crypto.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-crypto.h" ++#include "qapi/qapi-visit-crypto.h" + + bool visit_type_QCryptoTLSCredsEndpoint(Visitor *v, const char *name, + QCryptoTLSCredsEndpoint *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-job.c b/qcow2/lib/qapi/qapi-visit-job.c +index 092f9a03..77c6bcd0 100644 +--- a/qcow2/lib/qapi/qapi-visit-job.c ++++ b/qcow2/lib/qapi/qapi-visit-job.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-job.h" ++#include "qapi/qapi-visit-job.h" + + bool visit_type_JobType(Visitor *v, const char *name, + JobType *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-sockets.c b/qcow2/lib/qapi/qapi-visit-sockets.c +index 34d0b894..a1dcc91f 100644 +--- a/qcow2/lib/qapi/qapi-visit-sockets.c ++++ b/qcow2/lib/qapi/qapi-visit-sockets.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-sockets.h" ++#include "qapi/qapi-visit-sockets.h" + + bool visit_type_NetworkAddressFamily(Visitor *v, const char *name, + NetworkAddressFamily *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qapi-visit-yank.c b/qcow2/lib/qapi/qapi-visit-yank.c +index f5d5bf25..569a88db 100644 +--- a/qcow2/lib/qapi/qapi-visit-yank.c ++++ b/qcow2/lib/qapi/qapi-visit-yank.c +@@ -12,7 +12,7 @@ + + #include "qemu/osdep.h" + #include "qapi/error.h" +-#include "qapi-visit-yank.h" ++#include "qapi/qapi-visit-yank.h" + + bool visit_type_YankInstanceType(Visitor *v, const char *name, + YankInstanceType *obj, Error **errp) +diff --git a/qcow2/lib/qapi/qmp-dispatch.c b/qcow2/lib/qapi/qmp-dispatch.c +index 176b5494..1bce84ea 100644 +--- a/qcow2/lib/qapi/qmp-dispatch.c ++++ b/qcow2/lib/qapi/qmp-dispatch.c +@@ -41,6 +41,7 @@ Visitor *qobject_output_visitor_new_qmp(QObject **result) + return v; + } + ++#if 0 + static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob, + Error **errp) + { +@@ -294,3 +295,4 @@ out: + + return rsp; + } ++#endif +diff --git a/qcow2/lib/qapi/qobject-input-visitor.c b/qcow2/lib/qapi/qobject-input-visitor.c +index f110a804..408ade81 100644 +--- a/qcow2/lib/qapi/qobject-input-visitor.c ++++ b/qcow2/lib/qapi/qobject-input-visitor.c +@@ -28,7 +28,7 @@ + #include "qapi/qmp/qnum.h" + #include "qapi/qmp/qstring.h" + #include "qemu/cutils.h" +-#include "qemu/keyval.h" ++//#include "qemu/keyval.h" + + typedef struct StackObject { + const char *name; /* Name of @obj in its parent, if any */ +@@ -746,6 +746,7 @@ Visitor *qobject_input_visitor_new_keyval(QObject *obj) + return &v->visitor; + } + ++#if 0 + Visitor *qobject_input_visitor_new_str(const char *str, + const char *implied_key, + Error **errp) +@@ -774,3 +775,4 @@ Visitor *qobject_input_visitor_new_str(const char *str, + + return v; + } ++#endif +diff --git a/qcow2/lib/qcow2-cache.c b/qcow2/lib/qcow2-cache.c +index 23d9588b..11453579 100644 +--- a/qcow2/lib/qcow2-cache.c ++++ b/qcow2/lib/qcow2-cache.c +@@ -26,7 +26,7 @@ + #include "block/block-io.h" + #include "qemu/memalign.h" + #include "qcow2.h" +-#include "trace.h" ++//#include "trace.h" + + typedef struct Qcow2CachedTable { + int64_t offset; +@@ -189,8 +189,8 @@ qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) + return 0; + } + +- trace_qcow2_cache_entry_flush(qemu_coroutine_self(), +- c == s->l2_table_cache, i); ++ //trace_qcow2_cache_entry_flush(qemu_coroutine_self(), ++ // c == s->l2_table_cache, i); + + if (c->depends) { + ret = qcow2_cache_flush_dependency(bs, c); +@@ -239,12 +239,12 @@ qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) + + int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c) + { +- BDRVQcow2State *s = bs->opaque; ++ //BDRVQcow2State *s = bs->opaque; + int result = 0; + int ret; + int i; + +- trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); ++ //trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); + + for (i = 0; i < c->size; i++) { + ret = qcow2_cache_entry_flush(bs, c, i); +@@ -333,8 +333,8 @@ qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + + assert(offset != 0); + +- trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, +- offset, read_from_disk); ++ //trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, ++ // offset, read_from_disk); + + if (!QEMU_IS_ALIGNED(offset, c->table_size)) { + qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s " +@@ -367,16 +367,16 @@ qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + + /* Cache miss: write a table back and replace it */ + i = min_lru_index; +- trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), +- c == s->l2_table_cache, i); ++ //trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), ++ // c == s->l2_table_cache, i); + + ret = qcow2_cache_entry_flush(bs, c, i); + if (ret < 0) { + return ret; + } + +- trace_qcow2_cache_get_read(qemu_coroutine_self(), +- c == s->l2_table_cache, i); ++ //trace_qcow2_cache_get_read(qemu_coroutine_self(), ++ // c == s->l2_table_cache, i); + c->entries[i].offset = 0; + if (read_from_disk) { + if (c == s->l2_table_cache) { +@@ -397,8 +397,8 @@ found: + c->entries[i].ref++; + *table = qcow2_cache_get_table_addr(c, i); + +- trace_qcow2_cache_get_done(qemu_coroutine_self(), +- c == s->l2_table_cache, i); ++ //trace_qcow2_cache_get_done(qemu_coroutine_self(), ++ // c == s->l2_table_cache, i); + + return 0; + } +diff --git a/qcow2/lib/qcow2-cluster.c b/qcow2/lib/qcow2-cluster.c +index ce8c0076..f71ff791 100644 +--- a/qcow2/lib/qcow2-cluster.c ++++ b/qcow2/lib/qcow2-cluster.c +@@ -30,7 +30,7 @@ + #include "qcow2.h" + #include "qemu/bswap.h" + #include "qemu/memalign.h" +-#include "trace.h" ++//#include "trace.h" + + int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, + uint64_t exact_size) +@@ -281,7 +281,7 @@ static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) + + old_l2_offset = s->l1_table[l1_index]; + +- trace_qcow2_l2_allocate(bs, l1_index); ++ //trace_qcow2_l2_allocate(bs, l1_index); + + /* allocate a new l2 entry */ + +@@ -312,7 +312,7 @@ static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) + slice_size2 = s->l2_slice_size * l2_entry_size(s); + n_slices = s->cluster_size / slice_size2; + +- trace_qcow2_l2_allocate_get_empty(bs, l1_index); ++ //trace_qcow2_l2_allocate_get_empty(bs, l1_index); + for (slice = 0; slice < n_slices; slice++) { + ret = qcow2_cache_get_empty(bs, s->l2_table_cache, + l2_offset + slice * slice_size2, +@@ -345,7 +345,7 @@ static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) + /* write the l2 slice to the file */ + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); + +- trace_qcow2_l2_allocate_write_l2(bs, l1_index); ++ //trace_qcow2_l2_allocate_write_l2(bs, l1_index); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); + } +@@ -356,18 +356,18 @@ static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) + } + + /* update the L1 entry */ +- trace_qcow2_l2_allocate_write_l1(bs, l1_index); ++ //trace_qcow2_l2_allocate_write_l1(bs, l1_index); + s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; + ret = qcow2_write_l1_entry(bs, l1_index); + if (ret < 0) { + goto fail; + } + +- trace_qcow2_l2_allocate_done(bs, l1_index, 0); ++ //trace_qcow2_l2_allocate_done(bs, l1_index, 0); + return 0; + + fail: +- trace_qcow2_l2_allocate_done(bs, l1_index, ret); ++ //trace_qcow2_l2_allocate_done(bs, l1_index, ret); + if (l2_slice != NULL) { + qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); + } +@@ -959,6 +959,7 @@ perform_cow(BlockDriverState *bs, QCowL2Meta *m) + goto fail; + } + ++#if 0 + /* Encrypt the data if necessary before writing it */ + if (bs->encrypted) { + ret = qcow2_co_encrypt(bs, +@@ -977,6 +978,7 @@ perform_cow(BlockDriverState *bs, QCowL2Meta *m) + goto fail; + } + } ++#endif + + /* And now we can write everything. If we have the guest data we + * can write everything in one single operation */ +@@ -1033,7 +1035,7 @@ int coroutine_fn qcow2_alloc_cluster_link_l2(BlockDriverState *bs, + uint64_t *old_cluster, *l2_slice; + uint64_t cluster_offset = m->alloc_offset; + +- trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); ++ //trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); + assert(m->nb_clusters > 0); + + old_cluster = g_try_new(uint64_t, m->nb_clusters); +@@ -1503,8 +1505,8 @@ handle_copied(BlockDriverState *bs, uint64_t guest_offset, + unsigned int keep_clusters; + int ret; + +- trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, +- *bytes); ++ //trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, ++ // *bytes); + + assert(*host_offset == INV_OFFSET || offset_into_cluster(s, guest_offset) + == offset_into_cluster(s, *host_offset)); +@@ -1607,8 +1609,8 @@ do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, + { + BDRVQcow2State *s = bs->opaque; + +- trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, +- *host_offset, *nb_clusters); ++ //trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, ++ // *host_offset, *nb_clusters); + + if (has_data_file(bs)) { + assert(*host_offset == INV_OFFSET || +@@ -1618,7 +1620,7 @@ do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, + } + + /* Allocate new clusters */ +- trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); ++ //trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); + if (*host_offset == INV_OFFSET) { + int64_t cluster_offset = + qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size); +@@ -1670,8 +1672,8 @@ handle_alloc(BlockDriverState *bs, uint64_t guest_offset, + + uint64_t alloc_cluster_offset; + +- trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, +- *bytes); ++ //trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, ++ // *bytes); + assert(*bytes > 0); + + /* +@@ -1791,7 +1793,7 @@ int coroutine_fn qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, + uint64_t cur_bytes; + int ret; + +- trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *bytes); ++ //trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *bytes); + + again: + start = offset; +diff --git a/qcow2/lib/qcow2-refcount.c b/qcow2/lib/qcow2-refcount.c +index 0266542c..9442522a 100644 +--- a/qcow2/lib/qcow2-refcount.c ++++ b/qcow2/lib/qcow2-refcount.c +@@ -30,7 +30,7 @@ + #include "qemu/bswap.h" + #include "qemu/cutils.h" + #include "qemu/memalign.h" +-#include "trace.h" ++//#include "trace.h" + + static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, + uint64_t max); +@@ -745,8 +745,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) + if (ret >= 0) { + int r2 = bdrv_pdiscard(bs->file, d->offset, d->bytes); + if (r2 < 0) { +- trace_qcow2_process_discards_failed_region(d->offset, d->bytes, +- r2); ++ //trace_qcow2_process_discards_failed_region(d->offset, d->bytes, r2); + } + } + +@@ -2277,6 +2276,7 @@ calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + return ret; + } + ++#if 0 + /* encryption */ + if (s->crypto_header.length) { + ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, nb_clusters, +@@ -2286,6 +2286,7 @@ calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, + return ret; + } + } ++#endif + + /* bitmaps */ + ret = qcow2_check_bitmaps_refcounts(bs, res, refcount_table, nb_clusters); +diff --git a/qcow2/lib/qcow2-threads.c b/qcow2/lib/qcow2-threads.c +index d6071a1e..f6de6e60 100644 +--- a/qcow2/lib/qcow2-threads.c ++++ b/qcow2/lib/qcow2-threads.c +@@ -36,7 +36,7 @@ + #include "qcow2.h" + #include "block/block-io.h" + #include "block/thread-pool.h" +-#include "crypto.h" ++//#include "crypto.h" + + static int coroutine_fn + qcow2_co_process(BlockDriverState *bs, ThreadPoolFunc *func, void *arg) +@@ -426,6 +426,7 @@ qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, + } + + ++#if 0 + /* + * Cryptography + */ +@@ -525,3 +526,4 @@ qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset, + return qcow2_co_encdec(bs, host_offset, guest_offset, buf, len, + qcrypto_block_decrypt); + } ++#endif +diff --git a/qcow2/lib/qcow2.c b/qcow2/lib/qcow2.c +index 70b19730..801042e0 100644 +--- a/qcow2/lib/qcow2.c ++++ b/qcow2/lib/qcow2.c +@@ -31,17 +31,17 @@ + #include "qcow2.h" + #include "qemu/error-report.h" + #include "qapi/error.h" +-#include "qapi/qapi-events-block-core.h" ++//#include "qapi/qapi-events-block-core.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qmp/qstring.h" +-#include "trace.h" ++//#include "trace.h" + #include "qemu/option_int.h" + #include "qemu/cutils.h" + #include "qemu/bswap.h" + #include "qemu/memalign.h" + #include "qapi/qobject-input-visitor.h" + #include "qapi/qapi-visit-block-core.h" +-#include "crypto.h" ++//#include "crypto.h" + #include "block/aio_task.h" + #include "block/dirty-bitmap.h" + +@@ -95,6 +95,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) + } + + ++#if 0 + static int GRAPH_RDLOCK + qcow2_crypto_hdr_read_func(QCryptoBlock *block, size_t offset, + uint8_t *buf, size_t buflen, +@@ -193,6 +194,7 @@ qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp) + qdict_put_str(cryptoopts_qdict, "format", fmt); + return cryptoopts_qdict; + } ++#endif + + /* + * read qcow2 extension and fill bs +@@ -287,6 +289,7 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, + break; + + case QCOW2_EXT_MAGIC_CRYPTO_HEADER: { ++#if 0 + unsigned int cflags = 0; + if (s->crypt_method_header != QCOW_CRYPT_LUKS) { + error_setg(errp, "CRYPTO header extension only " +@@ -325,6 +328,9 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, + if (!s->crypto) { + return -EINVAL; + } ++#else ++ assert(0); ++#endif + } break; + + case QCOW2_EXT_MAGIC_BITMAPS: +@@ -817,8 +823,10 @@ static QemuOptsList qcow2_runtime_opts = { + .type = QEMU_OPT_NUMBER, + .help = "Clean unused cache entries after this time (in seconds)", + }, ++#if 0 + BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", + "ID of secret providing qcow2 AES key or LUKS passphrase"), ++#endif + { /* end of list */ } + }, + }; +@@ -979,7 +987,7 @@ typedef struct Qcow2ReopenState { + bool discard_passthrough[QCOW2_DISCARD_MAX]; + bool discard_no_unref; + uint64_t cache_clean_interval; +- QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ ++ //QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ + } Qcow2ReopenState; + + static int GRAPH_RDLOCK +@@ -992,12 +1000,12 @@ qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, + int overlap_check_template = 0; + uint64_t l2_cache_size, l2_cache_entry_size, refcount_cache_size; + int i; +- const char *encryptfmt; +- QDict *encryptopts = NULL; ++ //const char *encryptfmt; ++ //QDict *encryptopts = NULL; + int ret; + +- qdict_extract_subqdict(options, &encryptopts, "encrypt."); +- encryptfmt = qdict_get_try_str(encryptopts, "format"); ++ //qdict_extract_subqdict(options, &encryptopts, "encrypt."); ++ //encryptfmt = qdict_get_try_str(encryptopts, "format"); + + opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort); + if (!qemu_opts_absorb_qdict(opts, options, errp)) { +@@ -1157,6 +1165,7 @@ qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, + goto fail; + } + ++#if 0 + switch (s->crypt_method_header) { + case QCOW_CRYPT_NONE: + if (encryptfmt) { +@@ -1205,10 +1214,11 @@ qcow2_update_options_prepare(BlockDriverState *bs, Qcow2ReopenState *r, + ret = -EINVAL; + goto fail; + } ++#endif + + ret = 0; + fail: +- qobject_unref(encryptopts); ++ //qobject_unref(encryptopts); + qemu_opts_del(opts); + opts = NULL; + return ret; +@@ -1245,8 +1255,8 @@ static void qcow2_update_options_commit(BlockDriverState *bs, + cache_clean_timer_init(bs, bdrv_get_aio_context(bs)); + } + +- qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); +- s->crypto_opts = r->crypto_opts; ++ //qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ //s->crypto_opts = r->crypto_opts; + } + + static void qcow2_update_options_abort(BlockDriverState *bs, +@@ -1258,7 +1268,7 @@ static void qcow2_update_options_abort(BlockDriverState *bs, + if (r->refcount_block_cache) { + qcow2_cache_destroy(r->refcount_block_cache); + } +- qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); ++ //qapi_free_QCryptoBlockOpenOptions(r->crypto_opts); + } + + static int coroutine_fn GRAPH_RDLOCK +@@ -1494,6 +1504,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + s->refcount_max = UINT64_C(1) << (s->refcount_bits - 1); + s->refcount_max += s->refcount_max - 1; + ++#if 0 + s->crypt_method_header = header.crypt_method; + if (s->crypt_method_header) { + if (bdrv_uses_whitelist() && +@@ -1521,6 +1532,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + + bs->encrypted = true; + } ++#endif + + s->l2_bits = s->cluster_bits - ctz32(l2_entry_size(s)); + s->l2_size = 1 << s->l2_bits; +@@ -1705,6 +1717,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + } + } + ++#if 0 + /* qcow2_read_extension may have set up the crypto context + * if the crypt method needs a header region, some methods + * don't need header extensions, so must check here +@@ -1728,6 +1741,7 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + goto fail; + } + } ++#endif + + /* read the backing file name */ + if (header.backing_file_offset != 0) { +@@ -1913,8 +1927,8 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, + if (s->refcount_block_cache) { + qcow2_cache_destroy(s->refcount_block_cache); + } +- qcrypto_block_free(s->crypto); +- qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ //qcrypto_block_free(s->crypto); ++ //qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); + return ret; + } + +@@ -1976,10 +1990,12 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) + { + BDRVQcow2State *s = bs->opaque; + ++#if 0 + if (bs->encrypted) { + /* Encryption works on a sector granularity */ + bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); + } ++#endif + bs->bl.pwrite_zeroes_alignment = s->subcluster_size; + bs->bl.pdiscard_alignment = s->cluster_size; + } +@@ -2170,7 +2186,7 @@ qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, + + if ((type == QCOW2_SUBCLUSTER_NORMAL || + type == QCOW2_SUBCLUSTER_ZERO_ALLOC || +- type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC) && !s->crypto) { ++ type == QCOW2_SUBCLUSTER_UNALLOCATED_ALLOC)/* && !s->crypto*/) { + *map = host_offset; + *file = s->data_file->bs; + status |= BDRV_BLOCK_OFFSET_VALID; +@@ -2225,6 +2241,7 @@ out: + return ret; + } + ++#if 0 + static int coroutine_fn GRAPH_RDLOCK + qcow2_co_preadv_encrypted(BlockDriverState *bs, + uint64_t host_offset, +@@ -2271,6 +2288,7 @@ fail: + + return ret; + } ++#endif + + typedef struct Qcow2AioTask { + AioTask task; +@@ -2312,10 +2330,10 @@ static coroutine_fn int qcow2_add_task(BlockDriverState *bs, + .l2meta = l2meta, + }; + +- trace_qcow2_add_task(qemu_coroutine_self(), bs, pool, +- func == qcow2_co_preadv_task_entry ? "read" : "write", +- subcluster_type, host_offset, offset, bytes, +- qiov, qiov_offset); ++ //trace_qcow2_add_task(qemu_coroutine_self(), bs, pool, ++ // func == qcow2_co_preadv_task_entry ? "read" : "write", ++ // subcluster_type, host_offset, offset, bytes, ++ // qiov, qiov_offset); + + if (!pool) { + return func(&task->task); +@@ -2353,8 +2371,10 @@ qcow2_co_preadv_task(BlockDriverState *bs, QCow2SubclusterType subc_type, + + case QCOW2_SUBCLUSTER_NORMAL: + if (bs->encrypted) { ++#if 0 + return qcow2_co_preadv_encrypted(bs, host_offset, + offset, bytes, qiov, qiov_offset); ++#endif + } + + BLKDBG_CO_EVENT(bs->file, BLKDBG_READ_AIO); +@@ -2398,10 +2418,12 @@ qcow2_co_preadv_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + while (bytes != 0 && aio_task_pool_status(aio) == 0) { + /* prepare next request */ + cur_bytes = MIN(bytes, INT_MAX); ++#if 0 + if (s->crypto) { + cur_bytes = MIN(cur_bytes, + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); + } ++#endif + + qemu_co_mutex_lock(&s->lock); + ret = qcow2_get_host_offset(bs, offset, &cur_bytes, +@@ -2534,10 +2556,12 @@ handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) + if (!(s->data_file->bs->supported_zero_flags & BDRV_REQ_NO_FALLBACK)) { + return 0; + } ++#if 0 + + if (bs->encrypted) { + return 0; + } ++#endif + + for (m = l2meta; m != NULL; m = m->next) { + int ret; +@@ -2577,7 +2601,7 @@ handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) + continue; + } + +- trace_qcow2_skip_cow(qemu_coroutine_self(), m->offset, m->nb_clusters); ++ //trace_qcow2_skip_cow(qemu_coroutine_self(), m->offset, m->nb_clusters); + m->skip_cow = true; + } + return 0; +@@ -2596,9 +2620,10 @@ int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, + { + int ret; + BDRVQcow2State *s = bs->opaque; +- void *crypt_buf = NULL; +- QEMUIOVector encrypted_qiov; ++ //void *crypt_buf = NULL; ++ //QEMUIOVector encrypted_qiov; + ++#if 0 + if (bs->encrypted) { + assert(s->crypto); + assert(bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); +@@ -2618,6 +2643,7 @@ int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, + qiov = &encrypted_qiov; + qiov_offset = 0; + } ++#endif + + /* Try to efficiently initialize the physical space with zeroes */ + ret = handle_alloc_space(bs, l2meta); +@@ -2633,7 +2659,7 @@ int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, + */ + if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) { + BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); +- trace_qcow2_writev_data(qemu_coroutine_self(), host_offset); ++ //trace_qcow2_writev_data(qemu_coroutine_self(), host_offset); + ret = bdrv_co_pwritev_part(s->data_file, host_offset, + bytes, qiov, qiov_offset, 0); + if (ret < 0) { +@@ -2653,7 +2679,7 @@ out_locked: + qcow2_handle_l2meta(bs, &l2meta, false); + qemu_co_mutex_unlock(&s->lock); + +- qemu_vfree(crypt_buf); ++ //qemu_vfree(crypt_buf); + + return ret; + } +@@ -2686,13 +2712,13 @@ qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + QCowL2Meta *l2meta = NULL; + AioTaskPool *aio = NULL; + +- trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes); ++ //trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes); + + while (bytes != 0 && aio_task_pool_status(aio) == 0) { + + l2meta = NULL; + +- trace_qcow2_writev_start_part(qemu_coroutine_self()); ++ //trace_qcow2_writev_start_part(qemu_coroutine_self()); + offset_in_cluster = offset_into_cluster(s, offset); + cur_bytes = MIN(bytes, INT_MAX); + if (bs->encrypted) { +@@ -2731,7 +2757,7 @@ qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, + bytes -= cur_bytes; + offset += cur_bytes; + qiov_offset += cur_bytes; +- trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes); ++ //trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes); + } + ret = 0; + +@@ -2751,7 +2777,7 @@ fail_nometa: + g_free(aio); + } + +- trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); ++ //trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); + + return ret; + } +@@ -2807,9 +2833,9 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file) + qcow2_cache_destroy(s->l2_table_cache); + qcow2_cache_destroy(s->refcount_block_cache); + +- qcrypto_block_free(s->crypto); +- s->crypto = NULL; +- qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); ++ //qcrypto_block_free(s->crypto); ++ //s->crypto = NULL; ++ //qapi_free_QCryptoBlockOpenOptions(s->crypto_opts); + + g_free(s->unknown_header_fields); + cleanup_unknown_header_ext(bs); +@@ -2847,7 +2873,7 @@ qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) + BDRVQcow2State *s = bs->opaque; + BdrvChild *data_file; + int flags = s->flags; +- QCryptoBlock *crypto = NULL; ++ // QCryptoBlock *crypto = NULL; + QDict *options; + int ret; + +@@ -2856,8 +2882,8 @@ qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) + * that means we don't have to worry about reopening them here. + */ + +- crypto = s->crypto; +- s->crypto = NULL; ++// crypto = s->crypto; ++// s->crypto = NULL; + + /* + * Do not reopen s->data_file (i.e., have qcow2_do_close() not close it, +@@ -2885,7 +2911,7 @@ qcow2_co_invalidate_cache(BlockDriverState *bs, Error **errp) + return; + } + +- s->crypto = crypto; ++// s->crypto = crypto; + } + + static size_t header_ext_add(char *buf, uint32_t magic, const void *s, +@@ -2957,7 +2983,8 @@ int qcow2_update_header(BlockDriverState *bs) + .backing_file_size = 0, + .cluster_bits = cpu_to_be32(s->cluster_bits), + .size = cpu_to_be64(total_size), +- .crypt_method = cpu_to_be32(s->crypt_method_header), ++ //.crypt_method = cpu_to_be32(s->crypt_method_header), ++ .crypt_method = cpu_to_be32(QCOW_CRYPT_NONE), + .l1_size = cpu_to_be32(s->l1_size), + .l1_table_offset = cpu_to_be64(s->l1_table_offset), + .refcount_table_offset = cpu_to_be64(s->refcount_table_offset), +@@ -3030,6 +3057,7 @@ int qcow2_update_header(BlockDriverState *bs) + buflen -= ret; + } + ++#if 0 + /* Full disk encryption header pointer extension */ + if (s->crypto_header.offset != 0) { + s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset); +@@ -3045,6 +3073,7 @@ int qcow2_update_header(BlockDriverState *bs) + buf += ret; + buflen -= ret; + } ++#endif + + /* + * Feature table. A mere 8 feature names occupies 392 bytes, and +@@ -3204,6 +3233,7 @@ qcow2_co_change_backing_file(BlockDriverState *bs, const char *backing_file, + return qcow2_update_header(bs); + } + ++#if 0 + static int coroutine_fn GRAPH_RDLOCK + qcow2_set_up_encryption(BlockDriverState *bs, + QCryptoBlockCreateOptions *cryptoopts, +@@ -3246,6 +3276,7 @@ qcow2_set_up_encryption(BlockDriverState *bs, + qcrypto_block_free(crypto); + return ret; + } ++#endif + + /** + * Preallocates metadata structures for data clusters between @offset (in the +@@ -3850,6 +3881,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) + } + } + ++#if 0 + /* Want encryption? There you go. */ + if (qcow2_opts->encrypt) { + bdrv_graph_co_rdlock(); +@@ -3860,7 +3892,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) + goto out; + } + } +- ++#endif + blk_co_unref(blk); + blk = NULL; + +@@ -4069,7 +4101,7 @@ qcow2_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + uint32_t tail = ROUND_UP(offset + bytes, s->subcluster_size) - + (offset + bytes); + +- trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, bytes); ++ //trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, bytes); + if (offset + bytes == bs->total_sectors * BDRV_SECTOR_SIZE) { + tail = 0; + } +@@ -4105,7 +4137,7 @@ qcow2_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, + qemu_co_mutex_lock(&s->lock); + } + +- trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, bytes); ++ //trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, bytes); + + /* Whatever is left can use real zero subclusters */ + ret = qcow2_subcluster_zeroize(bs, offset, bytes, flags); +@@ -4289,7 +4321,7 @@ fail: + + qemu_co_mutex_unlock(&s->lock); + +- trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); ++ //trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); + + return ret; + } +@@ -5023,7 +5055,7 @@ static int GRAPH_RDLOCK qcow2_make_empty(BlockDriverState *bs) + + if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps && + 3 + l1_clusters <= s->refcount_block_size && +- s->crypt_method_header != QCOW_CRYPT_LUKS && ++ /*s->crypt_method_header != QCOW_CRYPT_LUKS &&*/ + !has_data_file(bs)) { + /* The following function only works for qcow2 v3 images (it + * requires the dirty flag) and only as long as there are no +@@ -5083,7 +5115,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, + char *optstr; + PreallocMode prealloc; + bool has_backing_file; +- bool has_luks; ++ //bool has_luks; + bool extended_l2; + size_t l2e_size; + +@@ -5119,9 +5151,10 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, + g_free(optstr); + + optstr = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); +- has_luks = optstr && strcmp(optstr, "luks") == 0; ++ //has_luks = optstr && strcmp(optstr, "luks") == 0; + g_free(optstr); + ++#if 0 + if (has_luks) { + g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL; + QDict *cryptoopts = qcow2_extract_crypto_opts(opts, "luks", errp); +@@ -5142,6 +5175,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, + + luks_payload_size = ROUND_UP(headerlen, cluster_size); + } ++#endif + + virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); + virtual_size = ROUND_UP(virtual_size, cluster_size); +@@ -5252,14 +5286,16 @@ qcow2_get_specific_info(BlockDriverState *bs, Error **errp) + { + BDRVQcow2State *s = bs->opaque; + ImageInfoSpecific *spec_info; +- QCryptoBlockInfo *encrypt_info = NULL; ++ //QCryptoBlockInfo *encrypt_info = NULL; + ++#if 0 + if (s->crypto != NULL) { + encrypt_info = qcrypto_block_get_info(s->crypto, errp); + if (!encrypt_info) { + return NULL; + } + } ++#endif + + spec_info = g_new(ImageInfoSpecific, 1); + *spec_info = (ImageInfoSpecific){ +@@ -5275,7 +5311,7 @@ qcow2_get_specific_info(BlockDriverState *bs, Error **errp) + Qcow2BitmapInfoList *bitmaps; + if (!qcow2_get_bitmap_info_list(bs, &bitmaps, errp)) { + qapi_free_ImageInfoSpecific(spec_info); +- qapi_free_QCryptoBlockInfo(encrypt_info); ++ // qapi_free_QCryptoBlockInfo(encrypt_info); + return NULL; + } + *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ +@@ -5302,6 +5338,7 @@ qcow2_get_specific_info(BlockDriverState *bs, Error **errp) + assert(false); + } + ++#if 0 + if (encrypt_info) { + ImageInfoSpecificQCow2Encryption *qencrypt = + g_new(ImageInfoSpecificQCow2Encryption, 1); +@@ -5323,6 +5360,7 @@ qcow2_get_specific_info(BlockDriverState *bs, Error **errp) + + spec_info->u.qcow2.data->encrypt = qencrypt; + } ++#endif + + return spec_info; + } +@@ -5725,6 +5763,7 @@ qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE); + } else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) { + backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT); ++#if 0 + } else if (g_str_has_prefix(desc->name, "encrypt.")) { + if (!s->crypto) { + error_setg(errp, +@@ -5737,6 +5776,7 @@ qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + return -ENOTSUP; + } + encryption_update = true; ++#endif + } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { + lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS, + lazy_refcounts); +@@ -5793,6 +5833,7 @@ qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + } + } + ++#if 0 + if (encryption_update) { + QDict *amend_opts_dict; + QCryptoBlockAmendOptions *amend_opts; +@@ -5819,6 +5860,7 @@ qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, + return ret; + } + } ++#endif + + if (s->refcount_bits != refcount_bits) { + int refcount_order = ctz32(refcount_bits); +@@ -5938,10 +5980,11 @@ static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, + bool force, + Error **errp) + { +- BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2; +- BDRVQcow2State *s = bs->opaque; ++// BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2; ++// BDRVQcow2State *s = bs->opaque; + int ret = 0; + ++#if 0 + if (qopts->encrypt) { + if (!s->crypto) { + error_setg(errp, "image is not encrypted, can't amend"); +@@ -5968,6 +6011,7 @@ static int coroutine_fn qcow2_co_amend(BlockDriverState *bs, + force, + errp); + } ++#endif + return ret; + } + +@@ -5981,7 +6025,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + int64_t size, const char *message_format, ...) + { + BDRVQcow2State *s = bs->opaque; +- const char *node_name; ++ //const char *node_name; + char *message; + va_list ap; + +@@ -6005,12 +6049,14 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + "corruption events will be suppressed\n", message); + } + ++#if 0 + node_name = bdrv_get_node_name(bs); + qapi_event_send_block_image_corrupted(bdrv_get_device_name(bs), + *node_name ? node_name : NULL, + message, offset >= 0, offset, + size >= 0, size, + fatal); ++#endif + g_free(message); + + if (fatal) { +@@ -6069,6 +6115,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, + static QemuOptsList qcow2_create_opts = { + .name = "qcow2-create-opts", + .head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head), ++#if 0 + .desc = { + { \ + .name = BLOCK_OPT_ENCRYPT, \ +@@ -6117,24 +6164,55 @@ static QemuOptsList qcow2_create_opts = { + QCOW_COMMON_OPTIONS, + { /* end of list */ } + } ++#else ++ .desc = { ++ { \ ++ .name = BLOCK_OPT_CLUSTER_SIZE, \ ++ .type = QEMU_OPT_SIZE, \ ++ .help = "qcow2 cluster size", \ ++ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_EXTL2, \ ++ .type = QEMU_OPT_BOOL, \ ++ .help = "Extended L2 tables", \ ++ .def_value_str = "off" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_PREALLOC, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Preallocation mode (allowed values: off, " \ ++ "metadata, falloc, full)" \ ++ }, \ ++ { \ ++ .name = BLOCK_OPT_COMPRESSION_TYPE, \ ++ .type = QEMU_OPT_STRING, \ ++ .help = "Compression method used for image cluster " \ ++ "compression", \ ++ .def_value_str = "zlib" \ ++ }, ++ QCOW_COMMON_OPTIONS, ++ { /* end of list */ } ++ } ++#endif + }; + + static QemuOptsList qcow2_amend_opts = { + .name = "qcow2-amend-opts", + .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head), + .desc = { +- BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."), +- BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."), +- BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."), +- BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("encrypt."), +- BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("encrypt."), ++// BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), + QCOW_COMMON_OPTIONS, + { /* end of list */ } + } + }; + + static const char *const qcow2_strong_runtime_opts[] = { +- "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, ++// "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, + + NULL + }; +diff --git a/qcow2/lib/qobject/block-qdict.c b/qcow2/lib/qobject/block-qdict.c +index 4a83bda2..0d2eba5b 100644 +--- a/qcow2/lib/qobject/block-qdict.c ++++ b/qcow2/lib/qobject/block-qdict.c +@@ -586,6 +586,7 @@ static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) + return dst; + } + ++#if 0 + /** + * qdict_array_entries(): Returns the number of direct array entries if the + * sub-QDict of src specified by the prefix in subqdict (or src itself for +@@ -647,6 +648,7 @@ int qdict_array_entries(QDict *src, const char *subqdict) + + return i; + } ++#endif + + /** + * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all +diff --git a/qcow2/lib/qom/object.c b/qcow2/lib/qom/object.c +index 157a45c5..728b6927 100644 +--- a/qcow2/lib/qom/object.c ++++ b/qcow2/lib/qom/object.c +@@ -14,17 +14,17 @@ + #include "hw/qdev-core.h" + #include "qapi/error.h" + #include "qom/object.h" +-#include "qom/object_interfaces.h" ++//#include "qom/object_interfaces.h" + #include "qemu/cutils.h" + #include "qemu/memalign.h" + #include "qapi/visitor.h" + #include "qapi/string-input-visitor.h" + #include "qapi/string-output-visitor.h" + #include "qapi/qobject-input-visitor.h" +-#include "qapi/forward-visitor.h" ++//#include "qapi/forward-visitor.h" + #include "qapi/qapi-builtin-visit.h" + #include "qapi/qmp/qjson.h" +-#include "trace.h" ++//#include "trace.h" + + /* TODO: replace QObject with a simpler visitor to avoid a dependency + * of the QOM core on QObject? */ +@@ -262,6 +262,7 @@ static size_t type_object_get_align(TypeImpl *ti) + return 0; + } + ++#if 0 + size_t object_type_get_instance_size(const char *typename) + { + TypeImpl *type = type_get_by_name(typename); +@@ -269,6 +270,7 @@ size_t object_type_get_instance_size(const char *typename) + g_assert(type != NULL); + return type_object_get_size(type); + } ++#endif + + static bool type_is_ancestor(TypeImpl *type, TypeImpl *target_type) + { +@@ -432,6 +434,7 @@ static void object_post_init_with_type(Object *obj, TypeImpl *ti) + } + } + ++#if 0 + bool object_apply_global_props(Object *obj, const GPtrArray *props, + Error **errp) + { +@@ -531,6 +534,7 @@ void object_apply_compat_props(Object *obj) + i == 2 ? &error_fatal : &error_abort); + } + } ++#endif + + static void object_class_property_init_all(Object *obj) + { +@@ -563,6 +567,7 @@ static void object_initialize_with_type(Object *obj, size_t size, TypeImpl *type + object_post_init_with_type(obj, type); + } + ++#if 0 + void object_initialize(void *data, size_t size, const char *typename) + { + TypeImpl *type = type_get_by_name(typename); +@@ -653,6 +658,7 @@ void object_initialize_child_internal(Object *parent, + object_initialize_child_with_props(parent, propname, child, size, type, + &error_abort, NULL); + } ++#endif + + static inline bool object_property_is_child(ObjectProperty *prop) + { +@@ -683,6 +689,7 @@ static void object_property_del_all(Object *obj) + g_hash_table_unref(obj->properties); + } + ++#if 0 + static void object_property_del_child(Object *obj, Object *child) + { + ObjectProperty *prop; +@@ -716,6 +723,7 @@ void object_unparent(Object *obj) + object_property_del_child(obj->parent, obj); + } + } ++#endif + + static void object_deinit(Object *obj, TypeImpl *type) + { +@@ -798,6 +806,7 @@ Object *object_new(const char *typename) + } + + ++#if 0 + Object *object_new_with_props(const char *typename, + Object *parent, + const char *id, +@@ -898,6 +907,7 @@ bool object_set_propv(Object *obj, + + return true; + } ++#endif + + + Object *object_dynamic_cast(Object *obj, const char *typename) +@@ -912,8 +922,10 @@ Object *object_dynamic_cast(Object *obj, const char *typename) + Object *object_dynamic_cast_assert(Object *obj, const char *typename, + const char *file, int line, const char *func) + { ++#if 0 + trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)", + typename, file, line, func); ++#endif + + #ifdef CONFIG_QOM_CAST_DEBUG + int i; +@@ -1003,8 +1015,10 @@ ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class, + { + ObjectClass *ret; + ++#if 0 + trace_object_class_dynamic_cast_assert(class ? class->type->name : "(null)", + typename, file, line, func); ++#endif + + #ifdef CONFIG_QOM_CAST_DEBUG + int i; +@@ -1051,16 +1065,19 @@ ObjectClass *object_get_class(Object *obj) + return obj->class; + } + ++#if 0 + bool object_class_is_abstract(ObjectClass *klass) + { + return klass->type->abstract; + } ++#endif + + const char *object_class_get_name(ObjectClass *klass) + { + return klass->type->name; + } + ++#if 0 + ObjectClass *object_class_by_name(const char *typename) + { + TypeImpl *type = type_get_by_name(typename); +@@ -1092,6 +1109,7 @@ ObjectClass *module_object_class_by_name(const char *typename) + #endif + return oc; + } ++#endif + + ObjectClass *object_class_get_parent(ObjectClass *class) + { +@@ -1106,6 +1124,7 @@ ObjectClass *object_class_get_parent(ObjectClass *class) + return type->class; + } + ++#if 0 + typedef struct OCFData + { + void (*fn)(ObjectClass *klass, void *opaque); +@@ -1217,6 +1236,7 @@ GSList *object_class_get_list_sorted(const char *implements_type, + return g_slist_sort(object_class_get_list(implements_type, include_abstract), + object_class_cmp); + } ++#endif + + Object *object_ref(void *objptr) + { +@@ -1403,6 +1423,7 @@ ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name) + return g_hash_table_lookup(klass->properties, name); + } + ++#if 0 + ObjectProperty *object_class_property_find_err(ObjectClass *klass, + const char *name, + Error **errp) +@@ -1424,6 +1445,7 @@ void object_property_del(Object *obj, const char *name) + } + g_hash_table_remove(obj->properties, name); + } ++#endif + + bool object_property_get(Object *obj, const char *name, Visitor *v, + Error **errp) +@@ -1464,6 +1486,7 @@ bool object_property_set(Object *obj, const char *name, Visitor *v, + return !*errp; + } + ++#if 0 + bool object_property_set_str(Object *obj, const char *name, + const char *value, Error **errp) + { +@@ -1473,6 +1496,7 @@ bool object_property_set_str(Object *obj, const char *name, + qobject_unref(qstr); + return ok; + } ++#endif + + char *object_property_get_str(Object *obj, const char *name, + Error **errp) +@@ -1497,6 +1521,7 @@ char *object_property_get_str(Object *obj, const char *name, + return retval; + } + ++#if 0 + bool object_property_set_link(Object *obj, const char *name, + Object *value, Error **errp) + { +@@ -1665,6 +1690,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name, + qobject_unref(ret); + return retval; + } ++#endif + + typedef struct EnumProperty { + const QEnumLookup *lookup; +@@ -1829,6 +1855,7 @@ object_property_add_child(Object *obj, const char *name, + return object_property_try_add_child(obj, name, child, &error_abort); + } + ++#if 0 + void object_property_allow_set_link(const Object *obj, const char *name, + Object *val, Error **errp) + { +@@ -2056,6 +2083,7 @@ object_property_add_const_link(Object *obj, const char *name, + object_get_typename(target), target, + NULL, OBJ_PROP_LINK_DIRECT); + } ++#endif + + const char *object_get_canonical_path_component(const Object *obj) + { +@@ -2125,6 +2153,7 @@ Object *object_resolve_path_component(Object *parent, const char *part) + } + } + ++#if 0 + static Object *object_resolve_abs_path(Object *parent, + char **parts, + const char *typename) +@@ -2240,6 +2269,7 @@ Object *object_resolve_type_unambiguous(const char *typename, Error **errp) + } + return o; + } ++#endif + + typedef struct StringProperty + { +@@ -2771,6 +2801,7 @@ object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, + getter, setter, NULL, (void *)v); + } + ++#if 0 + typedef struct { + Object *target_obj; + char *target_name; +@@ -2849,6 +2880,7 @@ object_property_add_alias(Object *obj, const char *name, + target_prop->description); + return op; + } ++#endif + + void object_property_set_description(Object *obj, const char *name, + const char *description) +diff --git a/qcow2/lib/system/cpus.c b/qcow2/lib/system/cpus.c +index 1c818ff6..81236835 100644 +--- a/qcow2/lib/system/cpus.c ++++ b/qcow2/lib/system/cpus.c +@@ -23,29 +23,29 @@ + */ + + #include "qemu/osdep.h" +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qemu/coroutine-tls.h" + #include "qapi/error.h" +-#include "qapi/qapi-commands-machine.h" +-#include "qapi/qapi-commands-misc.h" +-#include "qapi/qapi-events-run-state.h" ++//#include "qapi/qapi-commands-machine.h" ++//#include "qapi/qapi-commands-misc.h" ++//#include "qapi/qapi-events-run-state.h" + #include "qapi/qmp/qerror.h" +-#include "exec/gdbstub.h" +-#include "sysemu/hw_accel.h" +-#include "exec/cpu-common.h" ++//#include "exec/gdbstub.h" ++//#include "sysemu/hw_accel.h" ++//#include "exec/cpu-common.h" + #include "qemu/thread.h" + #include "qemu/main-loop.h" +-#include "qemu/plugin.h" +-#include "sysemu/cpus.h" +-#include "qemu/guest-random.h" +-#include "hw/nmi.h" +-#include "sysemu/replay.h" +-#include "sysemu/runstate.h" ++//#include "qemu/plugin.h" ++//#include "sysemu/cpus.h" ++//#include "qemu/guest-random.h" ++//#include "hw/nmi.h" ++//#include "sysemu/replay.h" ++//#include "sysemu/runstate.h" + #include "sysemu/cpu-timers.h" +-#include "sysemu/whpx.h" +-#include "hw/boards.h" +-#include "hw/hw.h" +-#include "trace.h" ++//#include "sysemu/whpx.h" ++//#include "hw/boards.h" ++//#include "hw/hw.h" ++//#include "trace.h" + + #ifdef CONFIG_LINUX + +@@ -68,6 +68,7 @@ + /* The Big QEMU Lock (BQL) */ + static QemuMutex bql; + ++#if 0 + /* + * The chosen accelerator is supposed to register this. + */ +@@ -414,17 +415,21 @@ static QemuThread io_thread; + static QemuCond qemu_cpu_cond; + /* system init */ + static QemuCond qemu_pause_cond; ++#endif + + void qemu_init_cpu_loop(void) + { ++#if 0 + qemu_init_sigbus(); + qemu_cond_init(&qemu_cpu_cond); + qemu_cond_init(&qemu_pause_cond); ++#endif + qemu_mutex_init(&bql); + +- qemu_thread_get_self(&io_thread); ++ //qemu_thread_get_self(&io_thread); + } + ++#if 0 + void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) + { + do_run_on_cpu(cpu, func, data, &bql); +@@ -511,6 +516,7 @@ bool qemu_in_vcpu_thread(void) + { + return current_cpu && qemu_cpu_is_self(current_cpu); + } ++#endif + + QEMU_DEFINE_STATIC_CO_TLS(bool, bql_locked) + +@@ -544,6 +550,7 @@ void bql_unlock(void) + qemu_mutex_unlock(&bql); + } + ++#if 0 + void qemu_cond_wait_bql(QemuCond *cond) + { + qemu_cond_wait(cond, &bql); +@@ -875,4 +882,5 @@ void qmp_inject_nmi(Error **errp) + { + nmi_monitor_handle(monitor_get_cpu_index(monitor_cur()), errp); + } ++#endif + +diff --git a/qcow2/lib/util/aio-posix.c b/qcow2/lib/util/aio-posix.c +index 266c9dd3..237951d9 100644 +--- a/qcow2/lib/util/aio-posix.c ++++ b/qcow2/lib/util/aio-posix.c +@@ -19,9 +19,9 @@ + #include "qemu/main-loop.h" + #include "qemu/rcu.h" + #include "qemu/rcu_queue.h" +-#include "qemu/sockets.h" ++//#include "qemu/sockets.h" + #include "qemu/cutils.h" +-#include "trace.h" ++//#include "trace.h" + #include "aio-posix.h" + + /* Stop userspace polling on a handler if it isn't active for some time */ +@@ -338,7 +338,7 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) + if (!QLIST_IS_INSERTED(node, node_deleted) && + !QLIST_IS_INSERTED(node, node_poll) && + node->io_poll) { +- trace_poll_add(ctx, node, node->pfd.fd, revents); ++ //trace_poll_add(ctx, node, node->pfd.fd, revents); + if (ctx->poll_started && node->io_poll_begin) { + node->io_poll_begin(node->opaque); + } +@@ -486,7 +486,7 @@ static bool remove_idle_poll_handlers(AioContext *ctx, + if (node->poll_idle_timeout == 0LL) { + node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; + } else if (now >= node->poll_idle_timeout) { +- trace_poll_remove(ctx, node, node->pfd.fd); ++ //trace_poll_remove(ctx, node, node->pfd.fd); + node->poll_idle_timeout = 0LL; + QLIST_SAFE_REMOVE(node, node_poll); + if (ctx->poll_started && node->io_poll_end) { +@@ -527,7 +527,7 @@ static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list, + + assert(qemu_lockcnt_count(&ctx->list_lock) > 0); + +- trace_run_poll_handlers_begin(ctx, max_ns, *timeout); ++ //trace_run_poll_handlers_begin(ctx, max_ns, *timeout); + + /* + * Optimization: ->io_poll() handlers often contain RCU read critical +@@ -561,7 +561,7 @@ static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list, + *timeout -= MIN(*timeout, elapsed_time); + } + +- trace_run_poll_handlers_end(ctx, progress, *timeout); ++ //trace_run_poll_handlers_end(ctx, progress, *timeout); + return progress; + } + +@@ -686,7 +686,7 @@ bool aio_poll(AioContext *ctx, bool blocking) + /* This is the sweet spot, no adjustment needed */ + } else if (block_ns > ctx->poll_max_ns) { + /* We'd have to poll for too long, poll less */ +- int64_t old = ctx->poll_ns; ++ //int64_t old = ctx->poll_ns; + + if (ctx->poll_shrink) { + ctx->poll_ns /= ctx->poll_shrink; +@@ -694,11 +694,11 @@ bool aio_poll(AioContext *ctx, bool blocking) + ctx->poll_ns = 0; + } + +- trace_poll_shrink(ctx, old, ctx->poll_ns); ++ //trace_poll_shrink(ctx, old, ctx->poll_ns); + } else if (ctx->poll_ns < ctx->poll_max_ns && + block_ns < ctx->poll_max_ns) { + /* There is room to grow, poll longer */ +- int64_t old = ctx->poll_ns; ++ //int64_t old = ctx->poll_ns; + int64_t grow = ctx->poll_grow; + + if (grow == 0) { +@@ -715,7 +715,7 @@ bool aio_poll(AioContext *ctx, bool blocking) + ctx->poll_ns = ctx->poll_max_ns; + } + +- trace_poll_grow(ctx, old, ctx->poll_ns); ++ //trace_poll_grow(ctx, old, ctx->poll_ns); + } + } + +diff --git a/qcow2/lib/util/async.c b/qcow2/lib/util/async.c +index 3e3e4fc7..bcc6dee9 100644 +--- a/qcow2/lib/util/async.c ++++ b/qcow2/lib/util/async.c +@@ -35,7 +35,7 @@ + #include "qemu/coroutine_int.h" + #include "qemu/coroutine-tls.h" + #include "sysemu/cpu-timers.h" +-#include "trace.h" ++//#include "trace.h" + + /***********************************************************/ + /* bottom halves (can be seen as timers which expire ASAP) */ +@@ -162,9 +162,11 @@ void aio_bh_call(QEMUBH *bh) + MemReentrancyGuard *reentrancy_guard = bh->reentrancy_guard; + if (reentrancy_guard) { + last_engaged_in_io = reentrancy_guard->engaged_in_io; ++#if 0 + if (reentrancy_guard->engaged_in_io) { + trace_reentrant_aio(bh->ctx, bh->name); + } ++#endif + reentrancy_guard->engaged_in_io = true; + } + +@@ -563,7 +565,7 @@ static void co_schedule_bh_cb(void *opaque) + while (!QSLIST_EMPTY(&straight)) { + Coroutine *co = QSLIST_FIRST(&straight); + QSLIST_REMOVE_HEAD(&straight, co_scheduled_next); +- trace_aio_co_schedule_bh_cb(ctx, co); ++ //trace_aio_co_schedule_bh_cb(ctx, co); + + /* Protected by write barrier in qemu_aio_coroutine_enter */ + qatomic_set(&co->scheduled, NULL); +@@ -628,7 +630,7 @@ fail: + + void aio_co_schedule(AioContext *ctx, Coroutine *co) + { +- trace_aio_co_schedule(ctx, co); ++ //trace_aio_co_schedule(ctx, co); + const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, + __func__); + +diff --git a/qcow2/lib/util/bufferiszero.c b/qcow2/lib/util/bufferiszero.c +index 522146da..b86b90a2 100644 +--- a/qcow2/lib/util/bufferiszero.c ++++ b/qcow2/lib/util/bufferiszero.c +@@ -24,7 +24,7 @@ + #include "qemu/osdep.h" + #include "qemu/cutils.h" + #include "qemu/bswap.h" +-#include "host/cpuinfo.h" ++//#include "host/cpuinfo.h" + + typedef bool (*biz_accel_fn)(const void *, size_t); + +@@ -81,7 +81,12 @@ static bool buffer_is_zero_int_ge256(const void *buf, size_t len) + return t == 0; + } + +-#include "host/bufferiszero.c.inc" ++//#include "host/bufferiszero.c.inc" ++static biz_accel_fn const accel_table[1] = { ++ buffer_is_zero_int_ge256 ++}; ++ ++#define best_accel() 0 + + static biz_accel_fn buffer_is_zero_accel; + static unsigned accel_index; +diff --git a/qcow2/lib/util/cutils.c b/qcow2/lib/util/cutils.c +index 42364039..a0f76614 100644 +--- a/qcow2/lib/util/cutils.c ++++ b/qcow2/lib/util/cutils.c +@@ -1009,6 +1009,7 @@ int qemu_pstrcmp0(const char **str1, const char **str2) + return g_strcmp0(*str1, *str2); + } + ++#if 0 + static inline bool starts_with_prefix(const char *dir) + { + size_t prefix_len = strlen(CONFIG_PREFIX); +@@ -1040,6 +1041,7 @@ static inline const char *next_component(const char *dir, int *p_len) + *p_len = len; + return dir; + } ++#endif + + static const char *exec_dir; + +@@ -1139,11 +1141,12 @@ void qemu_init_exec_dir(const char *argv0) + if (p) { + exec_dir = g_path_get_dirname(p); + } else { +- exec_dir = CONFIG_BINDIR; ++ exec_dir = "/usr/bin" /*CONFIG_BINDIR*/; + } + #endif + } + ++#if 0 + const char *qemu_get_exec_dir(void) + { + return exec_dir; +@@ -1216,3 +1219,4 @@ char *get_relocated_path(const char *dir) + out: + return g_string_free(result, false); + } ++#endif +diff --git a/qcow2/lib/util/error-report.c b/qcow2/lib/util/error-report.c +index 1b17c11d..0554f186 100644 +--- a/qcow2/lib/util/error-report.c ++++ b/qcow2/lib/util/error-report.c +@@ -10,8 +10,10 @@ + * See the COPYING file in the top-level directory. + */ + ++#include ++ + #include "qemu/osdep.h" +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qemu/error-report.h" + + /* +@@ -32,12 +34,11 @@ const char *error_guest_name; + int error_printf(const char *fmt, ...) + { + va_list ap; +- int ret; + + va_start(ap, fmt); +- ret = error_vprintf(fmt, ap); ++ vsyslog(LOG_ERR, fmt, ap); + va_end(ap); +- return ret; ++ return 0; + } + + static Location std_loc = { +@@ -144,7 +145,7 @@ static void print_loc(void) + int i; + const char *const *argp; + +- if (!monitor_cur() && g_get_prgname()) { ++ if (/*!monitor_cur() && */g_get_prgname()) { + error_printf("%s:", g_get_prgname()); + sep = " "; + } +@@ -188,14 +189,14 @@ static void vreport(report_type type, const char *fmt, va_list ap) + { + gchar *timestr; + +- if (message_with_timestamp && !monitor_cur()) { ++ if (message_with_timestamp/* && !monitor_cur()*/) { + timestr = real_time_iso8601(); + error_printf("%s ", timestr); + g_free(timestr); + } + + /* Only prepend guest name if -msg guest-name and -name guest=... are set */ +- if (error_with_guestname && error_guest_name && !monitor_cur()) { ++ if (error_with_guestname && error_guest_name/* && !monitor_cur()*/) { + error_printf("%s ", error_guest_name); + } + +@@ -212,8 +213,8 @@ static void vreport(report_type type, const char *fmt, va_list ap) + break; + } + +- error_vprintf(fmt, ap); +- error_printf("\n"); ++ vsyslog(LOG_ERR, fmt, ap); ++ //error_printf("\n"); + } + + /* +diff --git a/qcow2/lib/util/error.c b/qcow2/lib/util/error.c +index e5e24720..e84b4b16 100644 +--- a/qcow2/lib/util/error.c ++++ b/qcow2/lib/util/error.c +@@ -32,7 +32,7 @@ Error *error_warn; + static void error_handle(Error **errp, Error *err) + { + if (errp == &error_abort) { +- fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", ++ error_report("Unexpected error in %s() at %s:%d:\n", + err->func, err->src, err->line); + error_report("%s", error_get_pretty(err)); + if (err->hint) { +@@ -231,6 +231,8 @@ const char *error_get_pretty(const Error *err) + + void error_report_err(Error *err) + { ++ error_report("libqcow2: error in %s() at %s:%d:\n", ++ err->func, err->src, err->line); + error_report("%s", error_get_pretty(err)); + if (err->hint) { + error_printf("%s", err->hint->str); +@@ -240,6 +242,8 @@ void error_report_err(Error *err) + + void warn_report_err(Error *err) + { ++ warn_report("libqcow2: error in %s() at %s:%d:\n", ++ err->func, err->src, err->line); + warn_report("%s", error_get_pretty(err)); + if (err->hint) { + error_printf("%s", err->hint->str); +diff --git a/qcow2/lib/util/hbitmap.c b/qcow2/lib/util/hbitmap.c +index 6d6e1b59..106be57b 100644 +--- a/qcow2/lib/util/hbitmap.c ++++ b/qcow2/lib/util/hbitmap.c +@@ -12,7 +12,7 @@ + #include "qemu/osdep.h" + #include "qemu/hbitmap.h" + #include "qemu/host-utils.h" +-#include "trace.h" ++//#include "trace.h" + #include "crypto/hash.h" + + /* HBitmaps provides an array of bits. The bits are stored as usual in an +@@ -140,7 +140,7 @@ static unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) + } + + hbi->pos = pos; +- trace_hbitmap_iter_skip_words(hbi->hb, hbi, pos, cur); ++ //trace_hbitmap_iter_skip_words(hbi->hb, hbi, pos, cur); + + assert(cur); + return cur; +@@ -471,8 +471,8 @@ void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count) + return; + } + +- trace_hbitmap_set(hb, start, count, +- start >> hb->granularity, last >> hb->granularity); ++ //trace_hbitmap_set(hb, start, count, ++ // start >> hb->granularity, last >> hb->granularity); + + first = start >> hb->granularity; + last >>= hb->granularity; +@@ -569,8 +569,8 @@ void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count) + assert(QEMU_IS_ALIGNED(start, gran)); + assert(QEMU_IS_ALIGNED(count, gran) || (start + count == hb->orig_size)); + +- trace_hbitmap_reset(hb, start, count, +- start >> hb->granularity, last >> hb->granularity); ++ //trace_hbitmap_reset(hb, start, count, ++ // start >> hb->granularity, last >> hb->granularity); + + first = start >> hb->granularity; + last >>= hb->granularity; +diff --git a/qcow2/lib/util/host-utils.c b/qcow2/lib/util/host-utils.c +index fb91bcba..6d0e2825 100644 +--- a/qcow2/lib/util/host-utils.c ++++ b/qcow2/lib/util/host-utils.c +@@ -267,6 +267,7 @@ void ulshift(uint64_t *plow, uint64_t *phigh, int32_t shift, bool *overflow) + } + } + ++#if 0 + /* + * Unsigned 256-by-128 division. + * Returns the remainder via r. +@@ -446,3 +447,4 @@ Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor) + return rem; + } + } ++#endif +diff --git a/qcow2/lib/util/iov.c b/qcow2/lib/util/iov.c +index 7e73948f..14eb3cd1 100644 +--- a/qcow2/lib/util/iov.c ++++ b/qcow2/lib/util/iov.c +@@ -18,7 +18,7 @@ + + #include "qemu/osdep.h" + #include "qemu/iov.h" +-#include "qemu/sockets.h" ++//#include "qemu/sockets.h" + #include "qemu/cutils.h" + + size_t iov_from_buf_full(const struct iovec *iov, unsigned int iov_cnt, +@@ -229,6 +229,7 @@ ssize_t iov_send_recv(int sockfd, const struct iovec *_iov, unsigned iov_cnt, + } + + ++#if 0 + void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, + FILE *fp, const char *prefix, size_t limit) + { +@@ -245,6 +246,7 @@ void iov_hexdump(const struct iovec *iov, const unsigned int iov_cnt, + qemu_hexdump(fp, prefix, buf, size); + g_free(buf); + } ++#endif + + unsigned iov_copy(struct iovec *dst_iov, unsigned int dst_iov_cnt, + const struct iovec *iov, unsigned int iov_cnt, +@@ -432,7 +434,7 @@ bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes) + uint8_t *base = (uint8_t *)iov->iov_base + current_offset; + size_t len = MIN(iov->iov_len - current_offset, bytes); + +- if (!buffer_is_zero(base, len)) { ++ if (!buffer_is_zero((char*)base, len)) { + return false; + } + +diff --git a/qcow2/lib/util/lockcnt.c b/qcow2/lib/util/lockcnt.c +index 5da36946..69274a4a 100644 +--- a/qcow2/lib/util/lockcnt.c ++++ b/qcow2/lib/util/lockcnt.c +@@ -9,7 +9,7 @@ + #include "qemu/osdep.h" + #include "qemu/thread.h" + #include "qemu/atomic.h" +-#include "trace.h" ++//#include "trace.h" + + #ifdef CONFIG_LINUX + #include "qemu/futex.h" +@@ -60,10 +60,10 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, + if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_FREE) { + int expected = *val; + +- trace_lockcnt_fast_path_attempt(lockcnt, expected, new_if_free); ++ //trace_lockcnt_fast_path_attempt(lockcnt, expected, new_if_free); + *val = qatomic_cmpxchg(&lockcnt->count, expected, new_if_free); + if (*val == expected) { +- trace_lockcnt_fast_path_success(lockcnt, expected, new_if_free); ++ //trace_lockcnt_fast_path_success(lockcnt, expected, new_if_free); + *val = new_if_free; + return true; + } +@@ -80,7 +80,7 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, + int expected = *val; + int new = expected - QEMU_LOCKCNT_STATE_LOCKED + QEMU_LOCKCNT_STATE_WAITING; + +- trace_lockcnt_futex_wait_prepare(lockcnt, expected, new); ++ //trace_lockcnt_futex_wait_prepare(lockcnt, expected, new); + *val = qatomic_cmpxchg(&lockcnt->count, expected, new); + if (*val == expected) { + *val = new; +@@ -90,10 +90,10 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, + + if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_WAITING) { + *waited = true; +- trace_lockcnt_futex_wait(lockcnt, *val); ++ //trace_lockcnt_futex_wait(lockcnt, *val); + qemu_futex_wait(&lockcnt->count, *val); + *val = qatomic_read(&lockcnt->count); +- trace_lockcnt_futex_wait_resume(lockcnt, *val); ++ //trace_lockcnt_futex_wait_resume(lockcnt, *val); + continue; + } + +@@ -104,7 +104,7 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, + + static void lockcnt_wake(QemuLockCnt *lockcnt) + { +- trace_lockcnt_futex_wake(lockcnt); ++ //trace_lockcnt_futex_wake(lockcnt); + qemu_futex_wake(&lockcnt->count, 1); + } + +@@ -261,11 +261,11 @@ void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt) + do { + expected = val; + new = (val + QEMU_LOCKCNT_COUNT_STEP) & ~QEMU_LOCKCNT_STATE_MASK; +- trace_lockcnt_unlock_attempt(lockcnt, val, new); ++ //trace_lockcnt_unlock_attempt(lockcnt, val, new); + val = qatomic_cmpxchg(&lockcnt->count, val, new); + } while (val != expected); + +- trace_lockcnt_unlock_success(lockcnt, val, new); ++ //trace_lockcnt_unlock_success(lockcnt, val, new); + if (val & QEMU_LOCKCNT_STATE_WAITING) { + lockcnt_wake(lockcnt); + } +@@ -279,11 +279,11 @@ void qemu_lockcnt_unlock(QemuLockCnt *lockcnt) + do { + expected = val; + new = val & ~QEMU_LOCKCNT_STATE_MASK; +- trace_lockcnt_unlock_attempt(lockcnt, val, new); ++ //trace_lockcnt_unlock_attempt(lockcnt, val, new); + val = qatomic_cmpxchg(&lockcnt->count, val, new); + } while (val != expected); + +- trace_lockcnt_unlock_success(lockcnt, val, new); ++ //trace_lockcnt_unlock_success(lockcnt, val, new); + if (val & QEMU_LOCKCNT_STATE_WAITING) { + lockcnt_wake(lockcnt); + } +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +index a0386cfe..817e65a6 100644 +--- a/qcow2/lib/util/main-loop.c ++++ b/qcow2/lib/util/main-loop.c +@@ -27,13 +27,13 @@ + #include "qemu/cutils.h" + #include "qemu/timer.h" + #include "sysemu/cpu-timers.h" +-#include "sysemu/replay.h" ++//#include "sysemu/replay.h" + #include "qemu/main-loop.h" + #include "block/aio.h" + #include "block/thread-pool.h" + #include "qemu/error-report.h" + #include "qemu/queue.h" +-#include "qom/object.h" ++//#include "qom/object.h" + + #ifndef _WIN32 + #include +@@ -153,6 +153,14 @@ void qemu_notify_event(void) + + static GArray *gpollfds; + ++ ++void qemu_timer_notify_cb(void *opaque, QEMUClockType type) ++{ ++ if (!icount_enabled() || type != QEMU_CLOCK_VIRTUAL) { ++ qemu_notify_event(); ++ } ++} ++ + int qemu_init_main_loop(Error **errp) + { + int ret; +@@ -183,6 +191,7 @@ int qemu_init_main_loop(Error **errp) + return 0; + } + ++#if 0 + static void main_loop_update_params(EventLoopBase *base, Error **errp) + { + ERRP_GUARD(); +@@ -242,6 +251,7 @@ static void main_loop_register_types(void) + } + + type_init(main_loop_register_types) ++#endif + + static int max_priority; + +@@ -300,11 +310,11 @@ static int os_host_main_loop_wait(int64_t timeout) + glib_pollfds_fill(&timeout); + + bql_unlock(); +- replay_mutex_unlock(); ++ //replay_mutex_unlock(); + + ret = qemu_poll_ns((GPollFD *)gpollfds->data, gpollfds->len, timeout); + +- replay_mutex_lock(); ++ //replay_mutex_lock(); + bql_lock(); + + glib_pollfds_poll(); +diff --git a/qcow2/lib/util/memalign.c b/qcow2/lib/util/memalign.c +index c199ae70..c8b526e5 100644 +--- a/qcow2/lib/util/memalign.c ++++ b/qcow2/lib/util/memalign.c +@@ -27,7 +27,7 @@ + #include "qemu/osdep.h" + #include "qemu/host-utils.h" + #include "qemu/memalign.h" +-#include "trace.h" ++//#include "trace.h" + + void *qemu_try_memalign(size_t alignment, size_t size) + { +@@ -62,9 +62,10 @@ void *qemu_try_memalign(size_t alignment, size_t size) + #elif defined(CONFIG_MEMALIGN) + ptr = memalign(alignment, size); + #else +- #error No function to allocate aligned memory available ++ ptr = aligned_alloc(alignment, size); ++ //#error No function to allocate aligned memory available + #endif +- trace_qemu_memalign(alignment, size, ptr); ++ //trace_qemu_memalign(alignment, size, ptr); + return ptr; + } + +@@ -82,7 +83,7 @@ void *qemu_memalign(size_t alignment, size_t size) + + void qemu_vfree(void *ptr) + { +- trace_qemu_vfree(ptr); ++ //trace_qemu_vfree(ptr); + #if !defined(CONFIG_POSIX_MEMALIGN) && defined(CONFIG_ALIGNED_MALLOC) + /* Only Windows _aligned_malloc needs a special free function */ + _aligned_free(ptr); +diff --git a/qcow2/lib/util/module.c b/qcow2/lib/util/module.c +index 3eb0f06d..f0791505 100644 +--- a/qcow2/lib/util/module.c ++++ b/qcow2/lib/util/module.c +@@ -25,7 +25,7 @@ + #ifdef CONFIG_MODULE_UPGRADES + #include "qemu-version.h" + #endif +-#include "trace.h" ++//#include "trace.h" + + typedef struct ModuleEntry + { +@@ -191,7 +191,7 @@ static bool module_load_dso(const char *fname, bool export_symbols, + e->init(); + register_module_init(e->init, e->type); + } +- trace_module_load_module(fname); ++ //trace_module_load_module(fname); + QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { + QTAILQ_REMOVE(&dso_init_list, e, node); + g_free(e); +@@ -327,7 +327,7 @@ int module_load_qom(const char *type, Error **errp) + return -1; + } + +- trace_module_lookup_object_type(type); ++ //trace_module_lookup_object_type(type); + for (modinfo = module_info; modinfo->name != NULL; modinfo++) { + if (!modinfo->objs) { + continue; +diff --git a/qcow2/lib/util/osdep.c b/qcow2/lib/util/osdep.c +index 77036983..f78d6cd3 100644 +--- a/qcow2/lib/util/osdep.c ++++ b/qcow2/lib/util/osdep.c +@@ -24,14 +24,14 @@ + #include "qemu/osdep.h" + #include "qapi/error.h" + #include "qemu/cutils.h" +-#include "qemu/sockets.h" ++//#include "qemu/sockets.h" + #include "qemu/error-report.h" +-#include "qemu/madvise.h" +-#include "qemu/mprotect.h" +-#include "qemu/hw-version.h" +-#include "monitor/monitor.h" ++//#include "qemu/madvise.h" ++//#include "qemu/mprotect.h" ++//#include "qemu/hw-version.h" ++//#include "monitor/monitor.h" + +-static const char *hw_version = QEMU_HW_VERSION; ++//static const char *hw_version = QEMU_HW_VERSION; + + int socket_set_cork(int fd, int v) + { +@@ -42,6 +42,7 @@ int socket_set_cork(int fd, int v) + #endif + } + ++#if 0 + int socket_set_nodelay(int fd) + { + int v = 1; +@@ -118,6 +119,7 @@ int qemu_mprotect_none(void *addr, size_t size) + return qemu_mprotect__osdep(addr, size, PROT_NONE); + #endif + } ++#endif + + #ifndef _WIN32 + +@@ -327,7 +329,7 @@ qemu_open_internal(const char *name, int flags, mode_t mode, Error **errp) + return -1; + } + +- return monitor_fdset_dup_fd_add(fdset_id, flags, errp); ++ return 0 /*monitor_fdset_dup_fd_add(fdset_id, flags, errp)*/; + } + #endif + +@@ -400,7 +402,7 @@ int qemu_open_old(const char *name, int flags, ...) + int qemu_close(int fd) + { + /* Close fd that was dup'd from an fdset */ +- monitor_fdset_dup_fd_remove(fd); ++ //monitor_fdset_dup_fd_remove(fd); + return close(fd); + } + +@@ -493,6 +495,7 @@ int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen) + return ret; + } + ++#if 0 + ssize_t qemu_send_full(int s, const void *buf, size_t count) + { + ssize_t ret = 0; +@@ -514,7 +517,9 @@ ssize_t qemu_send_full(int s, const void *buf, size_t count) + + return total; + } ++#endif + ++#if 0 + void qemu_set_hw_version(const char *version) + { + hw_version = version; +@@ -524,6 +529,7 @@ const char *qemu_hw_version(void) + { + return hw_version; + } ++#endif + + #ifdef _WIN32 + static void socket_cleanup(void) +diff --git a/qcow2/lib/util/oslib-posix.c b/qcow2/lib/util/oslib-posix.c +index 11b35e48..23c378e9 100644 +--- a/qcow2/lib/util/oslib-posix.c ++++ b/qcow2/lib/util/oslib-posix.c +@@ -31,20 +31,20 @@ + + #include + +-#include "sysemu/sysemu.h" +-#include "trace.h" ++//#include "sysemu/sysemu.h" ++//#include "trace.h" + #include "qapi/error.h" + #include "qemu/error-report.h" +-#include "qemu/madvise.h" +-#include "qemu/sockets.h" ++//#include "qemu/madvise.h" ++//#include "qemu/sockets.h" + #include "qemu/thread.h" + #include + #include "qemu/cutils.h" + #include "qemu/units.h" +-#include "qemu/thread-context.h" ++//#include "qemu/thread-context.h" + #include "qemu/main-loop.h" + +-#ifdef CONFIG_LINUX ++#if defined(__linux__) + #include + #endif + +@@ -58,14 +58,14 @@ + #include + #endif + +-#include "qemu/mmap-alloc.h" ++//#include "qemu/mmap-alloc.h" + + #define MAX_MEM_PREALLOC_THREAD_COUNT 16 + + struct MemsetThread; + +-static QLIST_HEAD(, MemsetContext) memset_contexts = +- QLIST_HEAD_INITIALIZER(memset_contexts); ++//static QLIST_HEAD(, MemsetContext) memset_contexts = ++// QLIST_HEAD_INITIALIZER(memset_contexts); + + typedef struct MemsetContext { + bool all_threads_created; +@@ -86,12 +86,12 @@ struct MemsetThread { + typedef struct MemsetThread MemsetThread; + + /* used by sigbus_handler() */ +-static MemsetContext *sigbus_memset_context; +-struct sigaction sigbus_oldact; +-static QemuMutex sigbus_mutex; ++//static MemsetContext *sigbus_memset_context; ++//struct sigaction sigbus_oldact; ++//static QemuMutex sigbus_mutex; + +-static QemuMutex page_mutex; +-static QemuCond page_cond; ++//static QemuMutex page_mutex; ++//static QemuCond page_cond; + + int qemu_get_thread_id(void) + { +@@ -188,6 +188,7 @@ fail_close: + return false; + } + ++#if 0 + /* alloc shared memory pages */ + void *qemu_anon_ram_alloc(size_t size, uint64_t *alignment, bool shared, + bool noreserve) +@@ -214,6 +215,7 @@ void qemu_anon_ram_free(void *ptr, size_t size) + trace_qemu_anon_ram_free(ptr, size); + qemu_ram_munmap(-1, ptr, size); + } ++#endif + + void qemu_socket_set_block(int fd) + { +@@ -253,6 +255,7 @@ void qemu_set_cloexec(int fd) + assert(f != -1); + } + ++#if 0 + int qemu_socketpair(int domain, int type, int protocol, int sv[2]) + { + int ret; +@@ -655,6 +658,7 @@ char *qemu_get_pid_name(pid_t pid) + + return name; + } ++#endif + + + void *qemu_alloc_stack(size_t *sz) +@@ -775,6 +779,7 @@ void sigaction_invoke(struct sigaction *action, + action->sa_sigaction(info->ssi_signo, &si, NULL); + } + ++#if 0 + size_t qemu_get_host_physmem(void) + { + #ifdef _SC_PHYS_PAGES +@@ -931,3 +936,4 @@ void qemu_close_all_open_fd(const int *skip, unsigned int nskip) + qemu_close_all_open_fd_fallback(skip, nskip, open_max); + } + } ++#endif +diff --git a/qcow2/lib/util/qemu-coroutine-lock.c b/qcow2/lib/util/qemu-coroutine-lock.c +index 25344353..6afe27a0 100644 +--- a/qcow2/lib/util/qemu-coroutine-lock.c ++++ b/qcow2/lib/util/qemu-coroutine-lock.c +@@ -31,7 +31,7 @@ + #include "qemu/processor.h" + #include "qemu/queue.h" + #include "block/aio.h" +-#include "trace.h" ++//#include "trace.h" + + void qemu_co_queue_init(CoQueue *queue) + { +@@ -198,7 +198,7 @@ static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx, + CoWaitRecord w; + unsigned old_handoff; + +- trace_qemu_co_mutex_lock_entry(mutex, self); ++ //trace_qemu_co_mutex_lock_entry(mutex, self); + push_waiter(mutex, &w); + + /* +@@ -230,7 +230,7 @@ static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx, + } + + qemu_coroutine_yield(); +- trace_qemu_co_mutex_lock_return(mutex, self); ++ //trace_qemu_co_mutex_lock_return(mutex, self); + } + + void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) +@@ -264,7 +264,7 @@ retry_fast_path: + + if (waiters == 0) { + /* Uncontended. */ +- trace_qemu_co_mutex_lock_uncontended(mutex, self); ++ //trace_qemu_co_mutex_lock_uncontended(mutex, self); + mutex->ctx = ctx; + } else { + qemu_co_mutex_lock_slowpath(ctx, mutex); +@@ -277,7 +277,7 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) + { + Coroutine *self = qemu_coroutine_self(); + +- trace_qemu_co_mutex_unlock_entry(mutex, self); ++ //trace_qemu_co_mutex_unlock_entry(mutex, self); + + assert(mutex->locked); + assert(mutex->holder == self); +@@ -326,7 +326,7 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) + } + } + +- trace_qemu_co_mutex_unlock_return(mutex, self); ++ //trace_qemu_co_mutex_unlock_return(mutex, self); + } + + struct CoRwTicket { +diff --git a/qcow2/lib/util/qemu-coroutine.c b/qcow2/lib/util/qemu-coroutine.c +index eb4eebef..3f9b9920 100644 +--- a/qcow2/lib/util/qemu-coroutine.c ++++ b/qcow2/lib/util/qemu-coroutine.c +@@ -13,7 +13,7 @@ + */ + + #include "qemu/osdep.h" +-#include "trace.h" ++//#include "trace.h" + #include "qemu/thread.h" + #include "qemu/atomic.h" + #include "qemu/coroutine_int.h" +@@ -265,7 +265,7 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) + + QSIMPLEQ_REMOVE_HEAD(&pending, co_queue_next); + +- trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg); ++ //trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg); + + /* if the Coroutine has already been scheduled, entering it again will + * cause us to enter it twice, potentially even after the coroutine has +@@ -302,7 +302,7 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) + break; + case COROUTINE_TERMINATE: + assert(!to->locks_held); +- trace_qemu_coroutine_terminate(to); ++ //trace_qemu_coroutine_terminate(to); + coroutine_delete(to); + break; + default: +@@ -328,7 +328,7 @@ void coroutine_fn qemu_coroutine_yield(void) + Coroutine *self = qemu_coroutine_self(); + Coroutine *to = self->caller; + +- trace_qemu_coroutine_yield(self, to); ++ //trace_qemu_coroutine_yield(self, to); + + if (!to) { + fprintf(stderr, "Co-routine is yielding to no one\n"); +diff --git a/qcow2/lib/util/qemu-option.c b/qcow2/lib/util/qemu-option.c +index 201f7a87..4ba11fa1 100644 +--- a/qcow2/lib/util/qemu-option.c ++++ b/qcow2/lib/util/qemu-option.c +@@ -637,7 +637,7 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, + opts = g_malloc0(sizeof(*opts)); + opts->id = g_strdup(id); + opts->list = list; +- loc_save(&opts->loc); ++ //loc_save(&opts->loc); + QTAILQ_INIT(&opts->head); + QTAILQ_INSERT_TAIL(&list->head, opts, next); + return opts; +@@ -654,7 +654,7 @@ void qemu_opts_reset(QemuOptsList *list) + + void qemu_opts_loc_restore(QemuOpts *opts) + { +- loc_restore(&opts->loc); ++ //loc_restore(&opts->loc); + } + + const char *qemu_opts_id(QemuOpts *opts) +@@ -1125,20 +1125,20 @@ bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp) + int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, + void *opaque, Error **errp) + { +- Location loc; ++ //Location loc; + QemuOpts *opts, *next; + int rc = 0; + +- loc_push_none(&loc); ++ //loc_push_none(&loc); + QTAILQ_FOREACH_SAFE(opts, &list->head, next, next) { +- loc_restore(&opts->loc); ++ //loc_restore(&opts->loc); + rc = func(opaque, opts, errp); + if (rc) { + break; + } + assert(!errp || !*errp); + } +- loc_pop(&loc); ++ //loc_pop(&loc); + return rc; + } + +diff --git a/qcow2/lib/util/qemu-print.c b/qcow2/lib/util/qemu-print.c +index 69ba612f..98ca5474 100644 +--- a/qcow2/lib/util/qemu-print.c ++++ b/qcow2/lib/util/qemu-print.c +@@ -11,7 +11,7 @@ + */ + + #include "qemu/osdep.h" +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qemu/qemu-print.h" + + /* +@@ -20,10 +20,12 @@ + */ + int qemu_vprintf(const char *fmt, va_list ap) + { ++#if 0 + Monitor *cur_mon = monitor_cur(); + if (cur_mon) { + return monitor_vprintf(cur_mon, fmt, ap); + } ++#endif + return vprintf(fmt, ap); + } + +@@ -48,9 +50,11 @@ int qemu_printf(const char *fmt, ...) + */ + int qemu_vfprintf(FILE *stream, const char *fmt, va_list ap) + { ++#if 0 + if (!stream) { + return monitor_vprintf(monitor_cur(), fmt, ap); + } ++#endif + return vfprintf(stream, fmt, ap); + } + +diff --git a/qcow2/lib/util/qemu-sockets.c b/qcow2/lib/util/qemu-sockets.c +index 60c44b2b..ea955c48 100644 +--- a/qcow2/lib/util/qemu-sockets.c ++++ b/qcow2/lib/util/qemu-sockets.c +@@ -21,7 +21,7 @@ + #include + #endif /* CONFIG_AF_VSOCK */ + +-#include "monitor/monitor.h" ++//#include "monitor/monitor.h" + #include "qapi/clone-visitor.h" + #include "qapi/error.h" + #include "qapi/qapi-visit-sockets.h" +@@ -30,7 +30,7 @@ + #include "qapi/qobject-input-visitor.h" + #include "qapi/qobject-output-visitor.h" + #include "qemu/cutils.h" +-#include "trace.h" ++//#include "trace.h" + + #ifndef AI_ADDRCONFIG + # define AI_ADDRCONFIG 0 +@@ -1137,21 +1137,23 @@ fail: + + static int socket_get_fd(const char *fdstr, Error **errp) + { +- Monitor *cur_mon = monitor_cur(); ++ //Monitor *cur_mon = monitor_cur(); + int fd; ++#if 0 + if (cur_mon) { + fd = monitor_get_fd(cur_mon, fdstr, errp); + if (fd < 0) { + return -1; + } + } else { ++#endif + if (qemu_strtoi(fdstr, NULL, 10, &fd) < 0) { + error_setg_errno(errp, errno, + "Unable to parse FD number %s", + fdstr); + return -1; + } +- } ++ //} + if (!fd_is_socket(fd)) { + error_setg(errp, "File descriptor '%s' is not a socket", fdstr); + close(fd); +@@ -1192,9 +1194,11 @@ int socket_connect(SocketAddress *addr, Error **errp) + fd = unix_connect_saddr(&addr->u.q_unix, errp); + break; + ++#if 0 + case SOCKET_ADDRESS_TYPE_FD: + fd = socket_get_fd(addr->u.fd.str, errp); + break; ++#endif + + case SOCKET_ADDRESS_TYPE_VSOCK: + fd = vsock_connect_saddr(&addr->u.vsock, errp); +@@ -1210,7 +1214,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) + { + int fd; + +- trace_socket_listen(num); ++ //trace_socket_listen(num); + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + fd = inet_listen_saddr(&addr->u.inet, 0, num, errp); +@@ -1220,6 +1224,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) + fd = unix_listen_saddr(&addr->u.q_unix, num, errp); + break; + ++#if 0 + case SOCKET_ADDRESS_TYPE_FD: + fd = socket_get_fd(addr->u.fd.str, errp); + if (fd < 0) { +@@ -1242,6 +1247,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) + return -1; + } + break; ++#endif + + case SOCKET_ADDRESS_TYPE_VSOCK: + fd = vsock_listen_saddr(&addr->u.vsock, num, errp); +@@ -1436,6 +1442,7 @@ SocketAddress *socket_remote_address(int fd, Error **errp) + } + + ++#if 0 + SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) + { + SocketAddress *addr; +@@ -1473,3 +1480,4 @@ SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy) + + return addr; + } ++#endif +diff --git a/qcow2/lib/util/qemu-thread-common.h b/qcow2/lib/util/qemu-thread-common.h +index 2af6b120..d09ade17 100644 +--- a/qcow2/lib/util/qemu-thread-common.h ++++ b/qcow2/lib/util/qemu-thread-common.h +@@ -14,7 +14,7 @@ + #define QEMU_THREAD_COMMON_H + + #include "qemu/thread.h" +-#include "trace.h" ++//#include "trace.h" + + static inline void qemu_mutex_post_init(QemuMutex *mutex) + { +@@ -28,7 +28,7 @@ static inline void qemu_mutex_post_init(QemuMutex *mutex) + static inline void qemu_mutex_pre_lock(QemuMutex *mutex, + const char *file, int line) + { +- trace_qemu_mutex_lock(mutex, file, line); ++ //trace_qemu_mutex_lock(mutex, file, line); + } + + static inline void qemu_mutex_post_lock(QemuMutex *mutex, +@@ -38,7 +38,7 @@ static inline void qemu_mutex_post_lock(QemuMutex *mutex, + mutex->file = file; + mutex->line = line; + #endif +- trace_qemu_mutex_locked(mutex, file, line); ++ //trace_qemu_mutex_locked(mutex, file, line); + } + + static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, +@@ -48,7 +48,7 @@ static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, + mutex->file = NULL; + mutex->line = 0; + #endif +- trace_qemu_mutex_unlock(mutex, file, line); ++ //trace_qemu_mutex_unlock(mutex, file, line); + } + + #endif +diff --git a/qcow2/lib/util/qemu-thread-posix.c b/qcow2/lib/util/qemu-thread-posix.c +index b2e26e21..8ea29d06 100644 +--- a/qcow2/lib/util/qemu-thread-posix.c ++++ b/qcow2/lib/util/qemu-thread-posix.c +@@ -15,7 +15,7 @@ + #include "qemu/atomic.h" + #include "qemu/notify.h" + #include "qemu-thread-common.h" +-#include "qemu/tsan.h" ++//#include "qemu/tsan.h" + #include "qemu/bitmap.h" + + #ifdef CONFIG_PTHREAD_SET_NAME_NP +@@ -235,9 +235,9 @@ qemu_cond_timedwait_ts(QemuCond *cond, QemuMutex *mutex, struct timespec *ts, + int err; + + assert(cond->initialized); +- trace_qemu_mutex_unlock(mutex, file, line); ++ //trace_qemu_mutex_unlock(mutex, file, line); + err = pthread_cond_timedwait(&cond->cond, &mutex->lock, ts); +- trace_qemu_mutex_locked(mutex, file, line); ++ //trace_qemu_mutex_locked(mutex, file, line); + if (err && err != ETIMEDOUT) { + error_exit(err, __func__); + } +@@ -518,7 +518,7 @@ static void *qemu_thread_start(void *args) + pthread_set_name_np(pthread_self(), qemu_thread_args->name); + # endif + } +- QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name); ++ //QEMU_TSAN_ANNOTATE_THREAD_NAME(qemu_thread_args->name); + g_free(qemu_thread_args->name); + g_free(qemu_thread_args); + +diff --git a/qcow2/lib/util/qemu-timer.c b/qcow2/lib/util/qemu-timer.c +index 6b1533bc..bdef6238 100644 +--- a/qcow2/lib/util/qemu-timer.c ++++ b/qcow2/lib/util/qemu-timer.c +@@ -27,8 +27,8 @@ + #include "qemu/timer.h" + #include "qemu/lockable.h" + #include "sysemu/cpu-timers.h" +-#include "sysemu/replay.h" +-#include "sysemu/cpus.h" ++//#include "sysemu/replay.h" ++//#include "sysemu/cpus.h" + + #ifdef CONFIG_POSIX + #include +@@ -522,14 +522,18 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) + case QEMU_CLOCK_VIRTUAL: + break; + case QEMU_CLOCK_HOST: ++#if 0 + if (!replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { + goto out; + } ++#endif + break; + case QEMU_CLOCK_VIRTUAL_RT: ++#if 0 + if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL_RT)) { + goto out; + } ++#endif + break; + } + +@@ -552,6 +556,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) + */ + break; + } ++#if 0 + /* Checkpoint for virtual clock is redundant in cases where + * it's being triggered with only non-EXTERNAL timers, because + * these timers don't change guest state directly. +@@ -563,6 +568,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) + qemu_mutex_unlock(&timer_list->active_timers_lock); + goto out; + } ++#endif + + /* remove timer from the list before calling the callback */ + timer_list->active_timers = ts->next; +@@ -604,6 +610,7 @@ void timerlistgroup_deinit(QEMUTimerListGroup *tlg) + QEMUClockType type; + for (type = 0; type < QEMU_CLOCK_MAX; type++) { + timerlist_free(tlg->tl[type]); ++ tlg->tl[type] = NULL; + } + } + +@@ -637,18 +644,28 @@ int64_t qemu_clock_get_ns(QEMUClockType type) + return get_clock(); + default: + case QEMU_CLOCK_VIRTUAL: ++#if 0 + return cpus_get_virtual_clock(); ++#else ++ return get_clock(); ++#endif + case QEMU_CLOCK_HOST: +- return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); ++ return /*REPLAY_CLOCK(REPLAY_CLOCK_HOST,*/ get_clock_realtime()/*)*/; + case QEMU_CLOCK_VIRTUAL_RT: ++#if 0 + return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock()); ++#else ++ assert(0); ++#endif + } + } + ++#if 0 + static void qemu_virtual_clock_set_ns(int64_t time) + { + return cpus_set_virtual_clock(time); + } ++#endif + + void init_clocks(QEMUTimerListNotifyCB *notify_cb) + { +@@ -681,6 +698,7 @@ bool qemu_clock_run_all_timers(void) + return progress; + } + ++#if 0 + int64_t qemu_clock_advance_virtual_time(int64_t dest) + { + int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +@@ -711,3 +729,4 @@ int64_t qemu_clock_advance_virtual_time(int64_t dest) + + return clock; + } ++#endif +diff --git a/qcow2/lib/util/qsp.c b/qcow2/lib/util/qsp.c +index 6b783e2e..5e54c4be 100644 +--- a/qcow2/lib/util/qsp.c ++++ b/qcow2/lib/util/qsp.c +@@ -61,10 +61,11 @@ + #include "qemu/qemu-print.h" + #include "qemu/thread.h" + #include "qemu/timer.h" +-#include "qemu/qht.h" ++//#include "qemu/qht.h" + #include "qemu/rcu.h" +-#include "qemu/xxhash.h" ++//#include "qemu/xxhash.h" + ++#if 0 + enum QSPType { + QSP_MUTEX, + QSP_BQL_MUTEX, +@@ -124,15 +125,19 @@ static const char * const qsp_typenames[] = { + [QSP_CONDVAR] = "condvar", + }; + ++#endif + QemuMutexLockFunc bql_mutex_lock_func = qemu_mutex_lock_impl; + QemuMutexLockFunc qemu_mutex_lock_func = qemu_mutex_lock_impl; ++#if 0 + QemuMutexTrylockFunc qemu_mutex_trylock_func = qemu_mutex_trylock_impl; + QemuRecMutexLockFunc qemu_rec_mutex_lock_func = qemu_rec_mutex_lock_impl; + QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func = + qemu_rec_mutex_trylock_impl; ++#endif + QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl; + QemuCondTimedWaitFunc qemu_cond_timedwait_func = qemu_cond_timedwait_impl; + ++#if 0 + /* + * It pays off to _not_ hash callsite->file; hashing a string is slow, and + * without it we still get a pretty unique hash. +@@ -811,3 +816,4 @@ void qsp_reset(void) + call_rcu(old, qsp_snapshot_destroy, rcu); + } + } ++#endif +diff --git a/qcow2/lib/util/thread-pool.c b/qcow2/lib/util/thread-pool.c +index 27eb777e..c0026baa 100644 +--- a/qcow2/lib/util/thread-pool.c ++++ b/qcow2/lib/util/thread-pool.c +@@ -19,7 +19,7 @@ + #include "qemu/queue.h" + #include "qemu/thread.h" + #include "qemu/coroutine.h" +-#include "trace.h" ++//#include "trace.h" + #include "block/thread-pool.h" + #include "qemu/main-loop.h" + +@@ -184,8 +184,8 @@ restart: + continue; + } + +- trace_thread_pool_complete(pool, elem, elem->common.opaque, +- elem->ret); ++ //trace_thread_pool_complete(pool, elem, elem->common.opaque, ++ // elem->ret); + QLIST_REMOVE(elem, all); + + if (elem->common.cb) { +@@ -220,7 +220,7 @@ static void thread_pool_cancel(BlockAIOCB *acb) + ThreadPoolElement *elem = (ThreadPoolElement *)acb; + ThreadPool *pool = elem->pool; + +- trace_thread_pool_cancel(elem, elem->common.opaque); ++ //trace_thread_pool_cancel(elem, elem->common.opaque); + + QEMU_LOCK_GUARD(&pool->lock); + if (elem->state == THREAD_QUEUED) { +@@ -256,7 +256,7 @@ BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, + + QLIST_INSERT_HEAD(&pool->head, req, all); + +- trace_thread_pool_submit(pool, req, arg); ++ //trace_thread_pool_submit(pool, req, arg); + + qemu_mutex_lock(&pool->lock); + if (pool->idle_threads == 0 && pool->cur_threads < pool->max_threads) { +diff --git a/qcow2/lib/util/yank.c b/qcow2/lib/util/yank.c +index eaac5053..be696c91 100644 +--- a/qcow2/lib/util/yank.c ++++ b/qcow2/lib/util/yank.c +@@ -12,7 +12,7 @@ + #include "qemu/thread.h" + #include "qemu/queue.h" + #include "qemu/lockable.h" +-#include "qapi/qapi-commands-yank.h" ++//#include "qapi/qapi-commands-yank.h" + #include "qapi/qapi-visit-yank.h" + #include "qapi/clone-visitor.h" + #include "qemu/yank.h" +diff --git a/qcow2/qemu-img.c b/qcow2/qemu-img.c +index 7668f867..60db40b2 100644 +--- a/qcow2/qemu-img.c ++++ b/qcow2/qemu-img.c +@@ -25,9 +25,9 @@ + #include "qemu/osdep.h" + #include + +-#include "qemu/help-texts.h" ++//#include "qemu/help-texts.h" + #include "qemu/qemu-progress.h" +-#include "qemu-version.h" ++//#include "qemu-version.h" + #include "qapi/error.h" + #include "qapi/qapi-commands-block-core.h" + #include "qapi/qapi-visit-block-core.h" +@@ -38,25 +38,24 @@ + #include "qemu/config-file.h" + #include "qemu/option.h" + #include "qemu/error-report.h" +-#include "qemu/log.h" ++//#include "qemu/log.h" + #include "qemu/main-loop.h" + #include "qemu/module.h" +-#include "qemu/sockets.h" ++//#include "qemu/sockets.h" + #include "qemu/units.h" + #include "qemu/memalign.h" +-#include "qom/object_interfaces.h" ++//#include "qom/object_interfaces.h" + #include "sysemu/block-backend.h" + #include "block/block_int.h" + #include "block/blockjob.h" + #include "block/dirty-bitmap.h" + #include "block/qapi.h" +-#include "crypto/init.h" +-#include "trace/control.h" +-#include "qemu/throttle.h" +-#include "block/throttle-groups.h" ++//#include "crypto/init.h" ++//#include "trace/control.h" ++//#include "qemu/throttle.h" ++//#include "block/throttle-groups.h" + +-#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \ +- "\n" QEMU_COPYRIGHT "\n" ++#define QEMU_IMG_VERSION "qemu-img version 9.1.1" + + typedef struct img_cmd_t { + const char *name; +@@ -230,7 +229,7 @@ void help(void) + + printf("%s\nSupported formats:", help_msg); + bdrv_iterate_format(format_print, NULL, false); +- printf("\n\n" QEMU_HELP_BOTTOM "\n"); ++ //printf("\n\n" QEMU_HELP_BOTTOM "\n"); + exit(EXIT_SUCCESS); + } + +@@ -563,7 +562,7 @@ static int img_create(int argc, char **argv) + flags |= BDRV_O_NO_BACKING; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + } + } +@@ -790,7 +789,7 @@ static int img_check(int argc, char **argv) + force_share = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -930,6 +929,8 @@ static void run_block_job(BlockJob *job, Error **errp) + progress = (float)progress_current / progress_total * 100.f; + } + qemu_progress_print(progress, 0); ++ printf("(%ld/%ld)\r", progress_current, progress_total); ++ fflush(stdout); + job_lock(); + } while (!job_is_ready_locked(&job->job) && + !job_is_completed_locked(&job->job)); +@@ -1014,7 +1015,7 @@ static int img_commit(int argc, char **argv) + } + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -1124,6 +1125,7 @@ unref_backing: + + done: + qemu_progress_end(); ++ printf("\n"); + + /* + * Manually inactivate the image first because this way we can know whether +@@ -1423,6 +1425,7 @@ static int img_compare(int argc, char **argv) + break; + case OPTION_OBJECT: + { ++#if 0 + Error *local_err = NULL; + + if (!user_creatable_add_from_str(optarg, &local_err)) { +@@ -1434,6 +1437,7 @@ static int img_compare(int argc, char **argv) + exit(EXIT_SUCCESS); + } + } ++#endif + break; + } + case OPTION_IMAGE_OPTS: +@@ -2222,6 +2226,7 @@ static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst, + + static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) + { ++#if 0 + ThrottleConfig cfg; + + throttle_config_init(&cfg); +@@ -2229,6 +2234,7 @@ static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) + + blk_io_limits_enable(blk, CONVERT_THROTTLE_GROUP); + blk_set_io_limits(blk, &cfg); ++#endif + } + + static int img_convert(int argc, char **argv) +@@ -2384,7 +2390,7 @@ static int img_convert(int argc, char **argv) + } + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -3051,7 +3057,7 @@ static int img_info(int argc, char **argv) + chain = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -3291,7 +3297,7 @@ static int img_map(int argc, char **argv) + } + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -3451,7 +3457,7 @@ static int img_snapshot(int argc, char **argv) + force_share = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -3608,7 +3614,7 @@ static int img_rebase(int argc, char **argv) + quiet = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -4093,7 +4099,7 @@ static int img_resize(int argc, char **argv) + quiet = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -4297,7 +4303,7 @@ static int img_amend(int argc, char **argv) + quiet = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -4870,7 +4876,7 @@ static int img_bitmap(int argc, char **argv) + merge = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -5145,7 +5151,7 @@ static int img_dd(int argc, char **argv) + force_share = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -5405,7 +5411,7 @@ static int img_measure(int argc, char **argv) + force_share = true; + break; + case OPTION_OBJECT: +- user_creatable_process_cmdline(optarg); ++ //user_creatable_process_cmdline(optarg); + break; + case OPTION_IMAGE_OPTS: + image_opts = true; +@@ -5555,14 +5561,16 @@ int main(int argc, char **argv) + signal(SIGPIPE, SIG_IGN); + #endif + +- socket_init(); ++ //socket_init(); + error_init(argv[0]); + module_call_init(MODULE_INIT_TRACE); + qemu_init_exec_dir(argv[0]); + + qemu_init_main_loop(&error_fatal); ++ qemu_init_cpu_loop(); ++ bql_lock(); + +- qcrypto_init(&error_fatal); ++ //qcrypto_init(&error_fatal); + + module_call_init(MODULE_INIT_QOM); + bdrv_init(); +@@ -5571,7 +5579,7 @@ int main(int argc, char **argv) + } + + qemu_add_opts(&qemu_source_opts); +- qemu_add_opts(&qemu_trace_opts); ++ //qemu_add_opts(&qemu_trace_opts); + + while ((c = getopt_long(argc, argv, "+:hVT:", long_options, NULL)) != -1) { + switch (c) { +@@ -5588,7 +5596,7 @@ int main(int argc, char **argv) + printf(QEMU_IMG_VERSION); + return 0; + case 'T': +- trace_opt_parse(optarg); ++ //trace_opt_parse(optarg); + break; + } + } +@@ -5603,11 +5611,11 @@ int main(int argc, char **argv) + argv += optind; + qemu_reset_optind(); + +- if (!trace_init_backends()) { +- exit(1); +- } +- trace_init_file(); +- qemu_set_log(LOG_TRACE, &error_fatal); ++ //if (!trace_init_backends()) { ++ // exit(1); ++ //} ++ //trace_init_file(); ++ //qemu_set_log(LOG_TRACE, &error_fatal); + + /* find the command */ + for (cmd = img_cmds; cmd->name != NULL; cmd++) { diff --git a/SOURCES/0027-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch b/SOURCES/0027-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch new file mode 100644 index 0000000..2fb8c3d --- /dev/null +++ b/SOURCES/0027-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch @@ -0,0 +1,158 @@ +From 630206452cd903deb03b916e6cf77047f4a5e48c Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 14 Jan 2025 11:41:26 +0100 +Subject: [PATCH] libqcow2: fix support for old components (gcc, glibc, glib, + gnutls) + +XCP-ng-8.3 doesn't have components recent enough for qemu sources. +Fix a few things to support old tools. + +Signed-off-by: Anthoine Bourgeois +--- + include/glib-compat.h | 4 ++-- + include/qemu/config-host.h | 2 +- + include/qemu/host-utils.h | 2 ++ + qcow2/lib/block/file-posix.c | 2 ++ + qcow2/lib/crypto/hash.c | 1 + + qcow2/lib/util/error-report.c | 2 +- + qcow2/lib/util/qemu-thread-posix.c | 2 ++ + qcow2/lib/util/thread-pool.c | 5 +++-- + 8 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/include/glib-compat.h b/include/glib-compat.h +index 86be439b..e4a23999 100644 +--- a/include/glib-compat.h ++++ b/include/glib-compat.h +@@ -19,12 +19,12 @@ + /* Ask for warnings for anything that was marked deprecated in + * the defined version, or before. It is a candidate for rewrite. + */ +-#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_66 ++#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_56 + + /* Ask for warnings if code tries to use function that did not + * exist in the defined version. These risk breaking builds + */ +-#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_66 ++#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_56 + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" +diff --git a/include/qemu/config-host.h b/include/qemu/config-host.h +index c0b00bf6..838d4bcc 100644 +--- a/include/qemu/config-host.h ++++ b/include/qemu/config-host.h +@@ -441,7 +441,7 @@ + + #define HAVE_BTRFS_H + +-#define HAVE_COPY_FILE_RANGE ++//#define HAVE_COPY_FILE_RANGE + + #define HAVE_DRM_H + +diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h +index 12ae37af..b78a2b21 100644 +--- a/include/qemu/host-utils.h ++++ b/include/qemu/host-utils.h +@@ -438,6 +438,7 @@ static inline uint64_t uabs64(int64_t v) + return v < 0 ? -v : v; + } + ++#if 0 + /** + * sadd32_overflow - addition with overflow indication + * @x, @y: addends +@@ -681,6 +682,7 @@ static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow) + return x; + #endif + } ++#endif + + /* Host type specific sizes of these routines. */ + +diff --git a/qcow2/lib/block/file-posix.c b/qcow2/lib/block/file-posix.c +index 852f0222..efa8ec2b 100644 +--- a/qcow2/lib/block/file-posix.c ++++ b/qcow2/lib/block/file-posix.c +@@ -2008,6 +2008,7 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque) + return handle_aiocb_write_zeroes(aiocb); + } + ++#if __GLIBC_MINOR__ < 27 + #ifndef HAVE_COPY_FILE_RANGE + static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + off_t *out_off, size_t len, unsigned int flags) +@@ -2021,6 +2022,7 @@ static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + #endif + } + #endif ++#endif + + /* + * parse_zone - Fill a zone descriptor +diff --git a/qcow2/lib/crypto/hash.c b/qcow2/lib/crypto/hash.c +index a282cf74..ffd74778 100644 +--- a/qcow2/lib/crypto/hash.c ++++ b/qcow2/lib/crypto/hash.c +@@ -18,6 +18,7 @@ + * + */ + ++#include + #include + + #include "qemu/osdep.h" +diff --git a/qcow2/lib/util/error-report.c b/qcow2/lib/util/error-report.c +index 0554f186..b3936a3c 100644 +--- a/qcow2/lib/util/error-report.c ++++ b/qcow2/lib/util/error-report.c +@@ -174,7 +174,7 @@ static char * + real_time_iso8601(void) + { + g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); +- return g_date_time_format_iso8601(dt); ++ return g_date_time_format (dt, "%Y-%m-%dT%H:%M:%S"); + } + + /* +diff --git a/qcow2/lib/util/qemu-thread-posix.c b/qcow2/lib/util/qemu-thread-posix.c +index 8ea29d06..1bb011e3 100644 +--- a/qcow2/lib/util/qemu-thread-posix.c ++++ b/qcow2/lib/util/qemu-thread-posix.c +@@ -534,7 +534,9 @@ static void *qemu_thread_start(void *args) + */ + #pragma GCC diagnostic push + #ifndef __clang__ ++#if __GNUC_PREREQ(8,0) + #pragma GCC diagnostic ignored "-Wstringop-overflow" ++#endif + #endif + + pthread_cleanup_push(qemu_thread_atexit_notify, NULL); +diff --git a/qcow2/lib/util/thread-pool.c b/qcow2/lib/util/thread-pool.c +index c0026baa..225db985 100644 +--- a/qcow2/lib/util/thread-pool.c ++++ b/qcow2/lib/util/thread-pool.c +@@ -297,6 +297,7 @@ void thread_pool_submit(ThreadPoolFunc *func, void *arg) + + void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) + { ++ int i; + qemu_mutex_lock(&pool->lock); + + pool->min_threads = ctx->thread_pool_min; +@@ -311,11 +312,11 @@ void thread_pool_update_params(ThreadPool *pool, AioContext *ctx) + * - Do nothing. The current number of threads fall in between the min and + * max thresholds. We'll let the pool manage itself. + */ +- for (int i = pool->cur_threads; i < pool->min_threads; i++) { ++ for (i = pool->cur_threads; i < pool->min_threads; i++) { + spawn_thread(pool); + } + +- for (int i = pool->cur_threads; i > pool->max_threads; i--) { ++ for (i = pool->cur_threads; i > pool->max_threads; i--) { + qemu_cond_signal(&pool->request_cond); + } + diff --git a/SOURCES/0028-tapdisk-protect-td_vbd_t-structure.patch b/SOURCES/0028-tapdisk-protect-td_vbd_t-structure.patch new file mode 100644 index 0000000..697b41f --- /dev/null +++ b/SOURCES/0028-tapdisk-protect-td_vbd_t-structure.patch @@ -0,0 +1,516 @@ +From 1281c264539010ecd2c9cf122568855ce91cc52d Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:33 +0100 +Subject: [PATCH] tapdisk: protect td_vbd_t structure + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect vbd structure from libqcow2 concurrency. + +This is the first part of tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-vbd.c | 115 ++++++++++++++++++++++++++++++++++++++---- + drivers/tapdisk-vbd.h | 2 + + 2 files changed, 106 insertions(+), 11 deletions(-) + +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 433375d6..32131f31 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -114,6 +114,8 @@ tapdisk_vbd_create(uint16_t uuid) + INIT_LIST_HEAD(&vbd->next); + INIT_LIST_HEAD(&vbd->rings); + INIT_LIST_HEAD(&vbd->dead_rings); ++ pthread_mutex_init(&vbd->mutex, NULL); ++ + tapdisk_vbd_mark_progress(vbd); + + return vbd; +@@ -279,7 +281,9 @@ tapdisk_vbd_close_vdi(td_vbd_t *vbd) + vbd->retired = NULL; + } + ++ pthread_mutex_lock(&vbd->mutex); + td_flag_set(vbd->state, TD_VBD_CLOSED); ++ pthread_mutex_unlock(&vbd->mutex); + } + + static int +@@ -724,6 +728,7 @@ tapdisk_vbd_queue_count(td_vbd_t *vbd, int *new, + f = 0; + c = 0; + ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tvreq, &vbd->new_requests) + n++; + +@@ -735,6 +740,7 @@ tapdisk_vbd_queue_count(td_vbd_t *vbd, int *new, + + tapdisk_vbd_for_each_request(vreq, tvreq, &vbd->completed_requests) + c++; ++ pthread_mutex_unlock(&vbd->mutex); + + *new = n; + *pending = p; +@@ -747,8 +753,12 @@ tapdisk_vbd_shutdown(td_vbd_t *vbd) + { + int new, pending, failed, completed; + +- if (!list_empty(&vbd->pending_requests)) ++ pthread_mutex_lock(&vbd->mutex); ++ if (!list_empty(&vbd->pending_requests)) { ++ pthread_mutex_unlock(&vbd->mutex); + return -EAGAIN; ++ } ++ pthread_mutex_unlock(&vbd->mutex); + + tapdisk_vbd_queue_count(vbd, &new, &pending, &failed, &completed); + +@@ -773,6 +783,7 @@ tapdisk_vbd_shutdown(td_vbd_t *vbd) + void + tapdisk_vbd_free(td_vbd_t *vbd) + { ++ pthread_mutex_destroy(&vbd->mutex); + free(vbd->name); + free(vbd->encryption.encryption_key); + free(vbd); +@@ -781,6 +792,7 @@ tapdisk_vbd_free(td_vbd_t *vbd) + int + tapdisk_vbd_close(td_vbd_t *vbd) + { ++ pthread_mutex_lock(&vbd->mutex); + /* + * don't close if any requests are pending in the aio layer + */ +@@ -797,10 +809,12 @@ tapdisk_vbd_close(td_vbd_t *vbd) + !list_empty(&vbd->completed_requests))) + goto fail; + ++ pthread_mutex_unlock(&vbd->mutex); + return tapdisk_vbd_shutdown(vbd); + + fail: + td_flag_set(vbd->state, TD_VBD_SHUTDOWN_REQUESTED); ++ pthread_mutex_unlock(&vbd->mutex); + DBG(TLOG_WARN, "%s: requests pending\n", vbd->name); + return -EAGAIN; + } +@@ -833,12 +847,18 @@ tapdisk_vbd_debug(td_vbd_t *vbd) + static void + tapdisk_vbd_drop_log(td_vbd_t *vbd) + { +- if (td_flag_test(vbd->state, TD_VBD_LOG_DROPPED)) ++ pthread_mutex_lock(&vbd->mutex); ++ if (td_flag_test(vbd->state, TD_VBD_LOG_DROPPED)) { ++ pthread_mutex_unlock(&vbd->mutex); + return; ++ } ++ pthread_mutex_unlock(&vbd->mutex); + + tapdisk_vbd_debug(vbd); + tlog_precious(0); ++ pthread_mutex_lock(&vbd->mutex); + td_flag_set(vbd->state, TD_VBD_LOG_DROPPED); ++ pthread_mutex_unlock(&vbd->mutex); + } + + int +@@ -863,8 +883,12 @@ tapdisk_vbd_queue_ready(td_vbd_t *vbd) + int + tapdisk_vbd_retry_needed(td_vbd_t *vbd) + { +- return !(list_empty(&vbd->failed_requests) && ++ bool retry; ++ pthread_mutex_lock(&vbd->mutex); ++ retry = !(list_empty(&vbd->failed_requests) && + list_empty(&vbd->new_requests)); ++ pthread_mutex_unlock(&vbd->mutex); ++ return retry; + } + + int +@@ -876,13 +900,16 @@ tapdisk_vbd_lock(td_vbd_t *vbd) + int + tapdisk_vbd_quiesce_queue(td_vbd_t *vbd) + { ++ pthread_mutex_lock(&vbd->mutex); + if (!list_empty(&vbd->pending_requests)) { + td_flag_set(vbd->state, TD_VBD_QUIESCE_REQUESTED); ++ pthread_mutex_unlock(&vbd->mutex); + return -EAGAIN; + } + + td_flag_clear(vbd->state, TD_VBD_QUIESCE_REQUESTED); + td_flag_set(vbd->state, TD_VBD_QUIESCED); ++ pthread_mutex_unlock(&vbd->mutex); + return 0; + } + +@@ -967,11 +994,13 @@ tapdisk_vbd_pause(td_vbd_t *vbd) + /* Don't guard this one as at this point the pause operation is complete */ + INFO("pause completed\n"); + ++ pthread_mutex_lock(&vbd->mutex); + if (!list_empty(&vbd->failed_requests)) + INFO("warning: failed requests pending\n"); + + td_flag_clear(vbd->state, TD_VBD_PAUSE_REQUESTED); + td_flag_set(vbd->state, TD_VBD_PAUSED); ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -984,10 +1013,13 @@ tapdisk_vbd_resume(td_vbd_t *vbd, const char *name) + + DBG(TLOG_DBG, "resume requested\n"); + ++ pthread_mutex_lock(&vbd->mutex); + if (!td_flag_test(vbd->state, TD_VBD_PAUSED)) { ++ pthread_mutex_unlock(&vbd->mutex); + EPRINTF("resume request for unpaused vbd %s\n", vbd->name); + return -EINVAL; + } ++ pthread_mutex_unlock(&vbd->mutex); + + for (i = 0; i < TD_VBD_EIO_RETRIES; i++) { + err = tapdisk_vbd_open_vdi(vbd, name, vbd->flags | TD_OPEN_STRICT, -1); +@@ -1014,8 +1046,10 @@ tapdisk_vbd_resume(td_vbd_t *vbd, const char *name) + } + } + resume_failed: ++ pthread_mutex_lock(&vbd->mutex); + if (err) { + td_flag_set(vbd->state, TD_VBD_RESUME_FAILED); ++ pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_close_vdi(vbd); + return err; + } +@@ -1026,6 +1060,7 @@ resume_failed: + tapdisk_vbd_start_queue(vbd); + td_flag_clear(vbd->state, TD_VBD_PAUSED); + td_flag_clear(vbd->state, TD_VBD_PAUSE_REQUESTED); ++ pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_check_state(vbd); + + if (vbd->nbdserver) +@@ -1081,17 +1116,24 @@ tapdisk_vbd_check_complete_requests(td_vbd_t *vbd) + struct timeval now; + + gettimeofday(&now, NULL); ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tmp, &vbd->failed_requests) + if (__tapdisk_vbd_request_timeout(vreq, &now)) + tapdisk_vbd_complete_vbd_request(vbd, vreq); ++ pthread_mutex_unlock(&vbd->mutex); + } + + static void + tapdisk_vbd_check_requests_for_issue(td_vbd_t *vbd) + { ++ pthread_mutex_lock(&vbd->mutex); + if (!list_empty(&vbd->new_requests) || +- !list_empty(&vbd->failed_requests)) ++ !list_empty(&vbd->failed_requests)) { ++ pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_issue_requests(vbd); ++ return; ++ } ++ pthread_mutex_unlock(&vbd->mutex); + } + + void +@@ -1141,10 +1183,13 @@ tapdisk_vbd_check_progress(td_vbd_t *vbd) + time_t diff; + struct timeval now, delta; + ++ pthread_mutex_lock(&vbd->mutex); + if (list_empty(&vbd->pending_requests)) { ++ pthread_mutex_unlock(&vbd->mutex); + watchdog_cleared(vbd); + return; + } ++ pthread_mutex_unlock(&vbd->mutex); + + gettimeofday(&now, NULL); + timersub(&now, &vbd->ts, &delta); +@@ -1152,6 +1197,7 @@ tapdisk_vbd_check_progress(td_vbd_t *vbd) + + if (diff >= TD_VBD_WATCHDOG_TIMEOUT) + { ++ pthread_mutex_lock(&vbd->mutex); + if(tapdisk_vbd_queue_ready(vbd)) + { + if (!vbd->watchdog_warned) { +@@ -1159,8 +1205,11 @@ tapdisk_vbd_check_progress(td_vbd_t *vbd) + "idle for %ld seconds\n", vbd->name, diff); + vbd->watchdog_warned = true; + } ++ pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_drop_log(vbd); ++ pthread_mutex_lock(&vbd->mutex); + } ++ pthread_mutex_unlock(&vbd->mutex); + return; + } + +@@ -1237,6 +1286,7 @@ __tapdisk_vbd_complete_td_request(td_vbd_t *vbd, td_vbd_request_t *vreq, + long long interval; + + err = (res <= 0 ? res : -res); ++ pthread_mutex_lock(&vbd->mutex); + vbd->secs_pending -= treq.secs; + vreq->secs_pending -= treq.secs; + +@@ -1280,6 +1330,7 @@ __tapdisk_vbd_complete_td_request(td_vbd_t *vbd, td_vbd_request_t *vreq, + } + + tapdisk_vbd_complete_vbd_request(vbd, vreq); ++ pthread_mutex_unlock(&vbd->mutex); + } + + static void +@@ -1292,7 +1343,9 @@ __tapdisk_vbd_reissue_td_request(td_vbd_t *vbd, + vreq = treq.vreq; + gettimeofday(&vreq->last_try, NULL); + ++ pthread_mutex_lock(&vbd->mutex); + vreq->submitting++; ++ pthread_mutex_unlock(&vbd->mutex); + + if (tapdisk_vbd_is_last_image(vbd, image)) { + if (unlikely(treq.op == TD_OP_BLOCK_STATUS)) { +@@ -1348,9 +1401,11 @@ __tapdisk_vbd_reissue_td_request(td_vbd_t *vbd, + } + + done: ++ pthread_mutex_lock(&vbd->mutex); + vreq->submitting--; + if (!vreq->secs_pending) + tapdisk_vbd_complete_vbd_request(vbd, vreq); ++ pthread_mutex_unlock(&vbd->mutex); + } + + void +@@ -1518,6 +1573,7 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + sec = vreq->sec; + image = tapdisk_vbd_first_image(vbd); + ++ pthread_mutex_lock(&vbd->mutex); + vreq->submitting = 1; + + tapdisk_vbd_mark_progress(vbd); +@@ -1526,6 +1582,7 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + tapdisk_vbd_move_request(vreq, &vbd->pending_requests); + + err = tapdisk_vbd_check_queue(vbd); ++ pthread_mutex_unlock(&vbd->mutex); + if (err) { + goto fail; + } +@@ -1548,6 +1605,7 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + treq.vreq = vreq; + + ++ pthread_mutex_lock(&vbd->mutex); + vreq->secs_pending += iov->secs; + vbd->secs_pending += iov->secs; + if (vbd->secondary_mode == TD_VBD_SECONDARY_MIRROR && +@@ -1557,6 +1615,7 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + vreq->secs_pending += iov->secs; + vbd->secs_pending += iov->secs; + } ++ pthread_mutex_unlock(&vbd->mutex); + + switch (vreq->op) { + case TD_OP_WRITE: +@@ -1602,11 +1661,13 @@ tapdisk_vbd_issue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + err = 0; + + out: ++ pthread_mutex_lock(&vbd->mutex); + vreq->submitting--; + if (!vreq->secs_pending) { + err = (err ? : vreq->error); + tapdisk_vbd_complete_vbd_request(vbd, vreq); + } ++ pthread_mutex_unlock(&vbd->mutex); + + return err; + +@@ -1631,6 +1692,7 @@ tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd) + err = 0; + gettimeofday(&now, NULL); + ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tmp, &vbd->failed_requests) { + if (vreq->secs_pending) + continue; +@@ -1650,11 +1712,14 @@ tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd) + vreq->prev_error = vreq->error; + vreq->error = 0; + ++ pthread_mutex_unlock(&vbd->mutex); + DBG(TLOG_DBG, "retry #%d of req %s, " + "sec 0x%08"PRIx64", iovcnt: %d\n", vreq->num_retries, + vreq->name, vreq->sec, vreq->iovcnt); + + err = tapdisk_vbd_issue_request(vbd, vreq); ++ ++ pthread_mutex_lock(&vbd->mutex); + /* + * if this request failed, but was not completed, + * we'll back off for a while. +@@ -1662,6 +1727,7 @@ tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd) + if (err && !tapdisk_vbd_request_completed(vbd, vreq)) + break; + } ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -1684,17 +1750,23 @@ tapdisk_vbd_issue_new_requests(td_vbd_t *vbd) + int err; + td_vbd_request_t *vreq, *tmp; + ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tmp, &vbd->new_requests) { ++ pthread_mutex_unlock(&vbd->mutex); + err = tapdisk_vbd_issue_request(vbd, vreq); ++ pthread_mutex_lock(&vbd->mutex); + /* + * if this request failed, but was not completed, + * we'll back off for a while. + */ +- if (err && !tapdisk_vbd_request_completed(vbd, vreq)) ++ if (err && !tapdisk_vbd_request_completed(vbd, vreq)) { ++ pthread_mutex_unlock(&vbd->mutex); + return err; ++ } + + tapdisk_vbd_count_new_request(vbd, vreq); + } ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -1704,12 +1776,18 @@ tapdisk_vbd_recheck_state(td_vbd_t *vbd) + { + int err = 0; + +- if (list_empty(&vbd->new_requests)) ++ pthread_mutex_lock(&vbd->mutex); ++ if (list_empty(&vbd->new_requests)) { ++ pthread_mutex_unlock(&vbd->mutex); + return 0; ++ } + + if (td_flag_test(vbd->state, TD_VBD_QUIESCED) || +- td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED)) ++ td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED)) { ++ pthread_mutex_unlock(&vbd->mutex); + return 0; ++ } ++ pthread_mutex_unlock(&vbd->mutex); + + err = tapdisk_vbd_issue_requests(vbd); + +@@ -1722,6 +1800,7 @@ tapdisk_vbd_kill_requests(td_vbd_t *vbd) + { + td_vbd_request_t *vreq, *tmp; + ++ pthread_mutex_lock(&vbd->mutex); + tapdisk_vbd_for_each_request(vreq, tmp, &vbd->new_requests) { + vreq->error = -ESHUTDOWN; + tapdisk_vbd_move_request(vreq, &vbd->completed_requests); +@@ -1731,6 +1810,7 @@ tapdisk_vbd_kill_requests(td_vbd_t *vbd) + vreq->error = -ESHUTDOWN; + tapdisk_vbd_move_request(vreq, &vbd->completed_requests); + } ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -1740,17 +1820,24 @@ tapdisk_vbd_issue_requests(td_vbd_t *vbd) + { + int err; + +- if (td_flag_test(vbd->state, TD_VBD_DEAD)) ++ pthread_mutex_lock(&vbd->mutex); ++ if (td_flag_test(vbd->state, TD_VBD_DEAD)) { ++ pthread_mutex_unlock(&vbd->mutex); + return tapdisk_vbd_kill_requests(vbd); ++ } + + if (td_flag_test(vbd->state, TD_VBD_QUIESCED) || + td_flag_test(vbd->state, TD_VBD_QUIESCE_REQUESTED)) { + +- if (td_flag_test(vbd->state, TD_VBD_RESUME_FAILED)) ++ if (td_flag_test(vbd->state, TD_VBD_RESUME_FAILED)) { ++ pthread_mutex_unlock(&vbd->mutex); + return tapdisk_vbd_kill_requests(vbd); +- else ++ } else { ++ pthread_mutex_unlock(&vbd->mutex); + return -EAGAIN; ++ } + } ++ pthread_mutex_unlock(&vbd->mutex); + + err = tapdisk_vbd_reissue_failed_requests(vbd); + if (err) +@@ -1765,8 +1852,11 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + gettimeofday(&vreq->ts, NULL); + vreq->vbd = vbd; + ++ pthread_mutex_lock(&vbd->mutex); + list_add_tail(&vreq->next, &vbd->new_requests); ++ vreq->list_head = &vbd->new_requests; + vbd->received++; ++ pthread_mutex_unlock(&vbd->mutex); + + return 0; + } +@@ -1774,11 +1864,13 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + void + tapdisk_vbd_kick(td_vbd_t *vbd) + { +- const struct list_head *list = &vbd->completed_requests; ++ const struct list_head *list; + td_vbd_request_t *vreq, *prev, *next; + + vbd->kicked++; + ++ pthread_mutex_lock(&vbd->mutex); ++ list = &vbd->completed_requests; + while (!list_empty(list)) { + + /* +@@ -1808,6 +1900,7 @@ tapdisk_vbd_kick(td_vbd_t *vbd) + prev->cb(prev, prev->error, prev->token, 1); + vbd->returned++; + } ++ pthread_mutex_unlock(&vbd->mutex); + } + + int +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index 23ca7289..f3fa0398 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -32,6 +32,7 @@ + #define _TAPDISK_VBD_H_ + + #include ++#include + + #include "tapdisk.h" + #include "scheduler.h" +@@ -134,6 +135,7 @@ struct td_vbd_handle { + struct list_head pending_requests; + struct list_head failed_requests; + struct list_head completed_requests; ++ pthread_mutex_t mutex; + + struct list_head next; + diff --git a/SOURCES/0029-tapdisk-protect-td_blktap_t-structure.patch b/SOURCES/0029-tapdisk-protect-td_blktap_t-structure.patch new file mode 100644 index 0000000..8a59e8b --- /dev/null +++ b/SOURCES/0029-tapdisk-protect-td_blktap_t-structure.patch @@ -0,0 +1,117 @@ +From 54d1cad6f6769c28bdda54baaa1e48737882f48b Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:40 +0100 +Subject: [PATCH] tapdisk: protect td_blktap_t structure + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect blktap structure from qcow2 concurrency. + +This is the second part of the tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-blktap.c | 18 ++++++++++++++---- + drivers/tapdisk-blktap.h | 1 + + 2 files changed, 15 insertions(+), 4 deletions(-) + +diff --git a/drivers/tapdisk-blktap.c b/drivers/tapdisk-blktap.c +index cf72b095..8b9eeea5 100644 +--- a/drivers/tapdisk-blktap.c ++++ b/drivers/tapdisk-blktap.c +@@ -87,18 +87,20 @@ struct td_blktap_req { + struct timeval ts; + }; + +-td_blktap_req_t * ++static td_blktap_req_t * + tapdisk_blktap_alloc_request(td_blktap_t *tap) + { + td_blktap_req_t *req = NULL; + ++ pthread_mutex_lock(&tap->mutex); + if (likely(tap->n_reqs_free)) + req = tap->reqs_free[--tap->n_reqs_free]; ++ pthread_mutex_unlock(&tap->mutex); + + return req; + } + +-void ++static void + tapdisk_blktap_free_request(td_blktap_t *tap, td_blktap_req_t *req) + { + BUG_ON(tap->n_reqs_free >= tap->n_reqs); +@@ -117,6 +119,8 @@ tapdisk_blktap_reqs_free(td_blktap_t *tap) + free(tap->reqs_free); + tap->reqs_free = NULL; + } ++ ++ pthread_mutex_destroy(&tap->mutex); + } + + static int +@@ -124,6 +128,8 @@ tapdisk_blktap_reqs_init(td_blktap_t *tap, int n_reqs) + { + int i, err; + ++ pthread_mutex_init(&tap->mutex, NULL); ++ + tap->reqs = malloc(n_reqs * sizeof(td_blktap_req_t)); + if (!tap->reqs) { + err = -errno; +@@ -200,6 +206,7 @@ tapdisk_blktap_fail_request(td_blktap_t *tap, + + BUG_ON(!tap->vma); + ++ pthread_mutex_lock(&tap->mutex); + rsp = BLKTAP_GET_RESPONSE(tap, tap->rsp_prod_pvt); + + rsp->id = msg->id; +@@ -207,6 +214,7 @@ tapdisk_blktap_fail_request(td_blktap_t *tap, + rsp->status = tapdisk_blktap_error_status(tap, error); + + __tapdisk_blktap_push_response(tap, 1); ++ pthread_mutex_unlock(&tap->mutex); + } + + static void +@@ -218,6 +226,7 @@ tapdisk_blktap_put_response(td_blktap_t *tap, + unsigned long long interval; + struct timeval now; + ++ pthread_mutex_lock(&tap->mutex); + BUG_ON(!tap->vma); + + rsp = BLKTAP_GET_RESPONSE(tap, tap->rsp_prod_pvt); +@@ -246,7 +255,10 @@ tapdisk_blktap_put_response(td_blktap_t *tap, + rsp->operation = op; + rsp->status = tapdisk_blktap_error_status(tap, error); + ++ tapdisk_blktap_free_request(tap, req); ++ + __tapdisk_blktap_push_response(tap, final); ++ pthread_mutex_unlock(&tap->mutex); + } + + static void +@@ -256,8 +268,6 @@ tapdisk_blktap_complete_request(td_blktap_t *tap, + { + if (likely(tap->vma)) + tapdisk_blktap_put_response(tap, req, error, final); +- +- tapdisk_blktap_free_request(tap, req); + } + + static void +diff --git a/drivers/tapdisk-blktap.h b/drivers/tapdisk-blktap.h +index ca576a77..5bd4aae8 100644 +--- a/drivers/tapdisk-blktap.h ++++ b/drivers/tapdisk-blktap.h +@@ -70,6 +70,7 @@ struct td_blktap { + td_blktap_req_t *reqs; + int n_reqs_free; + td_blktap_req_t **reqs_free; ++ pthread_mutex_t mutex; + + struct list_head entry; + diff --git a/SOURCES/0030-tapdisk-protect-td_xenblkif-structure.patch b/SOURCES/0030-tapdisk-protect-td_xenblkif-structure.patch new file mode 100644 index 0000000..f25259a --- /dev/null +++ b/SOURCES/0030-tapdisk-protect-td_xenblkif-structure.patch @@ -0,0 +1,271 @@ +From a49cc3b09e3a3f8f074dc85f7120ae3e41f5bece Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:47 +0100 +Subject: [PATCH] tapdisk: protect td_xenblkif structure + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect blkif structure from qcow2 concurrency. + +This is the third part of the tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/td-blkif.h | 5 +++++ + drivers/td-ctx.c | 12 ++++++++--- + drivers/td-req.c | 50 +++++++++++++++++++++++++++++++++++----------- + drivers/td-req.h | 15 -------------- + 4 files changed, 52 insertions(+), 30 deletions(-) + +diff --git a/drivers/td-blkif.h b/drivers/td-blkif.h +index d96888bd..2eaf080c 100644 +--- a/drivers/td-blkif.h ++++ b/drivers/td-blkif.h +@@ -134,6 +134,11 @@ struct td_xenblkif { + */ + struct td_vbd_handle *vbd; + ++ /** ++ * Protect requests list ++ */ ++ pthread_mutex_t mutex; ++ + /** + * stats + */ +diff --git a/drivers/td-ctx.c b/drivers/td-ctx.c +index e344a62f..d54d8457 100644 +--- a/drivers/td-ctx.c ++++ b/drivers/td-ctx.c +@@ -295,10 +295,13 @@ tapdisk_xenio_ctx_process_ring(struct td_xenblkif *blkif, + blkif_request_t **reqs; + int limit; + ++ pthread_mutex_lock(&blkif->mutex); + start = blkif->n_reqs_free; + +- if (unlikely(blkif->barrier.msg)) +- return 0; ++ if (unlikely(blkif->barrier.msg)) { ++ pthread_mutex_unlock(&blkif->mutex); ++ return 0; ++ } + + /* + * In each iteration, copy as many request descriptors from the shared ring +@@ -340,7 +343,7 @@ tapdisk_xenio_ctx_process_ring(struct td_xenblkif *blkif, + + n_reqs = start - blkif->n_reqs_free; + +- if (!n_reqs) ++ if (!n_reqs) { + /* + * We got a notification but the ring is empty. This is because we had + * previously suspended the operation of the ring because of a +@@ -350,7 +353,9 @@ tapdisk_xenio_ctx_process_ring(struct td_xenblkif *blkif, + * notification. This notification is the one we should have consumed, + * and can be ignored. + */ ++ pthread_mutex_unlock(&blkif->mutex); + return 0; ++ } + + if (blkif->in_polling) + /* We found at least one request, so keep polling some more */ +@@ -364,6 +369,7 @@ tapdisk_xenio_ctx_process_ring(struct td_xenblkif *blkif, + reqs = alloca(sizeof(blkif_request_t*) * n_reqs); + memcpy(reqs, &blkif->reqs_free[blkif->ring_size - start], + sizeof(blkif_request_t*) * n_reqs); ++ pthread_mutex_unlock(&blkif->mutex); + + tapdisk_xenblkif_queue_requests(blkif, reqs, n_reqs); + +diff --git a/drivers/td-req.c b/drivers/td-req.c +index 028e4bf6..1b7bd7ee 100644 +--- a/drivers/td-req.c ++++ b/drivers/td-req.c +@@ -74,9 +74,11 @@ td_xenblkif_bufcache_event(event_id_t id, char mode, void *private) + { + struct td_xenblkif *blkif = private; + ++ pthread_mutex_lock(&blkif->mutex); + td_xenblkif_bufcache_free(blkif); + + td_xenblkif_bufcache_evt_unreg(blkif); ++ pthread_mutex_unlock(&blkif->mutex); + } + + /** +@@ -439,10 +441,12 @@ out: + * @req the request to complete + * @error completion status of the request + * @final controls whether the other end should be notified ++ * @lock must always be true except in this function to control recursion + */ +-void ++static void + tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req* req, int err, const int final) ++ struct td_xenblkif_req* req, int err, const int final, ++ bool lock) + { + int _err; + long long *max = NULL, *sum = NULL, *cnt = NULL; +@@ -454,6 +458,8 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + ASSERT(req); + ASSERT(depth >= 0); + ++ if (lock) ++ pthread_mutex_lock(&blkif->mutex); + depth++; + + processing_barrier_message = +@@ -553,9 +559,10 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + /* + * If this is the last request, complete the barrier request. + */ +- if (tapdisk_xenblkif_barrier_should_complete(blkif)) ++ if (tapdisk_xenblkif_barrier_should_complete(blkif)) { + tapdisk_xenblkif_complete_request(blkif, +- msg_to_tapreq(blkif->barrier.msg), 0, 1); ++ msg_to_tapreq(blkif->barrier.msg), 0, 1, false); ++ } + } + + /* +@@ -566,11 +573,15 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + && !tapdisk_xenblkif_reqs_pending(blkif))) { + + RING_DEBUG(blkif, "destroying dead ring\n"); ++ pthread_mutex_unlock(&blkif->mutex); + tapdisk_xenblkif_destroy(blkif); ++ lock = 0; /* blkif with its mutex were destroyed above so don't try to unlock it */ + } + + out: + depth--; ++ if (lock) ++ pthread_mutex_unlock(&blkif->mutex); + } + + /** +@@ -595,13 +606,15 @@ __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, + req = container_of(vreq, struct td_xenblkif_req, vreq); + + if (error) { ++ pthread_mutex_lock(&blkif->mutex); + if (likely(!blkif->dead)) { + blkif->stats.errors.img++; + blkif->vbd_stats.stats->io_errors++; + } ++ pthread_mutex_unlock(&blkif->mutex); + } + +- tapdisk_xenblkif_complete_request(blkif, req, error, final); ++ tapdisk_xenblkif_complete_request(blkif, req, error, final, true); + } + + +@@ -625,6 +638,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + req->vma = td_xenblkif_bufcache_get(blkif); + if (unlikely(!req->vma)) { + err = errno; ++ RING_ERR(blkif, "errno %d: invalid vma\n", err); + goto out; + } + +@@ -779,8 +793,10 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, + goto out; + } + +- if (likely(req->msg.nr_segments)) ++ if (likely(req->msg.nr_segments)) { ++ pthread_mutex_lock(&blkif->mutex); + err = tapdisk_xenblkif_parse_request(blkif, req); ++ pthread_mutex_unlock(&blkif->mutex); + /* + * If we only got one request from the ring and that was a barrier one, + * check whether the barrier requests completion conditions are satisfied +@@ -789,10 +805,14 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, + * It could be that there are more requests in the ring after the barrier + * request, tapdisk_xenblkif_complete_request() will schedule a ring check. + */ +- else if (tapdisk_xenblkif_barrier_should_complete(blkif)) { +- tapdisk_xenblkif_complete_request(blkif, +- msg_to_tapreq(blkif->barrier.msg), 0, 1); +- err = 0; ++ } else { ++ pthread_mutex_lock(&blkif->mutex); ++ if (tapdisk_xenblkif_barrier_should_complete(blkif)) { ++ tapdisk_xenblkif_complete_request(blkif, ++ msg_to_tapreq(blkif->barrier.msg), 0, 1, false); ++ err = 0; ++ } ++ pthread_mutex_unlock(&blkif->mutex); + } + out: + return err; +@@ -876,7 +896,7 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, + if (err) { + /* TODO log error */ + nr_errors++; +- tapdisk_xenblkif_complete_request(blkif, req, err, 1); ++ tapdisk_xenblkif_complete_request(blkif, req, err, 1, true); + } + } + +@@ -884,8 +904,11 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, + dead and current request is the last one, hence adding + this check to avoid seg fault */ + +- if (nr_errors && blkif) ++ if (nr_errors && blkif) { ++ pthread_mutex_lock(&blkif->mutex); + xenio_blkif_put_response(blkif, NULL, 0, 1); ++ pthread_mutex_unlock(&blkif->mutex); ++ } + } + + void +@@ -905,6 +928,7 @@ tapdisk_xenblkif_reqs_free(struct td_xenblkif * const blkif) + free(blkif->reqs_free); + blkif->reqs_free = NULL; + ++ pthread_mutex_destroy(&blkif->mutex); + } + + int +@@ -916,6 +940,8 @@ tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif) + + ASSERT(td_blkif); + ++ pthread_mutex_init(&td_blkif->mutex, NULL); ++ + td_blkif->ring_size = td_blkif_ring_size(td_blkif); + ASSERT(td_blkif->ring_size > 0); + +diff --git a/drivers/td-req.h b/drivers/td-req.h +index 25eeb503..4d0cb24b 100644 +--- a/drivers/td-req.h ++++ b/drivers/td-req.h +@@ -118,21 +118,6 @@ tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif); + void + tapdisk_xenblkif_reqs_free(struct td_xenblkif * const blkif); + +-/** +- * Completes a request. If this is the last pending request of a dead block +- * interface, the block interface is destroyed, the caller must not access it +- * any more. +- * +- * @blkif the VBD the request belongs belongs to +- * @tapreq the request to complete +- * @error completion status of the request +- * @final controls whether the other end should be notified +- */ +- +-void +-tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, +- struct td_xenblkif_req* tapreq, int err, const int final); +- + #define msg_to_tapreq(_req) \ + container_of(_req, struct td_xenblkif_req, msg) + diff --git a/SOURCES/0031-tapdisk-protect-scheduler-structure.patch b/SOURCES/0031-tapdisk-protect-scheduler-structure.patch new file mode 100644 index 0000000..5badb62 --- /dev/null +++ b/SOURCES/0031-tapdisk-protect-scheduler-structure.patch @@ -0,0 +1,244 @@ +From 68feaf7278cffcbe4fe9ad338a1118d63c4a23e2 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 3 Apr 2025 16:06:24 +0200 +Subject: [PATCH] tapdisk: protect scheduler structure + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect scheduler structure from qcow2 concurrency. + +This is the fourth part of the tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/scheduler.c | 39 ++++++++++++++++++++++++----- + drivers/scheduler.h | 2 ++ + mockatests/drivers/test-scheduler.c | 4 +-- + 3 files changed, 37 insertions(+), 8 deletions(-) + +diff --git a/drivers/scheduler.c b/drivers/scheduler.c +index c18980eb..4be30570 100644 +--- a/drivers/scheduler.c ++++ b/drivers/scheduler.c +@@ -227,7 +227,7 @@ scheduler_check_events(scheduler_t *s, int nfds) + } + + static void +-scheduler_event_callback(event_t *event, char mode) ++scheduler_event_callback(scheduler_t *s, event_t *event, char mode) + { + if (event->mode & SCHEDULER_POLL_TIMEOUT + && !TV_IS_INF(event->timeout)) { +@@ -236,8 +236,11 @@ scheduler_event_callback(event_t *event, char mode) + TV_ADD(now, event->timeout, event->deadline); + } + +- if (!event->masked) ++ if (!event->masked) { ++ pthread_mutex_unlock(&s->mutex); + event->cb(event->id, mode, event->private); ++ pthread_mutex_lock(&s->mutex); ++ } + } + + static int +@@ -256,7 +259,7 @@ scheduler_run_events(scheduler_t *s) + if (pending) { + event->pending = 0; + /* NB. must clear before cb */ +- scheduler_event_callback(event, pending); ++ scheduler_event_callback(s, event, pending); + n_dispatched++; + } + } +@@ -312,6 +315,7 @@ scheduler_register_event(scheduler_t *s, char mode, int fd, + struct timeval timeout, event_cb_t cb, void *private) + { + event_t *event; ++ event_id_t id; + struct timeval now; + + if (!cb) +@@ -324,6 +328,9 @@ scheduler_register_event(scheduler_t *s, char mode, int fd, + if (!event) + return -ENOMEM; + ++ pthread_mutex_lock(&s->mutex); ++ id = scheduler_get_event_uuid(s); ++ + gettimeofday(&now, NULL); + + INIT_LIST_HEAD(&event->next); +@@ -338,12 +345,13 @@ scheduler_register_event(scheduler_t *s, char mode, int fd, + TV_ADD(now, timeout, event->deadline); + event->cb = cb; + event->private = private; +- event->id = scheduler_get_event_uuid(s); ++ event->id = id; + event->masked = 0; + + list_add_tail(&event->next, &s->events); ++ pthread_mutex_unlock(&s->mutex); + +- return event->id; ++ return id; + } + + void +@@ -354,11 +362,13 @@ scheduler_unregister_event(scheduler_t *s, event_id_t id) + if (!id) + return; + ++ pthread_mutex_lock(&s->mutex); + scheduler_for_each_event(s, event) + if (event->id == id) { + event->dead = 1; + break; + } ++ pthread_mutex_unlock(&s->mutex); + } + + void +@@ -369,11 +379,13 @@ scheduler_mask_event(scheduler_t *s, event_id_t id, int masked) + if (!id) + return; + ++ pthread_mutex_lock(&s->mutex); + scheduler_for_each_event(s, event) + if (event->id == id) { + event->masked = !!masked; + break; + } ++ pthread_mutex_unlock(&s->mutex); + } + + static void +@@ -391,8 +403,10 @@ scheduler_gc_events(scheduler_t *s) + void + scheduler_set_max_timeout(scheduler_t *s, struct timeval timeout) + { ++ pthread_mutex_lock(&s->mutex); + if (!TV_IS_INF(timeout)) + s->max_timeout = TV_MIN(s->max_timeout, timeout); ++ pthread_mutex_unlock(&s->mutex); + } + + int +@@ -401,6 +415,7 @@ scheduler_wait_for_events(scheduler_t *s) + int ret; + struct timeval tv; + ++ pthread_mutex_lock(&s->mutex); + s->depth++; + ret = 0; + +@@ -413,6 +428,7 @@ scheduler_wait_for_events(scheduler_t *s) + scheduler_prepare_events(s); + + tv = s->timeout; ++ pthread_mutex_unlock(&s->mutex); + + DBG("timeout: %ld.%ld, max_timeout: %ld.%ld\n", + s->timeout.tv_sec, s->timeout.tv_usec, s->max_timeout.tv_sec, s->max_timeout.tv_usec); +@@ -426,6 +442,7 @@ scheduler_wait_for_events(scheduler_t *s) + } + } while (ret == -EINTR); + ++ pthread_mutex_lock(&s->mutex); + if (ret < 0) { + EPRINTF("select failed: %s\n", strerror(-ret)); + goto out; +@@ -444,6 +461,7 @@ scheduler_wait_for_events(scheduler_t *s) + + out: + s->depth--; ++ pthread_mutex_unlock(&s->mutex); + + return ret; + } +@@ -453,6 +471,9 @@ scheduler_initialize(scheduler_t *s) + { + memset(s, 0, sizeof(scheduler_t)); + ++ pthread_mutex_init(&s->mutex, NULL); ++ ++ pthread_mutex_lock(&s->mutex); + s->uuid = 1; + s->depth = 0; + s->uuid_overflow = 0; +@@ -462,6 +483,7 @@ scheduler_initialize(scheduler_t *s) + FD_ZERO(&s->except_fds); + + INIT_LIST_HEAD(&s->events); ++ pthread_mutex_unlock(&s->mutex); + } + + int +@@ -474,10 +496,13 @@ scheduler_event_set_timeout(scheduler_t *sched, event_id_t event_id, struct time + if (!event_id) + return -EINVAL; + ++ pthread_mutex_lock(&sched->mutex); + scheduler_for_each_event(sched, event) { + if (event->id == event_id) { +- if (!(event->mode & SCHEDULER_POLL_TIMEOUT)) ++ if (!(event->mode & SCHEDULER_POLL_TIMEOUT)) { ++ pthread_mutex_unlock(&sched->mutex); + return -EINVAL; ++ } + event->timeout = timeo; + if (TV_IS_INF(event->timeout)) + event->deadline = TV_INF; +@@ -486,9 +511,11 @@ scheduler_event_set_timeout(scheduler_t *sched, event_id_t event_id, struct time + gettimeofday(&now, NULL); + TV_ADD(now, event->timeout, event->deadline); + } ++ pthread_mutex_unlock(&sched->mutex); + return 0; + } + } ++ pthread_mutex_unlock(&sched->mutex); + + return -ENOENT; + } +diff --git a/drivers/scheduler.h b/drivers/scheduler.h +index e2cae934..507b901a 100644 +--- a/drivers/scheduler.h ++++ b/drivers/scheduler.h +@@ -33,6 +33,7 @@ + + #include + #include ++#include + + #include "list.h" + +@@ -50,6 +51,7 @@ typedef struct scheduler { + fd_set except_fds; + + struct list_head events; ++ pthread_mutex_t mutex; + + event_id_t uuid; + int uuid_overflow; +diff --git a/mockatests/drivers/test-scheduler.c b/mockatests/drivers/test-scheduler.c +index 428dd732..8ede9693 100644 +--- a/mockatests/drivers/test-scheduler.c ++++ b/mockatests/drivers/test-scheduler.c +@@ -743,7 +743,7 @@ test_scheduler_callback(void **state) + // Update current time to time_now2 + fake_gettimeofday = (struct timeval){ .tv_sec = time_now2 }; + +- scheduler_event_callback(event1, test_mode); ++ scheduler_event_callback(&s, event1, test_mode, false); + + // Check callback has been called + assert_int_equal(event_cb_spy.was_called, 1); +@@ -776,7 +776,7 @@ test_scheduler_callback_ignores_masked_events(void **state) + event1->masked = true; + + const int test_mode = 1; +- scheduler_event_callback(event1, test_mode); ++ scheduler_event_callback(&s, event1, test_mode, false); + + // Check callback has not been called + assert_int_equal(event_cb_spy.was_called, 0); diff --git a/SOURCES/0032-mocka-fix-scheduler-tests-according-mutex-protection.patch b/SOURCES/0032-mocka-fix-scheduler-tests-according-mutex-protection.patch new file mode 100644 index 0000000..079d274 --- /dev/null +++ b/SOURCES/0032-mocka-fix-scheduler-tests-according-mutex-protection.patch @@ -0,0 +1,775 @@ +From 35dc5156cf69f3f11ba9e1cc06a83e6582759b88 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 21 Jan 2026 17:25:29 +0100 +Subject: [PATCH] mocka: fix scheduler tests according mutex protection + +Signed-off-by: Anthoine Bourgeois +--- + drivers/scheduler.c | 2 +- + drivers/scheduler.h | 1 - + mockatests/drivers/test-scheduler.c | 152 +++++++++++++++++++++++++++- + 3 files changed, 151 insertions(+), 4 deletions(-) + +diff --git a/drivers/scheduler.c b/drivers/scheduler.c +index 4be30570..58b6cc7a 100644 +--- a/drivers/scheduler.c ++++ b/drivers/scheduler.c +@@ -267,7 +267,7 @@ scheduler_run_events(scheduler_t *s) + return n_dispatched; + } + +-event_id_t ++static event_id_t + scheduler_get_event_uuid(scheduler_t *s) { + + bool uuid_found = false; +diff --git a/drivers/scheduler.h b/drivers/scheduler.h +index 507b901a..143065ba 100644 +--- a/drivers/scheduler.h ++++ b/drivers/scheduler.h +@@ -73,7 +73,6 @@ event_id_t scheduler_register_event(scheduler_t *, char mode, + int fd, struct timeval timeout, + event_cb_t cb, void *private); + +-event_id_t scheduler_get_event_uuid(scheduler_t *); + void scheduler_unregister_event(scheduler_t *, event_id_t); + void scheduler_mask_event(scheduler_t *, event_id_t, int masked); + void scheduler_set_max_timeout(scheduler_t *, struct timeval); +diff --git a/mockatests/drivers/test-scheduler.c b/mockatests/drivers/test-scheduler.c +index 8ede9693..234affac 100644 +--- a/mockatests/drivers/test-scheduler.c ++++ b/mockatests/drivers/test-scheduler.c +@@ -118,6 +118,7 @@ void + test_scheduler_set_max_timeout(void **state) + { + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 42; + struct timeval timeout = {.tv_sec = 0 }; + scheduler_set_max_timeout(&s, timeout); +@@ -129,6 +130,7 @@ test_scheduler_set_max_timeout_lower(void **state) + { + // Setting a new lower value will stick + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 430; + struct timeval timeout = {.tv_sec = 360 }; + scheduler_set_max_timeout(&s, timeout); +@@ -140,6 +142,7 @@ test_scheduler_set_max_timeout_higher(void **state) + { + // Setting a new higher value will be ignored + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 430; + struct timeval timeout = {.tv_sec = 458 }; + scheduler_set_max_timeout(&s, timeout); +@@ -151,6 +154,7 @@ test_scheduler_set_max_timeout_negative(void **state) + { + // Setting a new negative value will be ignored + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 458; + struct timeval timeout = {.tv_sec = -2 }; + scheduler_set_max_timeout(&s, timeout); +@@ -162,6 +166,7 @@ test_scheduler_set_max_timeout_inf(void **state) + { + // Setting TV_INF will be ignored + scheduler_t s; ++ scheduler_initialize(&s); + s.max_timeout.tv_sec = 458; + struct timeval timeout = TV_INF; + scheduler_set_max_timeout(&s, timeout); +@@ -184,7 +189,9 @@ test_scheduler_register_event_null_callback(void **state) + assert_int_equal(r, -EINVAL); + + scheduler_unregister_event(&s, r); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -203,7 +210,9 @@ test_scheduler_register_event_bad_mode(void **state) + assert_int_equal(r, -EINVAL); + + scheduler_unregister_event(&s, r); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -234,7 +243,9 @@ test_scheduler_register_multiple_events(void **state) + + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -268,7 +279,9 @@ test_scheduler_register_event_populates_event(void **state) + assert_int_equal(e->deadline.tv_usec, fake_gettimeofday.tv_usec + timeout.tv_usec); + + scheduler_unregister_event(&s, event_id1); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -290,7 +303,9 @@ test_scheduler_set_timeout_inf(void **state) + assert_true(TV_IS_INF(e->timeout)); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -298,6 +313,7 @@ test_scheduler_set_timeout_invalid_event(void **state) + { + // Invalid event ID will return EINVAL + scheduler_t sched; ++ scheduler_initialize(&sched); + event_id_t event_id = 0; + struct timeval timeo; + const int r = scheduler_event_set_timeout(&sched, event_id, timeo); +@@ -323,7 +339,9 @@ test_scheduler_set_timeout_on_non_polled_event(void **state) + + close(fd); + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -344,7 +362,9 @@ test_scheduler_set_timeout_missing_event(void **state) + assert_int_equal(r, -ENOENT); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -391,7 +411,9 @@ test_scheduler_set_timeout(void **state) + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); + scheduler_unregister_event(&s, id3); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -420,7 +442,9 @@ test_scheduler_set_timeout_inf_and_deadline(void **state) + assert_true(TV_IS_INF(e->deadline)); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -443,7 +467,9 @@ test_scheduler_unregister_event_will_set_dead_field(void **state) + assert_int_equal(e->dead, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -466,7 +492,9 @@ test_scheduler_unregister_event_will_ignore_invalid_event(void **state) + assert_int_not_equal(e->dead, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -492,7 +520,9 @@ test_scheduler_mask_event_will_set_masked_field(void **state) + assert_int_not_equal(e->masked, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -515,7 +545,9 @@ test_scheduler_mask_event_will_accept_non_zero_value(void **state) + assert_int_equal(e->masked, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -538,7 +570,9 @@ test_scheduler_mask_event_will_ignore_invalid_event_id(void **state) + assert_int_not_equal(e->masked, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -547,10 +581,12 @@ test_scheduler_get_uuid(void **state) + scheduler_t s; + scheduler_initialize(&s); + ++ pthread_mutex_lock(&s.mutex); + s.uuid = 1; + const event_id_t new_uuid = scheduler_get_event_uuid(&s); + assert_int_equal(new_uuid, 1); + assert_int_equal(s.uuid, 2); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -560,6 +596,7 @@ test_scheduler_get_uuid_overflow(void **state) + scheduler_t s; + scheduler_initialize(&s); + ++ pthread_mutex_lock(&s.mutex); + s.uuid = INT_MAX; + const event_id_t new_uuid = scheduler_get_event_uuid(&s); + assert_int_equal(new_uuid, INT_MAX); +@@ -568,6 +605,7 @@ test_scheduler_get_uuid_overflow(void **state) + const event_id_t new_uuid2 = scheduler_get_event_uuid(&s); + assert_int_equal(new_uuid2, 1); + assert_int_equal(s.uuid, 2); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -592,6 +630,7 @@ test_scheduler_get_uuid_overflow_fragmented(void **state) + // After an overflow the next UUID should be 2 + // because that is the next free event id + s.uuid = INT_MIN; ++ pthread_mutex_lock(&s.mutex); + const event_id_t new_uuid1 = scheduler_get_event_uuid(&s); + assert_int_equal(new_uuid1, 2); + // +---+---+---+--- +@@ -600,6 +639,7 @@ test_scheduler_get_uuid_overflow_fragmented(void **state) + + // The next UUID after that will be 4 because 3 is already used. + const event_id_t new_uuid2 = scheduler_get_event_uuid(&s); ++ pthread_mutex_unlock(&s.mutex); + assert_int_equal(new_uuid2, 4); + // +---+---+---+---+--- + // | 1 | 3 | 2 | 4 |... +@@ -607,7 +647,9 @@ test_scheduler_get_uuid_overflow_fragmented(void **state) + + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -645,7 +687,9 @@ test_scheduler_gc_will_remove_dead_events_from_list(void **state) + // +---+ +---+ + // | 1 |->| 3 | + // +---+ +---+ ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + assert_int_equal(event_queue_length(&s), 2); + + // event 1 is now linked to event 3 +@@ -654,7 +698,9 @@ test_scheduler_gc_will_remove_dead_events_from_list(void **state) + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); + scheduler_unregister_event(&s, id3); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -693,7 +739,9 @@ test_scheduler_check_timeouts(void **state) + e5->deadline.tv_sec = 3; // 5: skip because timeout not reached + e6->deadline.tv_sec = 1; // 6: mark because timeout has passed + ++ pthread_mutex_lock(&s.mutex); + scheduler_check_timeouts(&s); ++ pthread_mutex_unlock(&s.mutex); + assert_int_not_equal(e1->pending, SCHEDULER_POLL_TIMEOUT); // unchanged + assert_int_equal(e2->pending, SCHEDULER_POLL_TIMEOUT); // unchanged + assert_int_not_equal(e3->pending, SCHEDULER_POLL_TIMEOUT); // unchanged +@@ -707,7 +755,9 @@ test_scheduler_check_timeouts(void **state) + scheduler_unregister_event(&s, id4); + scheduler_unregister_event(&s, id5); + scheduler_unregister_event(&s, id6); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -743,7 +793,9 @@ test_scheduler_callback(void **state) + // Update current time to time_now2 + fake_gettimeofday = (struct timeval){ .tv_sec = time_now2 }; + +- scheduler_event_callback(&s, event1, test_mode, false); ++ pthread_mutex_lock(&s.mutex); ++ scheduler_event_callback(&s, event1, test_mode); ++ pthread_mutex_unlock(&s.mutex); + + // Check callback has been called + assert_int_equal(event_cb_spy.was_called, 1); +@@ -754,7 +806,9 @@ test_scheduler_callback(void **state) + assert_int_equal(event1->deadline.tv_sec, to.tv_sec + time_now2); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -776,7 +830,9 @@ test_scheduler_callback_ignores_masked_events(void **state) + event1->masked = true; + + const int test_mode = 1; +- scheduler_event_callback(&s, event1, test_mode, false); ++ pthread_mutex_lock(&s.mutex); ++ scheduler_event_callback(&s, event1, test_mode); ++ pthread_mutex_unlock(&s.mutex); + + // Check callback has not been called + assert_int_equal(event_cb_spy.was_called, 0); +@@ -784,7 +840,9 @@ test_scheduler_callback_ignores_masked_events(void **state) + assert_int_not_equal(event_cb_spy.id, event1->id); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -805,13 +863,17 @@ test_scheduler_run_events_run_callback_if_pending(void **state) + // Set event to pending + event->pending = SCHEDULER_POLL_TIMEOUT; + ++ pthread_mutex_lock(&s.mutex); + const int n_dispatched = scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(n_dispatched, 1); + assert_int_equal(event_cb_spy.was_called, 1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -831,13 +893,17 @@ test_scheduler_run_events_no_callback_if_not_pending(void **state) + + event->pending = 0; + ++ pthread_mutex_lock(&s.mutex); + const int n_dispatched = scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(n_dispatched, 0); + assert_int_equal(event_cb_spy.was_called, 0); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -858,7 +924,9 @@ test_scheduler_run_events_pending_mode_is_reset(void **state) + // Set event to pending + event->pending = SCHEDULER_POLL_TIMEOUT; + ++ pthread_mutex_lock(&s.mutex); + (void)scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // The callback gets the original pending value + assert_int_equal(event_cb_spy.mode, SCHEDULER_POLL_TIMEOUT); +@@ -867,7 +935,9 @@ test_scheduler_run_events_pending_mode_is_reset(void **state) + assert_int_not_equal(event->pending, SCHEDULER_POLL_TIMEOUT); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -890,13 +960,17 @@ test_scheduler_run_events_ignore_event_if_dead(void **state) + + event->dead = true; + ++ pthread_mutex_lock(&s.mutex); + const int n_dispatched = scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(n_dispatched, 0); + assert_int_equal(event_cb_spy.was_called, 0); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -906,7 +980,9 @@ test_scheduler_run_events_no_events(void **state) + scheduler_t s; + scheduler_initialize(&s); + ++ pthread_mutex_lock(&s.mutex); + const int n_dispatched = scheduler_run_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(n_dispatched, 0); + } +@@ -917,7 +993,9 @@ test_scheduler_prepare_events_no_events(void **state) + // scheduler_prepare_events no events no problem + scheduler_t s; + scheduler_initialize(&s); ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + assert_int_equal(s.max_fd, -1); + } + +@@ -937,12 +1015,16 @@ test_scheduler_prepare_events_masked_event_ignored(void **state) + // Mask the event here + event->masked = true; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -961,12 +1043,16 @@ test_scheduler_prepare_events_dead_event_ignored(void **state) + // Unalive event here + event->dead = true; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -981,12 +1067,16 @@ test_scheduler_add_read_event(void **state) + const struct timeval to = {}; + const int id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, test_fd); + + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1005,13 +1095,17 @@ test_scheduler_read_event_with_invalid_fd(void **state) + // Invalid event + event->fd = -2; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1026,12 +1120,16 @@ test_scheduler_add_write_event(void **state) + const struct timeval to = {}; + const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, test_fd); + + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1050,13 +1148,17 @@ test_scheduler_write_event_with_invalid_fd(void **state) + // Invalid event + event->fd = -2; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1071,12 +1173,16 @@ test_scheduler_add_except_event(void **state) + const struct timeval to = {}; + const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, test_fd); + + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1095,12 +1201,16 @@ test_scheduler_except_event_with_invalid_fd(void **state) + // Invalid event + event->fd = -2; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1116,14 +1226,18 @@ test_scheduler_no_timeout_events_then_timeout_is_max(void **state) + const struct timeval to = {}; + const int event_id = scheduler_register_event(&s, md, test_fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + const struct timeval expected_tv = TV_SECS(600); + assert_int_equal(s.timeout.tv_sec, expected_tv.tv_sec); + + close(test_fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1143,13 +1257,17 @@ test_scheduler_add_timeout_event(void **state) + const struct timeval time_now = { .tv_sec = 2, .tv_usec = 0}; + fake_gettimeofday = time_now; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // New timeout value is time to the event deadline + assert_int_equal(s.timeout.tv_sec, to.tv_sec - time_now.tv_sec); + + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1171,14 +1289,18 @@ test_scheduler_multiple_timeout_events_use_lowest_timeout(void **state) + const struct timeval time_now = { .tv_sec = 2, .tv_usec = 0}; + fake_gettimeofday = time_now; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // New timeout is based on the smaller event timeout value (to2). + assert_int_equal(s.timeout.tv_sec, to2.tv_sec - time_now.tv_sec); + + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1198,13 +1320,17 @@ test_scheduler_timeout_event_is_instant_if_deadline_is_now(void **state) + // Set the time now to the event timeout + fake_gettimeofday = to; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // New timeout is zero because deadline has already been reached. + assert_int_equal(s.timeout.tv_sec, 0); + + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1226,14 +1352,18 @@ test_scheduler_multiple_timeout_events_dont_interfere(void **state) + // Set the time now to the first event timeout + fake_gettimeofday = to1; + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + // Event though event2 still has 10 seconds left event1 is 0 therefor timeout is 0 + assert_int_equal(s.timeout.tv_sec, 0); + + scheduler_unregister_event(&s, id1); + scheduler_unregister_event(&s, id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1250,11 +1380,15 @@ test_scheduler_timeout_event_ignored_if_no_timeout(void **state) + fake_gettimeofday = (struct timeval){ .tv_sec = 0, .tv_usec = 0}; + const int id = scheduler_register_event(&s, md, fd, to, &fake_event_cb, NULL); + ++ pthread_mutex_lock(&s.mutex); + scheduler_prepare_events(&s); ++ pthread_mutex_unlock(&s.mutex); + + assert_int_equal(s.max_fd, -1); + scheduler_unregister_event(&s, id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1302,7 +1436,9 @@ test_scheduler_run_single_read_fd(void **state) + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + /* +@@ -1355,7 +1491,9 @@ test_scheduler_run_single_write_fd(void **state) + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + #if 0 +@@ -1428,7 +1566,9 @@ test_scheduler_run_single_dead_event(void **state) + + close(fd); + scheduler_unregister_event(&s, event_id); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + /* Create two events with the same fd but different callbacks. +@@ -1484,7 +1624,9 @@ test_scheduler_run_duplicate_fds_are_handled_once(void **state) + close(fd); + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + /* Register two events with different fds but the same callback. +@@ -1525,7 +1667,9 @@ test_scheduler_run_with_duplicate_callbacks(void **state) + close(fd2); + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1587,7 +1731,9 @@ test_scheduler_run_read_and_write_fd(void **state) + close(fd); + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } + + void +@@ -1635,5 +1781,7 @@ test_scheduler_run_deleted_duplicate_event(void **state) + + scheduler_unregister_event(&s, event_id1); + scheduler_unregister_event(&s, event_id2); ++ pthread_mutex_lock(&s.mutex); + scheduler_gc_events(&s); ++ pthread_mutex_unlock(&s.mutex); + } diff --git a/SOURCES/0033-tapdisk-protect-td_nbdserver-structures.patch b/SOURCES/0033-tapdisk-protect-td_nbdserver-structures.patch new file mode 100644 index 0000000..f8e8452 --- /dev/null +++ b/SOURCES/0033-tapdisk-protect-td_nbdserver-structures.patch @@ -0,0 +1,277 @@ +From d1a9950bc7c8bec5e7095748b8ee4a3c4e93f169 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 17 Oct 2025 15:14:44 +0200 +Subject: [PATCH] tapdisk: protect td_nbdserver structures + +libqcow2 spawns a thread to handle IO requests to qcow2 layer. +Add a mutex to protect NBD server structures from qcow2 concurrency. + +This is the fifth part of tapdisk threading protection. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-nbdserver.c | 55 +++++++++++++++++++++++++++++++++---- + drivers/tapdisk-nbdserver.h | 2 ++ + 2 files changed, 51 insertions(+), 6 deletions(-) + +diff --git a/drivers/tapdisk-nbdserver.c b/drivers/tapdisk-nbdserver.c +index 23f6e241..8d4f9269 100644 +--- a/drivers/tapdisk-nbdserver.c ++++ b/drivers/tapdisk-nbdserver.c +@@ -235,10 +235,13 @@ tapdisk_nbdserver_alloc_request(td_nbdserver_client_t *client) + + ASSERT(client); + ++ pthread_mutex_lock(&client->mutex); + if (likely(client->n_reqs_free)) + req = client->reqs_free[--client->n_reqs_free]; ++ pthread_mutex_unlock(&client->mutex); + + pending = tapdisk_nbdserver_reqs_pending(client); ++ pthread_mutex_lock(&client->mutex); + if (pending > client->max_used_reqs) + client->max_used_reqs = pending; + +@@ -246,7 +249,7 @@ tapdisk_nbdserver_alloc_request(td_nbdserver_client_t *client) + /* last free request, mask the events */ + tapdisk_server_mask_event(client->client_event_id, 1); + } +- ++ pthread_mutex_unlock(&client->mutex); + + return req; + } +@@ -318,17 +321,22 @@ void + tapdisk_nbdserver_free_request(td_nbdserver_client_t *client, + td_nbdserver_req_t *req, bool free_client_if_dead) + { ++ pthread_mutex_lock(&client->mutex); + tapdisk_nbdserver_set_free_request(client, req); + + if (unlikely(client->n_reqs_free == (client->n_reqs / 4))) { + /* free requests, unmask the events */ + tapdisk_server_mask_event(client->client_event_id, 0); + } ++ pthread_mutex_unlock(&client->mutex); + + if (unlikely(free_client_if_dead && + client->dead && +- !tapdisk_nbdserver_reqs_pending(client))) ++ !tapdisk_nbdserver_reqs_pending(client))) { ++ pthread_mutex_lock(&client->server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_lock(&client->server->mutex); ++ } + } + + static void +@@ -800,6 +808,7 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) + strerror(errno)); + goto fail; + } ++ pthread_mutex_init(&client->mutex, NULL); + + err = tapdisk_nbdserver_reqs_init(client, NBD_SERVER_NUM_REQS); + if (err < 0) { +@@ -810,8 +819,10 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) + client->client_fd = -1; + client->client_event_id = -1; + client->server = server; ++ pthread_mutex_lock(&server->mutex); + INIT_LIST_HEAD(&client->clientlist); + list_add(&client->clientlist, &server->clients); ++ pthread_mutex_unlock(&server->mutex); + + client->paused = 0; + client->dead = false; +@@ -820,8 +831,10 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) + return client; + + fail: +- if (client) ++ if (client) { ++ pthread_mutex_destroy(&client->mutex); + free(client); ++ } + + return NULL; + } +@@ -840,7 +853,7 @@ tapdisk_nbdserver_free_client(td_nbdserver_client_t *client) + + if (likely(!tapdisk_nbdserver_reqs_pending(client))) { + list_del(&client->clientlist); +- tapdisk_nbdserver_reqs_free(client); ++ pthread_mutex_destroy(&client->mutex); + free(client); + } else + client->dead = true; +@@ -1054,7 +1067,9 @@ tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) + if (tapdisk_nbdserver_enable_client(client) < 0) { + ERR("Error enabling client"); + tmp_fd = client->client_fd; ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + close(tmp_fd); + } + +@@ -1136,7 +1151,9 @@ tapdisk_nbdserver_newclient_fd_old(td_nbdserver_t *server, int new_fd) + INFO("About to enable client on fd %d", client->client_fd); + if (tapdisk_nbdserver_enable_client(client) < 0) { + ERR("Error enabling client"); ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + close(new_fd); + } + } +@@ -1164,7 +1181,9 @@ tapdisk_nbdserver_newclient_fd_new_fixed(td_nbdserver_t *server, int new_fd) + + if(tapdisk_nbdserver_new_protocol_handshake(client, new_fd) != 0) { + ERR("Error handshaking new client connection"); ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + close(new_fd); + return; + } +@@ -1290,7 +1309,9 @@ tapdisk_nbdserver_clientcb(event_id_t id, char mode, void *data) + break; + case TAPDISK_NBD_CMD_DISC: + INFO("Received close message. Sending reconnect header"); ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + INFO("About to send initial connection message"); + tapdisk_nbdserver_newclient_fd(server, fd); + INFO("Sent initial connection message"); +@@ -1338,7 +1359,9 @@ fail: + if (vreq) + tapdisk_nbd_server_free_vreq(client, vreq, false); + close(client->client_fd); ++ pthread_mutex_lock(&server->mutex); + tapdisk_nbdserver_free_client(client); ++ pthread_mutex_unlock(&server->mutex); + return; + } + +@@ -1431,7 +1454,10 @@ tapdisk_nbdserver_alloc(td_vbd_t *vbd, td_disk_info_t info, nbd_protocol_style_t + server->unix_listening_fd = -1; + server->unix_listening_event_id = -1; + server->style = style; ++ pthread_mutex_init(&server->mutex, NULL); ++ pthread_mutex_lock(&server->mutex); + INIT_LIST_HEAD(&server->clients); ++ pthread_mutex_unlock(&server->mutex); + + switch (style) { + case TAPDISK_NBD_PROTOCOL_OLD: +@@ -1479,6 +1505,7 @@ fail: + if (server) { + if (server->fdreceiver) + td_fdreceiver_stop(server->fdreceiver); ++ pthread_mutex_destroy(&server->mutex); + free(server); + } + +@@ -1494,12 +1521,14 @@ tapdisk_nbdserver_pause(td_nbdserver_t *server, bool log) + INFO("NBD server pause(%p)", server); + } + ++ pthread_mutex_lock(&server->mutex); + list_for_each_entry_safe(pos, q, &server->clients, clientlist){ + if (pos->paused != 1 && pos->client_event_id >= 0) { + tapdisk_nbdserver_disable_client(pos); + pos->paused = 1; + } + } ++ pthread_mutex_unlock(&server->mutex); + + if (server->fdrecv_listening_event_id >= 0) { + tapdisk_server_unregister_event(server->fdrecv_listening_event_id); +@@ -1732,15 +1761,18 @@ tapdisk_nbdserver_unpause(td_nbdserver_t *server) + "unix_listening_fd=%d", server, server->fdrecv_listening_fd, + server->unix_listening_fd); + ++ pthread_mutex_lock(&server->mutex); + list_for_each_entry_safe(pos, q, &server->clients, clientlist){ + if (pos->paused == 1) { + if((err = tapdisk_nbdserver_enable_client(pos)) < 0) { ++ pthread_mutex_unlock(&server->mutex); + ERR("Failed to enable nbd client after pause"); + return err; + } + pos->paused = 0; + } + } ++ pthread_mutex_unlock(&server->mutex); + + err = tapdisk_nbdserver_unpause_fdrecv(server); + if (err) +@@ -1761,8 +1793,10 @@ tapdisk_nbdserver_free(td_nbdserver_t *server) + + INFO("NBD server free(%p)", server); + ++ pthread_mutex_lock(&server->mutex); + list_for_each_entry_safe(pos, q, &server->clients, clientlist) + tapdisk_nbdserver_free_client(pos); ++ pthread_mutex_unlock(&server->mutex); + + if (server->fdrecv_listening_event_id >= 0) { + tapdisk_server_unregister_event(server->fdrecv_listening_event_id); +@@ -1796,15 +1830,20 @@ tapdisk_nbdserver_free(td_nbdserver_t *server) + if (err) + ERR("failed to delete NBD metrics: %s\n", strerror(errno)); + ++ pthread_mutex_destroy(&server->mutex); + free(server); + } + + int + tapdisk_nbdserver_reqs_pending(td_nbdserver_client_t *client) + { ++ int pending; + ASSERT(client); + +- return client->n_reqs - client->n_reqs_free; ++ pthread_mutex_lock(&client->mutex); ++ pending = client->n_reqs - client->n_reqs_free; ++ pthread_mutex_unlock(&client->mutex); ++ return pending; + } + + bool +@@ -1815,8 +1854,12 @@ tapdisk_nbdserver_contains_client(td_nbdserver_t *server, + + ASSERT(server); + ++ pthread_mutex_lock(&server->mutex); + list_for_each_entry(_client, &server->clients, clientlist) +- if (client == _client) ++ if (client == _client) { ++ pthread_mutex_unlock(&server->mutex); + return true; ++ } ++ pthread_mutex_unlock(&server->mutex); + return false; + } +diff --git a/drivers/tapdisk-nbdserver.h b/drivers/tapdisk-nbdserver.h +index 098dcb32..e92039f6 100644 +--- a/drivers/tapdisk-nbdserver.h ++++ b/drivers/tapdisk-nbdserver.h +@@ -125,6 +125,7 @@ struct td_nbdserver { + char sockpath[TAPDISK_NBDSERVER_MAX_PATH_LEN]; + + struct list_head clients; ++ pthread_mutex_t mutex; + + stats_t nbd_stats; + +@@ -137,6 +138,7 @@ struct td_nbdserver_client { + struct td_iovec *iovecs; + int n_reqs_free; + td_nbdserver_req_t **reqs_free; ++ pthread_mutex_t mutex; + + int client_fd; + int client_event_id; diff --git a/SOURCES/0034-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch b/SOURCES/0034-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch new file mode 100644 index 0000000..47f2f92 --- /dev/null +++ b/SOURCES/0034-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch @@ -0,0 +1,130 @@ +From 2e1911694415cf75617256838024de8a96e9169d Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 12 Mar 2025 13:46:25 +0100 +Subject: [PATCH] libqcow2: prepare proper cleanup of libqcow2 on close + +The close operation will stop the libqcow2 thread and we'll need to +clean-up resources to prepare the reopen or unpause. + +Signed-off-by: Anthoine Bourgeois +--- + include/qemu/main-loop.h | 2 ++ + qcow2/lib/system/cpus.c | 5 +++++ + qcow2/lib/util/main-loop.c | 45 +++++++++++++++++++++++++++++++++++++- + 3 files changed, 51 insertions(+), 1 deletion(-) + +diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h +index e0a0be44..9a6f11a8 100644 +--- a/include/qemu/main-loop.h ++++ b/include/qemu/main-loop.h +@@ -42,6 +42,7 @@ typedef struct MainLoop MainLoop; + #endif + + void qemu_init_cpu_loop(void); ++void qemu_deinit_cpu_loop(void); + + /** + * qemu_init_main_loop: Set up the process so that it can run the main loop. +@@ -57,6 +58,7 @@ void qemu_init_cpu_loop(void); + * In the case of QEMU tools, this will also start/initialize timers. + */ + int qemu_init_main_loop(Error **errp); ++int qemu_deinit_main_loop(void); + + /** + * main_loop_wait: Run one iteration of the main loop. +diff --git a/qcow2/lib/system/cpus.c b/qcow2/lib/system/cpus.c +index 81236835..fd3f991c 100644 +--- a/qcow2/lib/system/cpus.c ++++ b/qcow2/lib/system/cpus.c +@@ -429,6 +429,11 @@ void qemu_init_cpu_loop(void) + //qemu_thread_get_self(&io_thread); + } + ++void qemu_deinit_cpu_loop(void) ++{ ++ qemu_mutex_destroy(&bql); ++} ++ + #if 0 + void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) + { +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +index 817e65a6..9b212ff4 100644 +--- a/qcow2/lib/util/main-loop.c ++++ b/qcow2/lib/util/main-loop.c +@@ -39,8 +39,12 @@ + #include + #endif + ++static void iohandler_deinit(void); ++ + #ifndef _WIN32 + ++static int sigfd = -1; ++ + /* If we have signalfd, we mask out the signals we want to handle and then + * use signalfd to listen for them. We rely on whatever the current signal + * handler is to dispatch the signals when we receive them. +@@ -86,7 +90,6 @@ static void sigfd_handler(void *opaque) + + static int qemu_signal_init(Error **errp) + { +- int sigfd; + sigset_t set; + + /* +@@ -191,6 +194,39 @@ int qemu_init_main_loop(Error **errp) + return 0; + } + ++int qemu_deinit_main_loop(void) ++{ ++ GSource *src; ++ ++ src = iohandler_get_g_source(); ++ g_source_unref(src); ++ g_source_remove(g_source_get_id(src)); ++ g_source_unref(src); ++ ++ src = aio_get_g_source(qemu_aio_context); ++ g_source_unref(src); ++ g_source_remove(g_source_get_id(src)); ++ g_source_unref(src); ++ ++ g_array_free(gpollfds, TRUE); ++ ++ qemu_bh_delete(qemu_notify_bh); ++ qemu_notify_bh = NULL; ++ ++ if (sigfd != -1) { ++ aio_set_fd_handler(iohandler_get_aio_context(), sigfd, NULL, NULL, NULL, NULL, NULL); ++ close(sigfd); ++ sigfd = -1; ++ } ++ ++ iohandler_deinit(); ++ qemu_aio_context = NULL; ++ ++ timerlistgroup_deinit(&main_loop_tlg); ++ ++ return 0; ++} ++ + #if 0 + static void main_loop_update_params(EventLoopBase *base, Error **errp) + { +@@ -633,6 +669,13 @@ static void iohandler_init(void) + } + } + ++static void iohandler_deinit(void) ++{ ++ if (iohandler_ctx) { ++ iohandler_ctx = NULL; ++ } ++} ++ + AioContext *iohandler_get_aio_context(void) + { + iohandler_init(); diff --git a/SOURCES/0035-libqcow2-mask-signals-used-by-tapdisk.patch b/SOURCES/0035-libqcow2-mask-signals-used-by-tapdisk.patch new file mode 100644 index 0000000..47b05a7 --- /dev/null +++ b/SOURCES/0035-libqcow2-mask-signals-used-by-tapdisk.patch @@ -0,0 +1,41 @@ +From 74a73e8eaa5262a189a8e85c97dccab4e95a20e8 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 15 Oct 2025 14:35:21 +0200 +Subject: [PATCH] libqcow2: mask signals used by tapdisk + +To avoid conflict and signals stolen by the libqcow2 this patch masks the +signals used by tapdisk so they are exclusive to it. + +Signed-off-by: Anthoine Bourgeois +--- + qcow2/lib/util/main-loop.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +index 9b212ff4..82d737fe 100644 +--- a/qcow2/lib/util/main-loop.c ++++ b/qcow2/lib/util/main-loop.c +@@ -102,6 +102,11 @@ static int qemu_signal_init(Error **errp) + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGBUS); ++ sigaddset(&set, SIGINT); ++ sigaddset(&set, SIGXFSZ); ++ //sigaddset(&set, SIGUSR1); // SIG_IPI == SIGUSR1 ++ sigaddset(&set, SIGUSR2); ++ sigaddset(&set, SIGHUP); + /* SIGINT cannot be handled via signalfd, so that ^C can be used + * to interrupt QEMU when it is being run under gdb. SIGHUP and + * SIGTERM are also handled asynchronously, even though it is not +@@ -110,6 +115,11 @@ static int qemu_signal_init(Error **errp) + pthread_sigmask(SIG_BLOCK, &set, NULL); + + sigdelset(&set, SIG_IPI); ++ sigdelset(&set, SIGBUS); ++ sigdelset(&set, SIGINT); ++ sigdelset(&set, SIGXFSZ); ++ sigdelset(&set, SIGUSR2); ++ sigdelset(&set, SIGHUP); + sigfd = qemu_signalfd(&set); + if (sigfd == -1) { + error_setg_errno(errp, errno, "failed to create signalfd"); diff --git a/SOURCES/0036-tapdisk-replace-signals-handling-by-signalfd.patch b/SOURCES/0036-tapdisk-replace-signals-handling-by-signalfd.patch new file mode 100644 index 0000000..cc83a2b --- /dev/null +++ b/SOURCES/0036-tapdisk-replace-signals-handling-by-signalfd.patch @@ -0,0 +1,138 @@ +From 7cd6427151a6f9afbc04f93525b6a7b8751bb9e8 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Fri, 17 Oct 2025 18:10:41 +0200 +Subject: [PATCH] tapdisk: replace signals handling by signalfd + +With libqcow2, each of its threads create an signalfd to handle signals +at the thread level. Implement the same signalfd handler for tapdisk +thread to direct signals. + +No functional change are expected, but to achieve this, we would have +had to remove SIGINT from the managed signals in order for GDB to work. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/tapdisk-server.c | 63 ++++++++++++++++++++++++++++++++++------ + 1 file changed, 54 insertions(+), 9 deletions(-) + +diff --git a/drivers/tapdisk-server.c b/drivers/tapdisk-server.c +index a165d2f2..bf426735 100644 +--- a/drivers/tapdisk-server.c ++++ b/drivers/tapdisk-server.c +@@ -39,7 +39,8 @@ + #include + #include + #include +-#include ++#include ++#include + #ifdef HAVE_EVENTFD + #include + #else +@@ -100,6 +101,8 @@ typedef struct tapdisk_server { + } cpumond_state; + + event_id_t tlog_reopen_evid; ++ event_id_t signal_handler_evid; ++ int sigfd; + } tapdisk_server_t; + + static tapdisk_server_t server; +@@ -387,6 +390,12 @@ tapdisk_server_close(void) + if (likely(server.tlog_reopen_evid >= 0)) + tapdisk_server_unregister_event(server.tlog_reopen_evid); + ++ if (likely(server.signal_handler_evid >= 0)) ++ tapdisk_server_unregister_event(server.signal_handler_evid); ++ ++ if (likely(server.sigfd > 0)) ++ close(server.sigfd); ++ + tapdisk_server_close_tlog(); + tapdisk_server_close_aio(); + } +@@ -421,15 +430,25 @@ __tapdisk_server_run(void) + } + + static void +-tapdisk_server_signal_handler(int signal) ++tapdisk_server_signal_handler(event_id_t id, char mode __attribute__((unused)), void *private) + { ++ int signal; ++ ssize_t size; ++ struct signalfd_siginfo fdsi; + td_vbd_t *vbd, *tmp; + struct td_xenblkif *blkif; + static int xfsz_error_sent = 0; + ++ size = read(server.sigfd, &fdsi, sizeof(fdsi)); ++ if (size != sizeof(fdsi)) { ++ ERR(EFBIG, "failed to read signals"); ++ return; ++ } ++ ++ signal = fdsi.ssi_signo; ++ + switch (signal) { + case SIGBUS: +- case SIGINT: + tapdisk_server_for_each_vbd(vbd, tmp) + tapdisk_vbd_close(vbd); + break; +@@ -777,6 +796,7 @@ tapdisk_server_init(void) + + out: + server.tlog_reopen_evid = -1; ++ server.signal_handler_evid = -1; + + return 0; + } +@@ -828,17 +848,42 @@ int + tapdisk_server_run() + { + int err; ++ sigset_t set; + + err = tapdisk_set_resource_limits(); + if (err) + return err; + +- signal(SIGBUS, tapdisk_server_signal_handler); +- signal(SIGINT, tapdisk_server_signal_handler); +- signal(SIGUSR1, tapdisk_server_signal_handler); +- signal(SIGUSR2, tapdisk_server_signal_handler); +- signal(SIGHUP, tapdisk_server_signal_handler); +- signal(SIGXFSZ, tapdisk_server_signal_handler); ++ sigemptyset(&set); ++ sigaddset(&set, SIGBUS); ++ sigaddset(&set, SIGUSR1); ++ sigaddset(&set, SIGUSR2); ++ sigaddset(&set, SIGHUP); ++ sigaddset(&set, SIGXFSZ); ++ server.sigfd = signalfd(-1, &set, 0); ++ if (server.sigfd == -1) { ++ err = errno; ++ EPRINTF("failed to create a new signalfd: %s\n", ++ strerror(-err)); ++ goto out; ++ } ++ ++ if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) { ++ err = errno; ++ EPRINTF("failed to block signals we'd like to handle with signalfd: %s\n", ++ strerror(-err)); ++ goto out; ++ } ++ ++ err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD, ++ server.sigfd, TV_ZERO, ++ tapdisk_server_signal_handler, &server); ++ if (unlikely(err < 0)) { ++ EPRINTF("failed to register signal handler event: %s\n", strerror(-err)); ++ goto out; ++ } ++ ++ server.signal_handler_evid = err; + + err = tapdisk_server_register_event(SCHEDULER_POLL_TIMEOUT, -1, TV_INF, + tlog_reopen_cb, NULL); diff --git a/SOURCES/0037-qcow2-driver-support-qcow2-images-in-tapdisk.patch b/SOURCES/0037-qcow2-driver-support-qcow2-images-in-tapdisk.patch new file mode 100644 index 0000000..15a58c7 --- /dev/null +++ b/SOURCES/0037-qcow2-driver-support-qcow2-images-in-tapdisk.patch @@ -0,0 +1,977 @@ +From 9561c2c6b30a9eed55fe1fe24721956ede41f6fe Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:50 +0100 +Subject: [PATCH] qcow2: driver: support qcow2 images in tapdisk + +This commit creates the tapdisk driver for qcow2 format. +It create a thread on open/unpause and destroy it on close/pause. +The libqcow2 handles parents of the VDI on it side, no need for tapdisk +to open parents. Read/Write requests are forward to libqcow2. + +Signed-off-by: Anthoine Bourgeois +Signed-off-by: Emmanuel Varagnat +--- + drivers/Makefile.am | 4 + + drivers/block-qcow2.c | 882 +++++++++++++++++++++++++++++++++++++ + drivers/tapdisk-disktype.c | 8 +- + 3 files changed, 888 insertions(+), 6 deletions(-) + create mode 100644 drivers/block-qcow2.c + +diff --git a/drivers/Makefile.am b/drivers/Makefile.am +index c03ef7ed..70bfd2d5 100644 +--- a/drivers/Makefile.am ++++ b/drivers/Makefile.am +@@ -6,6 +6,7 @@ AM_CFLAGS += $(if $(GCOV),-fprofile-dir=/tmp/coverage/blktap/drivers -fprofile-a + + AM_CPPFLAGS = -D_GNU_SOURCE + AM_CPPFLAGS += -I$(top_srcdir)/include ++AM_CPPFLAGS += $(GLIB_CFLAGS) + + libexec_PROGRAMS = tapdisk + +@@ -90,6 +91,7 @@ libtapdisk_la_SOURCES += block-lcache.c + libtapdisk_la_SOURCES += block-llcache.c + libtapdisk_la_SOURCES += block-nbd.c + libtapdisk_la_SOURCES += block-log.c ++libtapdisk_la_SOURCES += block-qcow2.c + + # shared ring + libtapdisk_la_SOURCES += td-blkif.c +@@ -102,12 +104,14 @@ libtapdisk_la_SOURCES += td-stats.c + libtapdisk_la_SOURCES += td-stats.h + + libtapdisk_la_LIBADD = ../vhd/lib/libvhd.la ++libtapdisk_la_LIBADD += ../qcow2/lib/libqcow2.la + libtapdisk_la_LIBADD += -laio + libtapdisk_la_LIBADD += -lxenevtchn + libtapdisk_la_LIBADD += -lxengnttab + libtapdisk_la_LIBADD += -lz + libtapdisk_la_LIBADD += -lrt + libtapdisk_la_LIBADD += -ldl ++libtapdisk_la_LIBADD += -lm + + # encryption support + lib_LTLIBRARIES = libblockcrypto.la +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +new file mode 100644 +index 00000000..6fc3cc6a +--- /dev/null ++++ b/drivers/block-qcow2.c +@@ -0,0 +1,882 @@ ++/* ++ * Copyright (c) 2024, Vates ++ * ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of its ++ * contributors may be used to endorse or promote products derived from ++ * this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* ++ * block-qcow2.c: asynchronous qcow2 implementation. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "qemu/osdep.h" ++#include "qcow2.h" ++#include "qemu/main-loop.h" ++#include "hw/block/block.h" ++#include "qemu/error-report.h" ++#include "qapi/error.h" ++#include "sysemu/block-backend.h" ++#include "qapi/qmp/qdict.h" ++#include "tapdisk.h" ++#include "tapdisk-driver.h" ++#include "tapdisk-interface.h" ++#include "tapdisk-disktype.h" ++#include "tapdisk-storage.h" ++//#include "block-crypto.h" ++ ++#define DEBUGGING 2 ++ ++#define __TRACE(s) \ ++ do { \ ++ DBG(TLOG_DBG, "%s: QUEUED: %" PRIu64 ", COMPLETED: %" \ ++ PRIu64", RETURNED: %" PRIu64 ", DATA_ALLOCATED: " \ ++ "%u\n", \ ++ blk_name(s->conf.blk), s->queued, s->completed, s->returned, \ ++ QCOW2_REQS - s->vreq_free_count); \ ++ } while(0) ++ ++#if (DEBUGGING == 1) ++ #define DBG(level, _f, _a...) DPRINTF(_f, ##_a) ++ #define ERR(_s, err, _f, _a...) DPRINTF("ERROR: %d: " _f, err, ##_a) ++ #define TRACE(s) ((void)0) ++#elif (DEBUGGING == 2) ++ #define DBG(level, _f, _a...) tlog_write(level, _f, ##_a) ++ #define ERR(_s, _err, _f, _a...) tlog_drv_error((_s)->driver, _err, _f, ##_a) ++ #define TRACE(s) __TRACE(s) ++#else ++ #define DBG(level, _f, _a...) ((void)0) ++ #define ERR(_s, err, _f, _a...) ((void)0) ++ #define TRACE(s) ((void)0) ++#endif ++ ++enum qcow2_ops { ++ QCOW2_OP_READ, ++ QCOW2_OP_WRITE, ++}; ++ ++struct qcow2_state; ++struct qcow2_request; ++ ++#define QCOW2_REQS TAPDISK_DATA_REQUESTS ++ ++struct qcow2_request { ++ int error; ++ enum qcow2_ops op; ++ td_request_t treq; ++ struct qcow2_state *state; ++ BlockAIOCB *aiocb; ++ QSIMPLEQ_ENTRY(qcow2_request) list; ++ int aio_inflight; ++ QEMUIOVector qiov; ++#if DEBUGGING != 0 ++ int id; ++ struct timeval allocate_tv; ++ struct timeval submit_tv; ++ struct timeval complete_tv; ++#endif ++}; ++ ++#if DEBUGGING != 0 ++struct io_stat { ++ uint64_t max_val; ++ uint64_t min_val; ++ uint64_t samples; ++ ++ double mean; ++ double S; ++}; ++#endif ++ ++struct qcow2_state { ++ td_driver_t *driver; ++ const char *name; ++ struct td_vbd_encryption *encryption; ++ td_flag_t flags; ++ ++ BlockConf conf; ++ int blk_shift; ++ ++ int vreq_free_count; ++ struct qcow2_request *vreq_free[QCOW2_REQS]; ++ struct qcow2_request vreq_list[QCOW2_REQS]; ++ QSIMPLEQ_HEAD(inflight_head, qcow2_request) inflight; ++ ++ QEMUBH *bh; ++ AioContext *ctx; ++ ++ /* Open thread */ ++ QemuThread thread; ++ pthread_mutex_t lock; ++ pthread_cond_t cond; ++ bool driver_opened; ++ int open_status; ++ MemReentrancyGuard mem_reentrancy_guard; ++ ++ /* Stats */ ++ uint64_t queued; ++ uint64_t completed; ++ uint64_t returned; ++ uint64_t reads; ++ uint64_t read_size; ++ uint64_t writes; ++ uint64_t write_size; ++ uint64_t kick; ++ uint64_t schedule; ++#if DEBUGGING != 0 ++ struct io_stat read_slat; ++ struct io_stat read_clat; ++ struct io_stat read_lat; ++ struct io_stat write_slat; ++ struct io_stat write_clat; ++ struct io_stat write_lat; ++#endif ++}; ++ ++#define test_qcow2_flag(word, flag) ((word) & (flag)) ++#define set_qcow2_flag(word, flag) ((word) |= (flag)) ++#define clear_qcow2_flag(word, flag) ((word) &= ~(flag)) ++ ++static void qcow2_complete(void *, int); ++static inline void do_aio_read(struct qcow2_state *s, struct qcow2_request *req); ++static inline void do_aio_write(struct qcow2_state *s, struct qcow2_request *req); ++ ++static int ++qcow2_initialize(struct qcow2_state *s, Error **perr) ++{ ++ int err; ++ ++#if DEBUGGING != 0 ++ s->read_slat.min_val = ULONG_MAX; ++ s->read_clat.min_val = ULONG_MAX; ++ s->read_lat.min_val = ULONG_MAX; ++ s->write_slat.min_val = ULONG_MAX; ++ s->write_clat.min_val = ULONG_MAX; ++ s->write_lat.min_val = ULONG_MAX; ++#endif ++ ++ qemu_init_cpu_loop(); ++ bql_lock(); ++ ++ bdrv_init(); ++ ++ err = qemu_init_main_loop(perr); ++ if (err != 0) { ++ EPRINTF("failed to initialize main loop %d\n", err); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void ++qcow2_free(struct qcow2_state *s) ++{ ++ qemu_deinit_main_loop(); ++ ++ bql_unlock(); ++ qemu_deinit_cpu_loop(); ++} ++ ++static void qcow2_handle_requests(struct qcow2_state *s) ++{ ++ struct qcow2_request *req; ++ ++ pthread_mutex_lock(&s->lock); ++ while ((req = QSIMPLEQ_FIRST(&s->inflight))) { ++ QSIMPLEQ_REMOVE_HEAD(&s->inflight, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ switch (req->op) { ++ case QCOW2_OP_READ: ++ do_aio_read(s, req); ++ break; ++ case QCOW2_OP_WRITE: ++ do_aio_write(s, req); ++ break; ++ } ++ pthread_mutex_lock(&s->lock); ++ } ++ pthread_mutex_unlock(&s->lock); ++} ++ ++static void block_bh(void *opaque) ++{ ++ struct qcow2_state *s = opaque; ++ ++ qcow2_handle_requests(s); ++} ++ ++static void * ++qcow2_open(void *opaque) ++{ ++ int o_flags, err, i; ++ struct qcow2_state *s; ++ td_driver_t *driver; ++ td_flag_t flags; ++ const char *name; ++ QDict *options; ++ QDict *file_layer; ++ Error *local_err = NULL; ++ BlockConf *conf = NULL; ++ bool writethrough; ++ bool has_aio_native = true; ++ const char *cache = "none"; ++ bool has_discard = false; ++ const char *discard = "off"; ++ ++ s = opaque; ++ conf = &s->conf; ++ driver = s->driver; ++ name = s->name; ++ flags = s->flags; ++ ++ err = qcow2_initialize(s, &local_err); ++ if (err) { ++ error_report("Initialization error: %s", error_get_pretty(local_err)); ++ goto fail1; ++ } ++ ++ o_flags = 0; ++ ++ if (test_qcow2_flag(flags, TD_OPEN_RDONLY)) ++ clear_qcow2_flag(o_flags, BDRV_O_RDWR); ++ else ++ set_qcow2_flag(o_flags, BDRV_O_RDWR); ++ ++ if ((test_qcow2_flag(flags, TD_OPEN_RDONLY) || ++ test_qcow2_flag(flags, TD_OPEN_LOCAL_CACHE)) && ++ test_qcow2_flag(flags, TD_OPEN_NO_O_DIRECT)) { ++ clear_qcow2_flag(o_flags, BDRV_O_NOCACHE); ++ cache = "writeback"; ++ has_aio_native = false; ++ } ++ ++ if (test_qcow2_flag(flags, TD_OPEN_QUERY)) { ++ clear_qcow2_flag(o_flags, BDRV_O_RDWR); ++ set_qcow2_flag(o_flags, BDRV_O_NO_IO); ++ set_qcow2_flag(o_flags, BDRV_O_NOCACHE); ++ } ++ if (test_qcow2_flag(o_flags, TD_OPEN_LOCAL_CACHE)) { ++ cache = "writeback"; ++ has_aio_native = false; ++ } ++ ++ options = qdict_new(); ++ file_layer = qdict_new(); ++ ++ qdict_put_str(file_layer, "filename", name); ++ ++ qdict_put_str(options, "driver", "qcow2"); ++ ++ if (has_aio_native) { ++ has_discard = true; ++ discard = "unmap"; ++ } ++ ++ if (has_discard) { ++ qdict_put_str(options, "discard", discard); ++ qdict_put_str(file_layer, "discard", discard); ++ qdict_put_str(options, "detect-zeroes", discard); ++ qdict_put_str(file_layer, "detect-zeroes", discard); ++ } ++ ++ if (has_aio_native) { ++ qdict_put_str(file_layer, "aio", "native"); ++ o_flags |= BDRV_O_NATIVE_AIO; ++ } ++ ++ qdict_put(options, "file", file_layer); ++ ++ err = bdrv_parse_cache_mode(cache, &o_flags, &writethrough); ++ if (err < 0) { ++ error_setg(&local_err, "Invalid source cache option: %s", cache); ++ goto fail1; ++ } ++ ++ if (has_discard) { ++ err = bdrv_parse_discard_flags(discard, &o_flags); ++ if (err < 0) { ++ error_setg(&local_err, "Invalid discard option: %s", cache); ++ goto fail1; ++ } ++ } ++ ++ DPRINTF("discard %d (%s), aio_native %d (cache %s), flags %x\n", has_discard, discard, has_aio_native, cache, o_flags); ++ ++ conf->blk = blk_new_open(name, NULL, options, o_flags, &local_err); ++ if (!conf->blk) { ++ goto fail1; ++ } ++ blk_set_enable_write_cache(conf->blk, !writethrough); ++ ++ ++ if (!blk_is_inserted(conf->blk)) { ++ error_setg(&local_err, "device needs media, but drive is empty"); ++ goto fail; ++ } ++ ++ if (!blkconf_apply_backend_options(conf, test_qcow2_flag(flags, TD_OPEN_RDONLY), true, &local_err)) { ++ goto fail; ++ } ++ ++ if (!blkconf_geometry(conf, NULL, 65535, 255, 255, &local_err)) { ++ goto fail; ++ } ++ ++ if (!blkconf_blocksizes(conf, &local_err)) { ++ goto fail; ++ } ++ ++ if (conf->discard_granularity == -1) { ++ conf->discard_granularity = conf->physical_block_size; ++ } ++ ++ s->vreq_free_count = QCOW2_REQS; ++ for (i = 0; i < QCOW2_REQS; i++) { ++ s->vreq_free[i] = s->vreq_list + i; ++ qemu_iovec_init(&s->vreq_free[i]->qiov, 1); ++ } ++ ++ s->blk_shift = 31 - clz32(conf->logical_block_size); ++ driver->info.size = blk_getlength(conf->blk) >> s->blk_shift; ++ driver->info.sector_size = conf->logical_block_size; ++ driver->info.info = 0; ++ ++ QSIMPLEQ_INIT(&s->inflight); ++ s->ctx = qemu_get_aio_context(); ++ s->bh = aio_bh_new_guarded(s->ctx, block_bh, ++ s, ++ &s->mem_reentrancy_guard); ++ ++ DBG(TLOG_INFO, "qcow2_open: ctx %p bh %p\n", s->ctx, s->bh); ++ ++ DBG(TLOG_INFO, "qcow2_open: done (sz:%"PRIu64", sct:%lu, inf:%u)\n", ++ driver->info.size, driver->info.sector_size, driver->info.info); ++ ++ pthread_mutex_lock(&s->lock); ++ s->open_status = 0; ++ pthread_cond_signal(&s->cond); ++ ++ while (s->driver_opened) { ++ pthread_mutex_unlock(&s->lock); ++ main_loop_wait(false); ++ pthread_mutex_lock(&s->lock); ++ } ++ pthread_mutex_unlock(&s->lock); ++ ++ blk_set_aio_context(conf->blk, qemu_get_aio_context(), &error_abort); ++ ++ blk_drain_all(); ++ ++ qemu_bh_delete(s->bh); ++ ++ s->vreq_free_count = QCOW2_REQS; ++ for (i = 0; i < QCOW2_REQS; i++) { ++ qemu_iovec_destroy(&s->vreq_list[i].qiov); ++ } ++ ++ blk_unref(conf->blk); ++ ++ drain_call_rcu(); ++ ++ qcow2_free(s); ++ ++ return NULL; ++ fail: ++ blk_unref(conf->blk); ++ fail1: ++ EPRINTF("open error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ ++ pthread_mutex_lock(&s->lock); ++ s->open_status = -EINVAL; ++ pthread_cond_signal(&s->cond); ++ pthread_mutex_unlock(&s->lock); ++ ++ qcow2_free(s); ++ return NULL; ++} ++ ++static int ++_qcow2_open(td_driver_t *driver, const char *name, ++ struct td_vbd_encryption *encryption, td_flag_t flags) ++{ ++ int err; ++ struct qcow2_state *s; ++ ++ /* pre-allocate for all but NFS and LVM storage */ ++ driver->storage = tapdisk_storage_type(name); ++ ++ DPRINTF("%s: name %s, flags %x\n", __func__, name, flags); ++ ++ s = (struct qcow2_state *)driver->data; ++ memset(s, 0, sizeof(struct qcow2_state)); ++ ++ s->driver = driver; ++ s->name = name; ++ s->encryption = encryption; ++ s->flags = flags; ++ ++ err = pthread_cond_init(&s->cond, NULL); ++ if (err) { ++ EPRINTF("failed to init thread condition %d\n", err); ++ return err; ++ } ++ pthread_mutex_init(&s->lock, NULL); ++ ++ pthread_mutex_lock(&s->lock); ++ s->driver_opened = true; ++ qemu_thread_create(&s->thread, "td-qcow2", qcow2_open, s, ++ QEMU_THREAD_JOINABLE); ++ ++ pthread_cond_wait(&s->cond, &s->lock); ++ err = s->open_status; ++ s->open_status = 0; ++ pthread_mutex_unlock(&s->lock); ++ ++ return err; ++} ++ ++static int ++_qcow2_close(td_driver_t *driver) ++{ ++ int err; ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ ++ DBG(TLOG_WARN, "qcow2_close\n"); ++ ++ pthread_mutex_lock(&s->lock); ++ s->driver_opened = false; ++ pthread_mutex_unlock(&s->lock); ++ ++ qemu_bh_schedule(s->bh); ++ ++ // Ignore return, qcow2_open() always return NULL; or will abort ++ qemu_thread_join(&s->thread); ++ ++ err = pthread_cond_destroy(&s->cond); ++ if (err) { ++ EPRINTF("failed to destroy thread condition %d\n", err); ++ return err; ++ } ++ err = pthread_mutex_destroy(&s->lock); ++ if (err) { ++ EPRINTF("failed to destroy mutex %d\n", err); ++ return err; ++ } ++ ++ memset(s, 0, sizeof(struct qcow2_state)); ++ ++ return 0; ++} ++ ++int ++qcow2_validate_parent(td_driver_t *child_driver, ++ td_driver_t *parent_driver, td_flag_t flags) ++{ ++ DPRINTF("qcow2_validate_parent. ptype %d, ctype %d", ++ parent_driver->type, child_driver->type); ++ if (parent_driver->type != DISK_TYPE_QCOW) ++ { ++ if (child_driver->type != DISK_TYPE_QCOW) ++ return -EINVAL; ++ return 0; ++ } ++ ++ ++ /* TODO: compare sizes */ ++ ++ return 0; ++} ++ ++int ++qcow2_get_parent_id(td_driver_t *driver, td_disk_id_t *id) ++{ ++ DBG(TLOG_DBG, "\n"); ++ memset(id, 0, sizeof(td_disk_id_t)); ++ ++ return TD_NO_PARENT; ++} ++ ++static inline void ++init_qcow2_request(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ req->state = s; ++#if DEBUGGING != 0 ++ req->id = req - s->vreq_list; ++ gettimeofday(&req->allocate_tv, NULL); ++#endif ++} ++ ++static inline struct qcow2_request * ++alloc_qcow2_request(struct qcow2_state *s) ++{ ++ struct qcow2_request *req = NULL; ++ ++ pthread_mutex_lock(&s->lock); ++ if (s->vreq_free_count > 0) { ++ req = s->vreq_free[--s->vreq_free_count]; ++ pthread_mutex_unlock(&s->lock); ++ ASSERT(req->treq.secs == 0); ++ init_qcow2_request(s, req); ++ return req; ++ } ++ ++ pthread_mutex_unlock(&s->lock); ++ return NULL; ++} ++ ++static inline void ++free_qcow2_request(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ memset(&req->treq, 0, sizeof(req->treq)); ++ pthread_mutex_lock(&s->lock); ++ s->vreq_free[s->vreq_free_count++] = req; ++ pthread_mutex_unlock(&s->lock); ++ ++ qemu_iovec_reset(&req->qiov); ++} ++ ++static inline void ++signal_completion(struct qcow2_request *r) ++{ ++ struct qcow2_state *s = r->state; ++ td_vbd_t *vbd = r->treq.vreq->vbd; ++ ++ td_complete_request(r->treq, r->error); ++ DBG(TLOG_DBG, "lsec: 0x%08"PRIx64", blk: 0x%04x, " ++ "err: %d\n", r->treq.sec, r->treq.secs, r->error); ++ if (r->error == 0) { ++ tapdisk_vbd_kick(vbd); ++ s->kick++; ++ } ++ free_qcow2_request(s, r); ++ ++ s->returned++; ++ TRACE(s); ++} ++ ++#if DEBUGGING != 0 ++static void sum_stat(struct io_stat *dst, struct io_stat *src, bool first) ++{ ++ double mean, S; ++ ++ dst->min_val = MIN(dst->min_val, src->min_val); ++ dst->max_val = MAX(dst->max_val, src->max_val); ++ ++ if (first) { ++ mean = src->mean; ++ S = src->S; ++ } else { ++ double delta = src->mean - dst->mean; ++ ++ mean = ((src->mean * src->samples) + ++ (dst->mean * dst->samples)) / ++ (dst->samples + src->samples); ++ ++ S = src->S + dst->S + pow(delta, 2.0) * ++ (dst->samples * src->samples) / ++ (dst->samples + src->samples); ++ } ++ ++ dst->samples += src->samples; ++ dst->mean = mean; ++ dst->S = S; ++} ++ ++bool calc_lat(struct io_stat *is, uint64_t *min, ++ uint64_t *max, double *mean, double *dev) ++{ ++ double n = (double) is->samples; ++ ++ if (n == 0) ++ return false; ++ ++ *min = is->min_val; ++ *max = is->max_val; ++ *mean = is->mean; ++ ++ if (n > 1.0) ++ *dev = sqrt(is->S / (n - 1.0)); ++ else ++ *dev = 0; ++ ++ return true; ++} ++ ++#define update_latency(dst, latency) \ ++{ \ ++ struct io_stat ios; \ ++ ios.samples = 1; \ ++ ios.S = 0; \ ++ ios.min_val = ios.max_val = ios.mean = (latency); \ ++ sum_stat(&(dst), &ios, dst.samples == 0); \ ++} ++ ++#define print_latency(s, name, lat) \ ++{ \ ++ if (calc_lat(&(s)->name ## _ ## lat, &min, &max, &mean, &dev)) { \ ++ DBG(TLOG_WARN, #name ": " #lat " min %luus, max %luus, mean %fus, S %f, N %lu\n", \ ++ min, max, mean, dev, (s)->name ## _ ## lat.samples); \ ++ } else { \ ++ DBG(TLOG_WARN, #name ": " #lat " min 0us, max 0us, mean 0us, S 0.0, N 0\n"); \ ++ } \ ++} ++ ++#define print_latencies(s) \ ++{ \ ++ uint64_t min, max; \ ++ double mean, dev; \ ++ print_latency((s), read, slat); \ ++ print_latency((s), read, clat); \ ++ print_latency((s), read, lat); \ ++ print_latency((s), write, slat); \ ++ print_latency((s), write, clat); \ ++ print_latency((s), write, lat); \ ++} ++ ++#endif ++ ++static void qcow2_complete(void *opaque, int ret) ++{ ++ struct qcow2_request *req = (struct qcow2_request *)opaque; ++ struct qcow2_state *s = req->state; ++#if DEBUGGING != 0 ++ unsigned long long latency, submit_latency, complete_latency; ++ struct timeval now; ++#endif ++ ++ if (!s) { ++ DBG(TLOG_DBG, "s is null\n"); ++ return; ++ } ++ ++ req->error = ret; ++ ++ if (req->error) ++ ERR(s, req->error, "%s: op: %u, lsec: 0x%08"PRIx64", secs: 0x%04x", ++ req->treq.image->name, req->op, req->treq.sec, req->treq.secs); ++ ++ DBG(TLOG_DBG, "%d: inflight %d\n", req->id, req->aio_inflight); ++ ++#if DEBUGGING != 0 ++ gettimeofday(&now, NULL); ++ latency = timeval_to_us(&now) - timeval_to_us(&req->allocate_tv); ++ submit_latency = timeval_to_us(&req->submit_tv) - timeval_to_us(&req->allocate_tv); ++ complete_latency = timeval_to_us(&now) - timeval_to_us(&req->submit_tv); ++#endif ++ ++ s->completed++; ++ TRACE(s); ++ ++ switch (req->op) { ++ case QCOW2_OP_READ: ++ signal_completion(req); ++#if DEBUGGING != 0 ++ update_latency(s->read_lat, latency); ++ update_latency(s->read_slat, submit_latency); ++ update_latency(s->read_clat, complete_latency); ++#endif ++ break; ++ ++ case QCOW2_OP_WRITE: ++ DBG(TLOG_DBG, "%s: op: %u, lsec: 0x%08"PRIx64", secs: 0x%04x", ++ req->treq.image->name, req->op, req->treq.sec, req->treq.secs); ++ signal_completion(req); ++#if DEBUGGING != 0 ++ update_latency(s->write_lat, latency); ++ update_latency(s->write_slat, submit_latency); ++ update_latency(s->write_clat, complete_latency); ++#endif ++ break; ++ ++ default: ++ ASSERT(0); ++ break; ++ } ++} ++ ++static inline void ++do_aio_read(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ BlockBackend *blk = s->conf.blk; ++ ++ qemu_iovec_add(&req->qiov, req->treq.buf, req->treq.secs << s->blk_shift); ++ ++ req->aio_inflight++; ++#if DEBUGGING != 0 ++ gettimeofday(&req->submit_tv, NULL); ++#endif ++ req->aiocb = blk_aio_preadv(blk, req->treq.sec << s->blk_shift, &req->qiov, 0, qcow2_complete, req); ++ ++ s->queued++; ++ s->reads++; ++ s->read_size += req->treq.secs; ++ TRACE(s); ++} ++ ++static inline void ++do_aio_write(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ BlockBackend *blk = s->conf.blk; ++ ++ qemu_iovec_add(&req->qiov, req->treq.buf, req->treq.secs << s->blk_shift); ++ ++ req->aio_inflight++; ++#if DEBUGGING != 0 ++ gettimeofday(&req->submit_tv, NULL); ++#endif ++ req->aiocb = blk_aio_pwritev(blk, req->treq.sec << s->blk_shift, &req->qiov, 0, qcow2_complete, req); ++ ++ s->queued++; ++ s->writes++; ++ s->write_size += req->treq.secs; ++ TRACE(s); ++} ++ ++static int ++schedule_request(struct qcow2_state *s, td_request_t *treq, enum qcow2_ops op) ++{ ++ struct qcow2_request *req = NULL; ++ ++ req = alloc_qcow2_request(s); ++ if (!req) ++ return -EBUSY; ++ ++ req->treq = *treq; ++ req->op = op; ++ ++ pthread_mutex_lock(&s->lock); ++ QSIMPLEQ_INSERT_TAIL(&s->inflight, req, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ qemu_bh_schedule(s->bh); ++ s->schedule++; ++ ++ DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", " ++ "nr_secs: 0x%08x, buf: %p, id %d\n", ++ treq->image->name, treq->sec, treq->secs, ++ treq->buf, req->id); ++ ++ return 0; ++} ++ ++static void ++qcow2_queue_block_status(td_driver_t *driver, td_request_t treq) ++{ ++ treq.status = TD_BLOCK_STATE_NONE; ++} ++ ++static void ++qcow2_queue_read(td_driver_t *driver, td_request_t treq) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ int err; ++ ++ DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", secs: 0x%04x (seg: %d)\n", ++ treq.image->name, treq.sec, treq.secs, treq.sidx); ++ ++ err = schedule_request(s, &treq, QCOW2_OP_READ); ++ if (err) ++ goto fail; ++ ++ return; ++fail: ++ DBG(TLOG_DBG, "request failed\n"); ++ td_complete_request(treq, err); ++} ++ ++static void ++qcow2_queue_write(td_driver_t *driver, td_request_t treq) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ int err; ++ ++ DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", secs: 0x%04x, (seg: %d)\n", ++ treq.image->name, treq.sec, treq.secs, treq.sidx); ++ ++ err = schedule_request(s, &treq, QCOW2_OP_WRITE); ++ if (err) ++ goto fail; ++ ++ return; ++fail: ++ DBG(TLOG_DBG, "request failed\n"); ++ td_complete_request(treq, err); ++} ++ ++void ++qcow2_debug(td_driver_t *driver) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ ++ DBG(TLOG_WARN, "Qcow2: %s: queued %lu, completed %lu, returned %lu, " ++ "reads %lu, read sz avg %f, " ++ "writes %lu, write sz avg %f, schedule %lu, kick %lu\n", ++ blk_name(s->conf.blk), ++ s->queued, s->completed, s->returned, ++ s->reads, (s->reads ? ((float)s->read_size / s->reads) : 0.0), ++ s->writes, (s->writes ? ((float)s->write_size / s->writes) : 0.0), ++ s->schedule, s->kick); ++ ++#if DEBUGGING != 0 ++ print_latencies(s); ++#endif ++} ++ ++struct tap_disk tapdisk_qcow = { ++ .disk_type = "tapdisk_qcow2", ++ .flags = 0, ++ .private_data_size = sizeof(struct qcow2_state), ++ .td_open = _qcow2_open, ++ .td_close = _qcow2_close, ++ .td_queue_read = qcow2_queue_read, ++ .td_queue_block_status = qcow2_queue_block_status, ++ .td_queue_write = qcow2_queue_write, ++ .td_get_parent_id = qcow2_get_parent_id, ++ .td_validate_parent = qcow2_validate_parent, ++ .td_debug = qcow2_debug, ++}; +diff --git a/drivers/tapdisk-disktype.c b/drivers/tapdisk-disktype.c +index 4a60c756..40bfcce5 100644 +--- a/drivers/tapdisk-disktype.c ++++ b/drivers/tapdisk-disktype.c +@@ -77,8 +77,8 @@ static const disk_info_t ram_disk = { + }; + + static const disk_info_t qcow_disk = { +- "qcow", +- "qcow disk (qcow)", ++ "qcow2", ++ "QEMU copy-on-write disk (qcow2)", + 0, + }; + +@@ -164,9 +164,7 @@ extern struct tap_disk tapdisk_vhdsync; + #endif + extern struct tap_disk tapdisk_vhd; + extern struct tap_disk tapdisk_ram; +-#if 0 + extern struct tap_disk tapdisk_qcow; +-#endif + extern struct tap_disk tapdisk_block_cache; + extern struct tap_disk tapdisk_vhd_index; + extern struct tap_disk tapdisk_log; +@@ -185,9 +183,7 @@ const struct tap_disk *tapdisk_disk_drivers[] = { + #endif + [DISK_TYPE_VHD] = &tapdisk_vhd, + [DISK_TYPE_RAM] = &tapdisk_ram, +-#if 0 + [DISK_TYPE_QCOW] = &tapdisk_qcow, +-#endif + [DISK_TYPE_BLOCK_CACHE] = &tapdisk_block_cache, + [DISK_TYPE_VINDEX] = &tapdisk_vhd_index, + [DISK_TYPE_LOG] = &tapdisk_log, diff --git a/SOURCES/0038-vbd-wake-up-scheduler-to-force-check-ring.patch b/SOURCES/0038-vbd-wake-up-scheduler-to-force-check-ring.patch new file mode 100644 index 0000000..23e2dda --- /dev/null +++ b/SOURCES/0038-vbd-wake-up-scheduler-to-force-check-ring.patch @@ -0,0 +1,227 @@ +From 260a69aec99546b332596c3fce53301eba95284c Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 8 Oct 2025 14:05:35 +0200 +Subject: [PATCH] vbd: wake-up scheduler to force check ring + +In the case of a write barrier, the ring is only explore until the +barrier lefting pending requests hereafter. The completion of a barrier +will trigger a ring check but if the select is already sleeping, because +libqcow2 completion is in another thread, it will not wake it up so it +will have to wait the next wake up or timeout. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 4 +-- + drivers/tapdisk-server.c | 2 +- + drivers/tapdisk-vbd.c | 68 +++++++++++++++++++++++++++++++++++++++- + drivers/tapdisk-vbd.h | 6 +++- + drivers/tapdisk.h | 2 ++ + 5 files changed, 77 insertions(+), 5 deletions(-) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index 6fc3cc6a..b8b74a67 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -590,7 +590,7 @@ signal_completion(struct qcow2_request *r) + DBG(TLOG_DBG, "lsec: 0x%08"PRIx64", blk: 0x%04x, " + "err: %d\n", r->treq.sec, r->treq.secs, r->error); + if (r->error == 0) { +- tapdisk_vbd_kick(vbd); ++ tapdisk_vbd_kick(vbd, true); + s->kick++; + } + free_qcow2_request(s, r); +@@ -869,7 +869,7 @@ qcow2_debug(td_driver_t *driver) + + struct tap_disk tapdisk_qcow = { + .disk_type = "tapdisk_qcow2", +- .flags = 0, ++ .flags = TD_DRIVER_THREADED, + .private_data_size = sizeof(struct qcow2_state), + .td_open = _qcow2_open, + .td_close = _qcow2_close, +diff --git a/drivers/tapdisk-server.c b/drivers/tapdisk-server.c +index bf426735..812ecd90 100644 +--- a/drivers/tapdisk-server.c ++++ b/drivers/tapdisk-server.c +@@ -283,7 +283,7 @@ tapdisk_server_kick_responses(void) + td_vbd_t *vbd, *tmp; + + tapdisk_server_for_each_vbd(vbd, tmp) +- tapdisk_vbd_kick(vbd); ++ tapdisk_vbd_kick(vbd, false); + } + + static void +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 32131f31..02f0dab0 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -39,10 +39,12 @@ + #include + #include + #include ++#include + #include + #include + #include + #include ++#include + + #include "debug.h" + #include "libvhd.h" +@@ -59,6 +61,7 @@ + #include "tapdisk-nbdserver.h" + #include "td-stats.h" + #include "tapdisk-utils.h" ++#include "timeout-math.h" + + #define DBG(_level, _f, _a...) tlog_write(_level, _f, ##_a) + #define ERR(_err, _f, _a...) tlog_error(_err, _f, ##_a) +@@ -283,6 +286,13 @@ tapdisk_vbd_close_vdi(td_vbd_t *vbd) + + pthread_mutex_lock(&vbd->mutex); + td_flag_set(vbd->state, TD_VBD_CLOSED); ++ ++ if (td_flag_test(vbd->driver_flags, TD_DRIVER_THREADED)) { ++ tapdisk_server_unregister_event(vbd->event); ++ vbd->event = -1; ++ close(vbd->efd); ++ vbd->efd = -1; ++ } + pthread_mutex_unlock(&vbd->mutex); + } + +@@ -576,6 +586,23 @@ fail: + return err; + } + ++void ++tapdisk_vbd_event_cb(event_id_t id __attribute__((unused)), ++ char mode __attribute__((unused)), void *private) ++{ ++ td_vbd_t *vbd = private; ++ uint64_t u; ++ ssize_t s; ++ ++ pthread_mutex_lock(&vbd->mutex); ++ if (vbd->efd < 0) ++ return; ++ ++ s = read(vbd->efd, &u, sizeof(uint64_t)); ++ ASSERT(s == sizeof(uint64_t)); ++ pthread_mutex_unlock(&vbd->mutex); ++} ++ + int + tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_devnum) + { +@@ -604,6 +631,26 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d + if (err) + goto fail; + ++ if (td_flag_test(tapdisk_vbd_first_image(vbd)->driver->ops->flags, TD_DRIVER_THREADED)) { ++ vbd->driver_flags = tapdisk_vbd_first_image(vbd)->driver->ops->flags; ++ ++ vbd->efd = eventfd(0, 0); ++ if (vbd->efd == -1) { ++ err = errno; ++ ERROR("Failed to create eventfd: %s\n", strerror(-err)); ++ goto fail; ++ } ++ ++ vbd->event = tapdisk_server_register_event( ++ SCHEDULER_POLL_READ_FD, vbd->efd, TV_INF, ++ tapdisk_vbd_event_cb, vbd); ++ if (unlikely(vbd->event < 0)) { ++ err = vbd->event; ++ ERROR("Failed to register eventfd: %s\n", strerror(-err)); ++ goto fail; ++ } ++ } ++ + td_flag_clear(vbd->state, TD_VBD_CLOSED); + vbd->flags = flags; + +@@ -660,6 +707,14 @@ fail: + free(vbd->name); + vbd->name = tmp; + } ++ if (vbd->event > 0) { ++ tapdisk_server_unregister_event(vbd->event); ++ vbd->event = -1; ++ } ++ if (vbd->efd > 0) { ++ close(vbd->efd); ++ vbd->efd = -1; ++ } + + if (!list_empty(&vbd->images)) + tapdisk_image_close_chain(&vbd->images); +@@ -1862,10 +1917,11 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) + } + + void +-tapdisk_vbd_kick(td_vbd_t *vbd) ++tapdisk_vbd_kick(td_vbd_t *vbd, bool scheduler_kick) + { + const struct list_head *list; + td_vbd_request_t *vreq, *prev, *next; ++ ssize_t s; + + vbd->kicked++; + +@@ -1900,6 +1956,16 @@ tapdisk_vbd_kick(td_vbd_t *vbd) + prev->cb(prev, prev->error, prev->token, 1); + vbd->returned++; + } ++ ++ if (scheduler_kick && td_flag_test(vbd->driver_flags, TD_DRIVER_THREADED)) { ++ static uint64_t token = 1; ++ ++ if (vbd->efd < 0) ++ return; ++ ++ s = write(vbd->efd, &token, sizeof(uint64_t)); ++ ASSERT(s == sizeof(uint64_t)); ++ } + pthread_mutex_unlock(&vbd->mutex); + } + +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index f3fa0398..42239094 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -167,6 +167,10 @@ struct td_vbd_handle { + struct td_vbd_encryption encryption; + + bool watchdog_warned; ++ ++ td_flag_t driver_flags; ++ int efd; ++ event_id_t event; + }; + + #define tapdisk_vbd_for_each_request(vreq, tmp, list) \ +@@ -223,7 +227,7 @@ int tapdisk_vbd_kill_queue(td_vbd_t *); + int tapdisk_vbd_pause(td_vbd_t *); + void tapdisk_vbd_squash_pause_logging(bool squash); + int tapdisk_vbd_resume(td_vbd_t *, const char *); +-void tapdisk_vbd_kick(td_vbd_t *); ++void tapdisk_vbd_kick(td_vbd_t *, bool); + void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); + +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index 3163e190..59e9ee64 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -115,6 +115,8 @@ enum TD_OPS{ + #define TD_CREATE_SPARSE 0x00001 + #define TD_CREATE_MULTITYPE 0x00002 + ++#define TD_DRIVER_THREADED 0x00001 ++ + #define td_flag_set(word, flag) ((word) |= (flag)) + #define td_flag_clear(word, flag) ((word) &= ~(flag)) + #define td_flag_test(word, flag) ((word) & (flag)) diff --git a/SOURCES/0039-blktap.spec-add-qcow2-dependencies.patch b/SOURCES/0039-blktap.spec-add-qcow2-dependencies.patch new file mode 100644 index 0000000..8623898 --- /dev/null +++ b/SOURCES/0039-blktap.spec-add-qcow2-dependencies.patch @@ -0,0 +1,22 @@ +From 6f4a8aec3fe5b10a7446a529586a6ba5d921f7b0 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 16 Jan 2025 14:09:54 +0100 +Subject: [PATCH] blktap.spec: add qcow2 dependencies + +Signed-off-by: Anthoine Bourgeois +--- + mk/blktap.spec.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/mk/blktap.spec.in b/mk/blktap.spec.in +index 2da6fe2c..14fea3c0 100644 +--- a/mk/blktap.spec.in ++++ b/mk/blktap.spec.in +@@ -11,6 +11,7 @@ BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot + Obsoletes: xen-blktap + BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autogen, autoconf, automake, libtool, libuuid-devel + BuildRequires: xen-devel, kernel-headers, xen-dom0-libs-devel, zlib-devel, xen-libs-devel, libcmocka-devel, lcov, git ++BuildRequires: glib2, glib2-devel, gnutls, gnutls-devel, libzstd, libzstd-devel + BuildRequires: openssl-devel >= 1.1.1 + Requires(post): systemd + Requires(preun): systemd diff --git a/SOURCES/0040-tapdisk-support-new-commit-command.patch b/SOURCES/0040-tapdisk-support-new-commit-command.patch new file mode 100644 index 0000000..c4f8c9a --- /dev/null +++ b/SOURCES/0040-tapdisk-support-new-commit-command.patch @@ -0,0 +1,378 @@ +From 00e7a92af1a19b9edb80a4e23f63fda677e47c70 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 10 Feb 2025 16:45:05 +0100 +Subject: [PATCH] tapdisk: support new commit command + +This command will coalesce the disk in parameter with its parent. +tap-ctl commit -m 0 -p 12345 -a /path/to/disk.qcow2 + +Signed-off-by: Anthoine Bourgeois +--- + control/Makefile.am | 1 + + control/tap-ctl-commit.c | 76 +++++++++++++++++++++++++++++++++++++ + control/tap-ctl.c | 47 +++++++++++++++++++++++ + drivers/tapdisk-control.c | 36 ++++++++++++++++++ + drivers/tapdisk-interface.c | 25 ++++++++++++ + drivers/tapdisk-interface.h | 1 + + drivers/tapdisk-vbd.c | 16 ++++++++ + drivers/tapdisk-vbd.h | 1 + + drivers/tapdisk.h | 1 + + include/tap-ctl.h | 5 +++ + include/tapdisk-message.h | 8 ++++ + 11 files changed, 217 insertions(+) + create mode 100644 control/tap-ctl-commit.c + +diff --git a/control/Makefile.am b/control/Makefile.am +index 6ca88ab7..5f954c92 100644 +--- a/control/Makefile.am ++++ b/control/Makefile.am +@@ -33,6 +33,7 @@ libblktapctl_la_SOURCES += tap-ctl-check.c + libblktapctl_la_SOURCES += tap-ctl-stats.c + libblktapctl_la_SOURCES += tap-ctl-xen.c + libblktapctl_la_SOURCES += tap-ctl-info.c ++libblktapctl_la_SOURCES += tap-ctl-commit.c + + libblktapctl_la_LDFLAGS = -version-info 1:1:1 + +diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c +new file mode 100644 +index 00000000..e5d8c1f5 +--- /dev/null ++++ b/control/tap-ctl-commit.c +@@ -0,0 +1,76 @@ ++/* ++ * Copyright (c) 2016, Citrix Systems, Inc. ++ * ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of its ++ * contributors may be used to endorse or promote products derived from ++ * this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "tap-ctl.h" ++#include "util.h" ++ ++int ++tap_ctl_commit(const int id, const int minor, const char *params) ++{ ++ int err; ++ tapdisk_message_t message; ++ ++ memset(&message, 0, sizeof(message)); ++ message.type = TAPDISK_MESSAGE_COMMIT; ++ message.cookie = minor; ++ ++ if (params) ++ safe_strncpy(message.u.params.path, params, ++ sizeof(message.u.params.path)); ++ ++ err = tap_ctl_connect_send_and_receive(id, &message, NULL); ++ if (err) ++ return err; ++ ++ if (message.type == TAPDISK_MESSAGE_COMMIT_RSP ++ || message.type == TAPDISK_MESSAGE_ERROR) ++ err = -message.u.response.error; ++ else { ++ EPRINTF("got unexpected result '%s' from %d\n", ++ tapdisk_message_name(message.type), id); ++ err = -EINVAL; ++ } ++ ++ if (err) ++ EPRINTF("commit failed: %s\n", strerror(-err)); ++ ++ return err; ++} +diff --git a/control/tap-ctl.c b/control/tap-ctl.c +index 64d96d0d..25d2248d 100644 +--- a/control/tap-ctl.c ++++ b/control/tap-ctl.c +@@ -943,6 +943,52 @@ usage: + return EINVAL; + } + ++static void ++tap_cli_commit_usage(FILE *stream) ++{ ++ fprintf(stream, "usage: commit <-p pid> <-m minor> <-a /path/to/file>\n"); ++} ++ ++int ++tap_cli_commit(int argc, char **argv) ++{ ++ const char *args; ++ int c, pid, minor; ++ ++ pid = -1; ++ minor = -1; ++ args = NULL; ++ ++ optind = 0; ++ while ((c = getopt(argc, argv, "p:m:a:h")) != -1) { ++ switch (c) { ++ case 'p': ++ pid = atoi(optarg); ++ break; ++ case 'm': ++ minor = atoi(optarg); ++ break; ++ case 'a': ++ args = optarg; ++ break; ++ case '?': ++ goto usage; ++ case 'h': ++ tap_cli_commit_usage(stdout); ++ return 0; ++ } ++ } ++ ++ if (pid == -1 || minor == -1 || args == NULL) ++ goto usage; ++ ++ return tap_ctl_commit(pid, minor, args); ++ ++usage: ++ tap_cli_commit_usage(stderr); ++ return EINVAL; ++} ++ + struct command commands[] = { + { .name = "list", .func = tap_cli_list }, + { .name = "allocate", .func = tap_cli_allocate }, +@@ -959,6 +1005,7 @@ struct command commands[] = { + { .name = "stats", .func = tap_cli_stats }, + { .name = "major", .func = tap_cli_major }, + { .name = "check", .func = tap_cli_check }, ++ { .name = "commit", .func = tap_cli_commit }, + }; + + #define print_commands() \ +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 39e7ab0e..7466f02c 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1307,6 +1307,38 @@ out: + return err; + } + ++static int ++tapdisk_control_commit(struct tapdisk_ctl_conn *conn, ++ tapdisk_message_t *request, tapdisk_message_t * const response) ++{ ++ int err; ++ td_vbd_t *vbd; ++ const char *desc = NULL; ++ ++ ASSERT(conn); ++ ASSERT(request); ++ ASSERT(response); ++ ++ INFO("commit %d\n", request->cookie); ++ ++ vbd = tapdisk_server_get_vbd(request->cookie); ++ if (!vbd) { ++ /* TODO log error */ ++ err = -ENODEV; ++ goto out; ++ } ++ ++ if (request->u.params.path[0]) ++ desc = request->u.params.path; ++ ++ err = tapdisk_vbd_commit(vbd, desc); ++out: ++ response->cookie = request->cookie; ++ if (!err) ++ response->type = TAPDISK_MESSAGE_COMMIT_RSP; ++ return err; ++} ++ + + struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_PID] = { +@@ -1364,6 +1396,10 @@ struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_EXIT] = { + .handler = NULL, + .flags = 0 ++ }, ++ [TAPDISK_MESSAGE_COMMIT] = { ++ .handler = tapdisk_control_commit, ++ .flags = TAPDISK_MSG_VERBOSE, + } + }; + +diff --git a/drivers/tapdisk-interface.c b/drivers/tapdisk-interface.c +index 2cdd1286..5f562707 100644 +--- a/drivers/tapdisk-interface.c ++++ b/drivers/tapdisk-interface.c +@@ -270,6 +270,31 @@ fail: + td_complete_request(*treq, err); + } + ++int ++td_commit(td_image_t *image, const char *name) ++{ ++ td_driver_t *driver; ++ ++ if (!image) { ++ return -ENODEV; ++ } ++ ++ driver = image->driver; ++ if (!driver) { ++ return -ENODEV; ++ } ++ ++ if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) { ++ return -EBADF; ++ } ++ ++ if (!driver->ops->td_commit) { ++ return -EOPNOTSUPP; ++ } ++ ++ return driver->ops->td_commit(driver, name); ++} ++ + void + td_forward_request(td_request_t treq) + { +diff --git a/drivers/tapdisk-interface.h b/drivers/tapdisk-interface.h +index 437feb58..076528ea 100644 +--- a/drivers/tapdisk-interface.h ++++ b/drivers/tapdisk-interface.h +@@ -42,6 +42,7 @@ int td_load(td_image_t *); + int td_close(td_image_t *); + int td_get_parent_id(td_image_t *, td_disk_id_t *); + int td_validate_parent(td_image_t *, td_image_t *); ++int td_commit(td_image_t *, const char *); + + void td_queue_write(td_image_t *, td_request_t); + void td_queue_read(td_image_t *, td_request_t); +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 02f0dab0..3cb38bf3 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1132,6 +1132,22 @@ resume_failed: + return 0; + } + ++int ++tapdisk_vbd_commit(td_vbd_t *vbd, const char *name) ++{ ++ int err; ++ ++ if (log) { ++ INFO("commit %s\n", name); ++ } ++ ++ err = td_commit(tapdisk_vbd_first_image(vbd), name); ++ ++ INFO("commit started (%d)\n", err); ++ ++ return err; ++} ++ + static int + tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, + const struct timeval *now) +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index 42239094..aaffb073 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -230,6 +230,7 @@ int tapdisk_vbd_resume(td_vbd_t *, const char *); + void tapdisk_vbd_kick(td_vbd_t *, bool); + void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); ++int tapdisk_vbd_commit(td_vbd_t *, const char *); + + void tapdisk_vbd_complete_td_request(td_request_t, int); + int add_extent(tapdisk_extents_t *, td_request_t *); +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index 59e9ee64..f43b6402 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -228,6 +228,7 @@ struct tap_disk { + void (*td_queue_write) (td_driver_t *, td_request_t); + void (*td_debug) (td_driver_t *); + void (*td_stats) (td_driver_t *, td_stats_t *); ++ int (*td_commit) (td_driver_t *, const char *); + + /** + * Callback to produce RRD output. +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index f2db49fe..03bb0434 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -143,6 +143,11 @@ int tap_ctl_stats_fwrite(pid_t pid, int minor, FILE *out); + + int tap_ctl_blk_major(void); + ++/** ++ * Commit a VBD. ++ */ ++int tap_ctl_commit(const int id, const int minor, const char *params); ++ + /** + * Instructs a tapdisk to connect to the shared ring. + * +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index 7c54fb3b..13295123 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -235,6 +235,8 @@ enum tapdisk_message_id { + TAPDISK_MESSAGE_DISK_INFO, + TAPDISK_MESSAGE_DISK_INFO_RSP, + TAPDISK_MESSAGE_EXIT, ++ TAPDISK_MESSAGE_COMMIT, ++ TAPDISK_MESSAGE_COMMIT_RSP, + TAPDISK_MESSAGE_MAX /* This value must be the last. */ + }; + +@@ -329,6 +331,12 @@ tapdisk_message_name(enum tapdisk_message_id id) + case TAPDISK_MESSAGE_EXIT: + return "exit"; + ++ case TAPDISK_MESSAGE_COMMIT: ++ return "commit"; ++ ++ case TAPDISK_MESSAGE_COMMIT_RSP: ++ return "commit response"; ++ + default: + return "unknown"; + } diff --git a/SOURCES/0041-qcow2-support-commit-command.patch b/SOURCES/0041-qcow2-support-commit-command.patch new file mode 100644 index 0000000..cbac381 --- /dev/null +++ b/SOURCES/0041-qcow2-support-commit-command.patch @@ -0,0 +1,197 @@ +From a971cc350e12c45949c5aaaa6caed1cb54b556ef Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Mon, 10 Feb 2025 18:54:25 +0100 +Subject: [PATCH] qcow2: support commit command + +This command will coalesce the disk in parameter with its parent. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 112 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 111 insertions(+), 1 deletion(-) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index b8b74a67..bceaf863 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -60,6 +60,8 @@ + #include "qapi/error.h" + #include "sysemu/block-backend.h" + #include "qapi/qmp/qdict.h" ++#include "qapi/qapi-commands-block-core.h" ++ + #include "tapdisk.h" + #include "tapdisk-driver.h" + #include "tapdisk-interface.h" +@@ -95,6 +97,7 @@ + enum qcow2_ops { + QCOW2_OP_READ, + QCOW2_OP_WRITE, ++ QCOW2_OP_COMMIT, + }; + + struct qcow2_state; +@@ -105,7 +108,12 @@ struct qcow2_request; + struct qcow2_request { + int error; + enum qcow2_ops op; +- td_request_t treq; ++ union { ++ /* OP_READ, OP_WRITE */ ++ td_request_t treq; ++ /* OP_COMMIT */ ++ char * top; ++ }; + struct qcow2_state *state; + BlockAIOCB *aiocb; + QSIMPLEQ_ENTRY(qcow2_request) list; +@@ -155,6 +163,12 @@ struct qcow2_state { + int open_status; + MemReentrancyGuard mem_reentrancy_guard; + ++ /* commit/query synchronization */ ++#define COMMIT_JOB_ID "JIDCOMMIT0" ++ pthread_mutex_t commit_lock; ++ pthread_cond_t commit_cond; ++ JobInfo job_info; ++ + /* Stats */ + uint64_t queued; + uint64_t completed; +@@ -182,6 +196,7 @@ struct qcow2_state { + static void qcow2_complete(void *, int); + static inline void do_aio_read(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_aio_write(struct qcow2_state *s, struct qcow2_request *req); ++static inline void do_commit(struct qcow2_state *s, struct qcow2_request *req); + + static int + qcow2_initialize(struct qcow2_state *s, Error **perr) +@@ -236,6 +251,9 @@ static void qcow2_handle_requests(struct qcow2_state *s) + case QCOW2_OP_WRITE: + do_aio_write(s, req); + break; ++ case QCOW2_OP_COMMIT: ++ do_commit(s, req); ++ break; + } + pthread_mutex_lock(&s->lock); + } +@@ -466,6 +484,12 @@ _qcow2_open(td_driver_t *driver, const char *name, + return err; + } + pthread_mutex_init(&s->lock, NULL); ++ err = pthread_cond_init(&s->commit_cond, NULL); ++ if (err) { ++ EPRINTF("failed to init thread condition %d\n", err); ++ return err; ++ } ++ pthread_mutex_init(&s->commit_lock, NULL); + + pthread_mutex_lock(&s->lock); + s->driver_opened = true; +@@ -497,6 +521,17 @@ _qcow2_close(td_driver_t *driver) + // Ignore return, qcow2_open() always return NULL; or will abort + qemu_thread_join(&s->thread); + ++ err = pthread_cond_destroy(&s->commit_cond); ++ if (err) { ++ EPRINTF("failed to destroy thread condition %d\n", err); ++ return err; ++ } ++ err = pthread_mutex_destroy(&s->commit_lock); ++ if (err) { ++ EPRINTF("failed to destroy mutex %d\n", err); ++ return err; ++ } ++ + err = pthread_cond_destroy(&s->cond); + if (err) { + EPRINTF("failed to destroy thread condition %d\n", err); +@@ -848,6 +883,80 @@ fail: + td_complete_request(treq, err); + } + ++int ++qcow2_commit(td_driver_t *driver, const char *name) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ struct qcow2_request *req; ++ int err; ++ ++ if (name == NULL) ++ return -EINVAL; ++ ++ DBG(TLOG_WARN, "Qcow2: commit %s.\n", name); ++ ++ req = alloc_qcow2_request(s); ++ if (!req) ++ return -EBUSY; ++ ++ req->top = strdup(name); ++ req->op = QCOW2_OP_COMMIT; ++ ++ pthread_mutex_lock(&s->lock); ++ QSIMPLEQ_INSERT_TAIL(&s->inflight, req, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ pthread_mutex_lock(&s->commit_lock); ++ qemu_bh_schedule(s->bh); ++ ++ pthread_cond_wait(&s->commit_cond, &s->commit_lock); ++ err = req->error; ++ pthread_mutex_unlock(&s->commit_lock); ++ ++ free(req->top); ++ free_qcow2_request(s, req); ++ ++ return err; ++} ++ ++static inline void ++do_commit(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ Error *local_err = NULL; ++ char *node, *top_node, *base_node; ++ BlockDriverState *bs, *top_bs, *base_bs; ++ int err = 0; ++ ++ bs = blk_bs(s->conf.blk); ++ node = bs->node_name; ++ if (strcmp(bs->filename, req->top) == 0) { ++ top_bs = bs; ++ } else { ++ top_bs = bdrv_find_backing_image(bs, req->top); ++ } ++ top_node = top_bs->node_name; ++ base_bs = bdrv_backing_chain_next(top_bs); ++ base_node = base_bs->node_name; ++ ++ DBG(TLOG_DBG, "Qcow2: block commit %s (node-name: '%s').\n", req->top, node); ++ DBG(TLOG_DBG, "Qcow2: block commit: top-node: '%s' on '%s' base-node).\n", top_node, base_node); ++ ++ qmp_block_commit(COMMIT_JOB_ID, node, base_node, NULL, top_node, NULL, NULL, ++ false, false, false, 0, false, BLOCKDEV_ON_ERROR_REPORT, ++ NULL, false, false, true, false, &local_err); ++ ++ if (local_err) { ++ DPRINTF("qcow2_commit: error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ err = -EINVAL; ++ } ++ ++ pthread_mutex_lock(&s->commit_lock); ++ req->error = err; ++ pthread_cond_signal(&s->commit_cond); ++ pthread_mutex_unlock(&s->commit_lock); ++} ++ + void + qcow2_debug(td_driver_t *driver) + { +@@ -878,5 +987,6 @@ struct tap_disk tapdisk_qcow = { + .td_queue_write = qcow2_queue_write, + .td_get_parent_id = qcow2_get_parent_id, + .td_validate_parent = qcow2_validate_parent, ++ .td_commit = qcow2_commit, + .td_debug = qcow2_debug, + }; diff --git a/SOURCES/0042-tapdisk-support-new-query-command.patch b/SOURCES/0042-tapdisk-support-new-query-command.patch new file mode 100644 index 0000000..f0814b7 --- /dev/null +++ b/SOURCES/0042-tapdisk-support-new-query-command.patch @@ -0,0 +1,370 @@ +From addb2dbcf41aae1a9648c6547b56d7b50dbb555d Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 13 Feb 2025 08:54:48 +0100 +Subject: [PATCH] tapdisk: support new query command + +This command will print the status of the coalesce in background. +$ tap-ctl query -m 0 -p 12345 +Commit status 'running' (2817589248/4558422016) + +Signed-off-by: Anthoine Bourgeois +--- + control/tap-ctl-commit.c | 31 +++++++++++++++++++++++++++ + control/tap-ctl.c | 42 +++++++++++++++++++++++++++++++++++++ + drivers/tapdisk-control.c | 41 ++++++++++++++++++++++++++++++++++++ + drivers/tapdisk-interface.c | 25 ++++++++++++++++++++++ + drivers/tapdisk-interface.h | 1 + + drivers/tapdisk-vbd.c | 16 ++++++++++++++ + drivers/tapdisk-vbd.h | 1 + + drivers/tapdisk.h | 8 +++++++ + include/tap-ctl.h | 1 + + include/tapdisk-message.h | 18 +++++++++++++++- + 10 files changed, 183 insertions(+), 1 deletion(-) + +diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c +index e5d8c1f5..eef6ea5f 100644 +--- a/control/tap-ctl-commit.c ++++ b/control/tap-ctl-commit.c +@@ -74,3 +74,34 @@ tap_ctl_commit(const int id, const int minor, const char *params) + + return err; + } ++ ++int ++tap_ctl_query_commit_job(const int id, const int minor) ++{ ++ int err; ++ tapdisk_message_t message; ++ ++ memset(&message, 0, sizeof(message)); ++ message.type = TAPDISK_MESSAGE_QUERY_COMMIT_JOB; ++ message.cookie = minor; ++ ++ err = tap_ctl_connect_send_and_receive(id, &message, NULL); ++ if (err) ++ return err; ++ ++ if (message.type == TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP) { ++ printf("Commit status '%s' (%lu/%lu)\n", message.u.query.status, message.u.query.current_progress, message.u.query.total_progress); ++ err = 0; ++ } else if (message.type == TAPDISK_MESSAGE_ERROR) { ++ err = -message.u.response.error; ++ } else { ++ EPRINTF("got unexpected result '%s' from %d\n", ++ tapdisk_message_name(message.type), id); ++ err = -EINVAL; ++ } ++ ++ if (err) ++ EPRINTF("query commit job failed: %s\n", strerror(-err)); ++ ++ return err; ++} +diff --git a/control/tap-ctl.c b/control/tap-ctl.c +index 25d2248d..1c55248a 100644 +--- a/control/tap-ctl.c ++++ b/control/tap-ctl.c +@@ -989,6 +989,47 @@ usage: + return EINVAL; + } + ++static void ++tap_cli_query_commit_job_usage(FILE *stream) ++{ ++ fprintf(stream, "usage: query <-p pid> <-m minor>\n"); ++} ++ ++int ++tap_cli_query_commit_job(int argc, char **argv) ++{ ++ int c, pid, minor; ++ ++ pid = -1; ++ minor = -1; ++ ++ optind = 0; ++ while ((c = getopt(argc, argv, "p:m:h")) != -1) { ++ switch (c) { ++ case 'p': ++ pid = atoi(optarg); ++ break; ++ case 'm': ++ minor = atoi(optarg); ++ break; ++ case '?': ++ goto usage; ++ case 'h': ++ tap_cli_query_commit_job_usage(stdout); ++ return 0; ++ } ++ } ++ ++ if (pid == -1 || minor == -1) ++ goto usage; ++ ++ return tap_ctl_query_commit_job(pid, minor); ++ ++usage: ++ tap_cli_query_commit_job_usage(stderr); ++ return EINVAL; ++} ++ + struct command commands[] = { + { .name = "list", .func = tap_cli_list }, + { .name = "allocate", .func = tap_cli_allocate }, +@@ -1006,6 +1047,7 @@ struct command commands[] = { + { .name = "major", .func = tap_cli_major }, + { .name = "check", .func = tap_cli_check }, + { .name = "commit", .func = tap_cli_commit }, ++ { .name = "query", .func = tap_cli_query_commit_job }, + }; + + #define print_commands() \ +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 7466f02c..ac21f4dc 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1339,6 +1339,43 @@ out: + return err; + } + ++static int ++tapdisk_control_query_commit_job(struct tapdisk_ctl_conn *conn, ++ tapdisk_message_t *request, tapdisk_message_t * const response) ++{ ++ int err; ++ td_vbd_t *vbd; ++ td_query_t query; ++ ++ ASSERT(conn); ++ ASSERT(request); ++ ASSERT(response); ++ ++ INFO("query commit job %d\n", request->cookie); ++ ++ vbd = tapdisk_server_get_vbd(request->cookie); ++ if (!vbd) { ++ /* TODO log error */ ++ err = -ENODEV; ++ goto out; ++ } ++ ++ err = tapdisk_vbd_query_commit_job(vbd, &query); ++out: ++ response->cookie = request->cookie; ++ ++ if (!err) { ++ response->type = TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP; ++ ++ if (query.status) ++ safe_strncpy(response->u.query.status, query.status, ++ sizeof(response->u.query.status)); ++ response->u.query.current_progress = query.current_progress; ++ response->u.query.total_progress = query.total_progress; ++ } ++ return err; ++} ++ + + struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_PID] = { +@@ -1400,6 +1437,10 @@ struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_COMMIT] = { + .handler = tapdisk_control_commit, + .flags = TAPDISK_MSG_VERBOSE, ++ }, ++ [TAPDISK_MESSAGE_QUERY_COMMIT_JOB] = { ++ .handler = tapdisk_control_query_commit_job, ++ .flags = TAPDISK_MSG_VERBOSE, + } + }; + +diff --git a/drivers/tapdisk-interface.c b/drivers/tapdisk-interface.c +index 5f562707..428ee98f 100644 +--- a/drivers/tapdisk-interface.c ++++ b/drivers/tapdisk-interface.c +@@ -295,6 +295,31 @@ td_commit(td_image_t *image, const char *name) + return driver->ops->td_commit(driver, name); + } + ++int ++td_query_commit_job(td_image_t *image, td_query_t *query) ++{ ++ td_driver_t *driver; ++ ++ if (!image) { ++ return -ENODEV; ++ } ++ ++ driver = image->driver; ++ if (!driver) { ++ return -ENODEV; ++ } ++ ++ if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) { ++ return -EBADF; ++ } ++ ++ if (!driver->ops->td_query_commit_job) { ++ return -EOPNOTSUPP; ++ } ++ ++ return driver->ops->td_query_commit_job(driver, query); ++} ++ + void + td_forward_request(td_request_t treq) + { +diff --git a/drivers/tapdisk-interface.h b/drivers/tapdisk-interface.h +index 076528ea..b3ff6e13 100644 +--- a/drivers/tapdisk-interface.h ++++ b/drivers/tapdisk-interface.h +@@ -43,6 +43,7 @@ int td_close(td_image_t *); + int td_get_parent_id(td_image_t *, td_disk_id_t *); + int td_validate_parent(td_image_t *, td_image_t *); + int td_commit(td_image_t *, const char *); ++int td_query_commit_job(td_image_t *, td_query_t *); + + void td_queue_write(td_image_t *, td_request_t); + void td_queue_read(td_image_t *, td_request_t); +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 3cb38bf3..7da25b6e 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1148,6 +1148,22 @@ tapdisk_vbd_commit(td_vbd_t *vbd, const char *name) + return err; + } + ++int ++tapdisk_vbd_query_commit_job(td_vbd_t *vbd, td_query_t *query) ++{ ++ int err; ++ ++ if (log) { ++ INFO("query commit job.\n"); ++ } ++ ++ err = td_query_commit_job(tapdisk_vbd_first_image(vbd), query); ++ ++ INFO("query commit job (%d)\n", err); ++ ++ return err; ++} ++ + static int + tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, + const struct timeval *now) +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index aaffb073..fb948255 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -231,6 +231,7 @@ void tapdisk_vbd_kick(td_vbd_t *, bool); + void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); + int tapdisk_vbd_commit(td_vbd_t *, const char *); ++int tapdisk_vbd_query_commit_job(td_vbd_t *, td_query_t *); + + void tapdisk_vbd_complete_td_request(td_request_t, int); + int add_extent(tapdisk_extents_t *, td_request_t *); +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index f43b6402..06891584 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -136,6 +136,7 @@ typedef struct td_image_handle td_image_t; + typedef struct td_sector_count td_sector_count_t; + typedef struct td_vbd_request td_vbd_request_t; + typedef struct td_vbd_handle td_vbd_t; ++typedef struct td_query td_query_t; + + /* + * Prototype of the callback to activate as requests complete. +@@ -211,6 +212,12 @@ struct td_vbd_encryption + uint8_t *encryption_key; + }; + ++struct td_query { ++ uint64_t current_progress; ++ uint64_t total_progress; ++ const char *status; ++}; ++ + /* + * Structure describing the interface to a virtual disk implementation. + * See note at the top of this file describing this interface. +@@ -229,6 +236,7 @@ struct tap_disk { + void (*td_debug) (td_driver_t *); + void (*td_stats) (td_driver_t *, td_stats_t *); + int (*td_commit) (td_driver_t *, const char *); ++ int (*td_query_commit_job) (td_driver_t *, td_query_t *); + + /** + * Callback to produce RRD output. +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index 03bb0434..8749b28c 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -147,6 +147,7 @@ int tap_ctl_blk_major(void); + * Commit a VBD. + */ + int tap_ctl_commit(const int id, const int minor, const char *params); ++int tap_ctl_query_commit_job(const int id, const int minor); + + /** + * Instructs a tapdisk to connect to the shared ring. +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index 13295123..7645553a 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -68,6 +68,7 @@ typedef struct tapdisk_message_response tapdisk_message_response_t; + typedef struct tapdisk_message_minors tapdisk_message_minors_t; + typedef struct tapdisk_message_list tapdisk_message_list_t; + typedef struct tapdisk_message_stat tapdisk_message_stat_t; ++typedef struct tapdisk_message_query tapdisk_message_query_t; + + struct tapdisk_message_params { + tapdisk_message_flag_t flags; +@@ -113,6 +114,12 @@ struct tapdisk_message_stat { + size_t length; + }; + ++struct tapdisk_message_query { ++ uint64_t current_progress; ++ uint64_t total_progress; ++ char status[TAPDISK_MESSAGE_STRING_LENGTH]; ++}; ++ + /** + * Tapdisk message containing all the necessary information required for the + * tapdisk to connect to a guest's blkfront. +@@ -200,7 +207,8 @@ struct tapdisk_message { + tapdisk_message_list_t list; + tapdisk_message_stat_t info; + tapdisk_message_blkif_t blkif; +- tapdisk_message_resume_t resume; ++ tapdisk_message_resume_t resume; ++ tapdisk_message_query_t query; + } u; + }; + +@@ -237,6 +245,8 @@ enum tapdisk_message_id { + TAPDISK_MESSAGE_EXIT, + TAPDISK_MESSAGE_COMMIT, + TAPDISK_MESSAGE_COMMIT_RSP, ++ TAPDISK_MESSAGE_QUERY_COMMIT_JOB, ++ TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP, + TAPDISK_MESSAGE_MAX /* This value must be the last. */ + }; + +@@ -337,6 +347,12 @@ tapdisk_message_name(enum tapdisk_message_id id) + case TAPDISK_MESSAGE_COMMIT_RSP: + return "commit response"; + ++ case TAPDISK_MESSAGE_QUERY_COMMIT_JOB: ++ return "query commit job"; ++ ++ case TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP: ++ return "query commit job response"; ++ + default: + return "unknown"; + } diff --git a/SOURCES/0043-qcow2-support-query-command.patch b/SOURCES/0043-qcow2-support-query-command.patch new file mode 100644 index 0000000..ce26566 --- /dev/null +++ b/SOURCES/0043-qcow2-support-query-command.patch @@ -0,0 +1,162 @@ +From 9d6d40ddb38904e8859844acb27b20b69d231c53 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Thu, 13 Feb 2025 08:56:22 +0100 +Subject: [PATCH] qcow2: support query command + +This command will print the status of the coalesce in background. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 105 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 105 insertions(+) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index bceaf863..37e7c324 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -61,6 +61,7 @@ + #include "sysemu/block-backend.h" + #include "qapi/qmp/qdict.h" + #include "qapi/qapi-commands-block-core.h" ++#include "qapi/qapi-commands-job.h" + + #include "tapdisk.h" + #include "tapdisk-driver.h" +@@ -98,6 +99,7 @@ enum qcow2_ops { + QCOW2_OP_READ, + QCOW2_OP_WRITE, + QCOW2_OP_COMMIT, ++ QCOW2_OP_QUERY, + }; + + struct qcow2_state; +@@ -197,6 +199,7 @@ static void qcow2_complete(void *, int); + static inline void do_aio_read(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_aio_write(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_commit(struct qcow2_state *s, struct qcow2_request *req); ++static inline void do_query_commit_job(struct qcow2_state *s, struct qcow2_request *req); + + static int + qcow2_initialize(struct qcow2_state *s, Error **perr) +@@ -254,6 +257,9 @@ static void qcow2_handle_requests(struct qcow2_state *s) + case QCOW2_OP_COMMIT: + do_commit(s, req); + break; ++ case QCOW2_OP_QUERY: ++ do_query_commit_job(s, req); ++ break; + } + pthread_mutex_lock(&s->lock); + } +@@ -957,6 +963,104 @@ do_commit(struct qcow2_state *s, struct qcow2_request *req) + pthread_mutex_unlock(&s->commit_lock); + } + ++int ++qcow2_query_commit_job(td_driver_t *driver, td_query_t *query) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ struct qcow2_request *req; ++ int err; ++ ++ DBG(TLOG_DBG, "Qcow2: query commit job.\n"); ++ ++ req = alloc_qcow2_request(s); ++ if (!req) ++ return -EBUSY; ++ ++ req->op = QCOW2_OP_QUERY; ++ ++ pthread_mutex_lock(&s->lock); ++ QSIMPLEQ_INSERT_TAIL(&s->inflight, req, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ pthread_mutex_lock(&s->commit_lock); ++ qemu_bh_schedule(s->bh); ++ ++ pthread_cond_wait(&s->commit_cond, &s->commit_lock); ++ ++ if (query) { ++ query->status = JobStatus_str(s->job_info.status); ++ query->current_progress = s->job_info.current_progress; ++ query->total_progress = s->job_info.total_progress; ++ } ++ ++ memset(&s->job_info, 0, sizeof(JobInfo)); ++ ++ err = req->error; ++ pthread_mutex_unlock(&s->commit_lock); ++ ++ DBG(TLOG_WARN, "Qcow2: query commit job done (%d).\n", err); ++ ++ free_qcow2_request(s, req); ++ ++ return err; ++} ++ ++static inline void ++do_query_commit_job(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ Error *local_err = NULL; ++ int err = 0; ++ BlockJob *bjob; ++ JobStatus status; ++ uint64_t current, total; ++ ++ job_lock(); ++ bjob = block_job_get_locked(COMMIT_JOB_ID); ++ if (!bjob) { ++ job_unlock(); ++ DPRINTF("Qcow2: no job running.\n"); ++ goto signal; ++ } ++ ++ status = bjob->job.status; ++ current = bjob->job.progress.current; ++ total = bjob->job.progress.total; ++ ++ if (status == JOB_STATUS_READY) { ++ Job *job = &bjob->job; ++ job_complete_locked(job, &local_err); ++ if (local_err) { ++ DPRINTF("Qcow2: job complete error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ err = -EINVAL; ++ } ++ } ++ if (status == JOB_STATUS_CONCLUDED) { ++ Job *job = &bjob->job; ++ job_dismiss_locked(&job, &local_err); ++ if (local_err) { ++ DPRINTF("Qcow2: job dismiss error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ err = -EINVAL; ++ } ++ } ++ job_unlock(); ++ ++ DPRINTF("Qcow2: commit job '%s'.\n", JobStatus_str(status)); ++ ++ pthread_mutex_lock(&s->commit_lock); ++ s->job_info.status = status; ++ s->job_info.current_progress = current; ++ s->job_info.total_progress = total; ++ pthread_mutex_unlock(&s->commit_lock); ++ ++signal: ++ pthread_mutex_lock(&s->commit_lock); ++ req->error = err; ++ pthread_cond_signal(&s->commit_cond); ++ pthread_mutex_unlock(&s->commit_lock); ++} ++ + void + qcow2_debug(td_driver_t *driver) + { +@@ -988,5 +1092,6 @@ struct tap_disk tapdisk_qcow = { + .td_get_parent_id = qcow2_get_parent_id, + .td_validate_parent = qcow2_validate_parent, + .td_commit = qcow2_commit, ++ .td_query_commit_job = qcow2_query_commit_job, + .td_debug = qcow2_debug, + }; diff --git a/SOURCES/0044-tapdisk-support-new-cancel-command.patch b/SOURCES/0044-tapdisk-support-new-cancel-command.patch new file mode 100644 index 0000000..ba853a4 --- /dev/null +++ b/SOURCES/0044-tapdisk-support-new-cancel-command.patch @@ -0,0 +1,312 @@ +From bab444b5fd7f67fdcf3cdc4b5a91f506dd97f336 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 5 Mar 2025 18:09:16 +0100 +Subject: [PATCH] tapdisk: support new cancel command + +This command will cancel the coalesce job in background. +$ tap-ctl cancel -m 0 -p 12345 + +Signed-off-by: Anthoine Bourgeois +--- + control/tap-ctl-commit.c | 30 +++++++++++++++++++++++ + control/tap-ctl.c | 47 +++++++++++++++++++++++++++++++++++++ + drivers/tapdisk-control.c | 32 +++++++++++++++++++++++++ + drivers/tapdisk-interface.c | 25 ++++++++++++++++++++ + drivers/tapdisk-interface.h | 1 + + drivers/tapdisk-vbd.c | 16 +++++++++++++ + drivers/tapdisk-vbd.h | 1 + + drivers/tapdisk.h | 1 + + include/tap-ctl.h | 1 + + include/tapdisk-message.h | 8 +++++++ + 10 files changed, 162 insertions(+) + +diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c +index eef6ea5f..1bc111a1 100644 +--- a/control/tap-ctl-commit.c ++++ b/control/tap-ctl-commit.c +@@ -105,3 +105,33 @@ tap_ctl_query_commit_job(const int id, const int minor) + + return err; + } ++ ++int ++tap_ctl_cancel_commit_job(const int id, const int minor, bool wait) ++{ ++ int err; ++ tapdisk_message_t message; ++ ++ memset(&message, 0, sizeof(message)); ++ message.type = TAPDISK_MESSAGE_CANCEL_COMMIT_JOB; ++ message.cookie = minor; ++ message.u.params.flags = wait; ++ ++ err = tap_ctl_connect_send_and_receive(id, &message, NULL); ++ if (err) ++ return err; ++ ++ if (message.type == TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP ++ || message.type == TAPDISK_MESSAGE_ERROR) { ++ err = -message.u.response.error; ++ } else { ++ EPRINTF("got unexpected result '%s' from %d\n", ++ tapdisk_message_name(message.type), id); ++ err = -EINVAL; ++ } ++ ++ if (err) ++ EPRINTF("cancel commit job failed: %s\n", strerror(-err)); ++ ++ return err; ++} +diff --git a/control/tap-ctl.c b/control/tap-ctl.c +index 1c55248a..c5597c94 100644 +--- a/control/tap-ctl.c ++++ b/control/tap-ctl.c +@@ -1030,6 +1030,52 @@ usage: + return EINVAL; + } + ++static void ++tap_cli_cancel_commit_job_usage(FILE *stream) ++{ ++ fprintf(stream, "usage: cancel <-p pid> <-m minor> [-w]\n"); ++} ++ ++int ++tap_cli_cancel_commit_job(int argc, char **argv) ++{ ++ int c, pid, minor; ++ bool wait; ++ ++ pid = -1; ++ minor = -1; ++ wait = false; ++ ++ optind = 0; ++ while ((c = getopt(argc, argv, "p:m:wh")) != -1) { ++ switch (c) { ++ case 'p': ++ pid = atoi(optarg); ++ break; ++ case 'm': ++ minor = atoi(optarg); ++ break; ++ case 'w': ++ wait = true; ++ break; ++ case '?': ++ goto usage; ++ case 'h': ++ tap_cli_cancel_commit_job_usage(stdout); ++ return 0; ++ } ++ } ++ ++ if (pid == -1 || minor == -1) ++ goto usage; ++ ++ return tap_ctl_cancel_commit_job(pid, minor, wait); ++ ++usage: ++ tap_cli_cancel_commit_job_usage(stderr); ++ return EINVAL; ++} ++ + struct command commands[] = { + { .name = "list", .func = tap_cli_list }, + { .name = "allocate", .func = tap_cli_allocate }, +@@ -1048,6 +1094,7 @@ struct command commands[] = { + { .name = "check", .func = tap_cli_check }, + { .name = "commit", .func = tap_cli_commit }, + { .name = "query", .func = tap_cli_query_commit_job }, ++ { .name = "cancel", .func = tap_cli_cancel_commit_job }, + }; + + #define print_commands() \ +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index ac21f4dc..4b7aab20 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -1376,6 +1376,34 @@ out: + return err; + } + ++static int ++tapdisk_control_cancel_commit_job(struct tapdisk_ctl_conn *conn, ++ tapdisk_message_t *request, tapdisk_message_t * const response) ++{ ++ int err; ++ td_vbd_t *vbd; ++ ++ ASSERT(conn); ++ ASSERT(request); ++ ASSERT(response); ++ ++ INFO("cancel commit job %d\n", request->cookie); ++ ++ vbd = tapdisk_server_get_vbd(request->cookie); ++ if (!vbd) { ++ err = -ENODEV; ++ ERR(err, "cancel commit job '%d' do not find an associated vbd, abort.\n", request->cookie); ++ goto out; ++ } ++ ++ err = tapdisk_vbd_cancel_commit_job(vbd, request->u.params.flags); ++out: ++ response->cookie = request->cookie; ++ if (!err) ++ response->type = TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP; ++ return err; ++} ++ + + struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_PID] = { +@@ -1441,6 +1469,10 @@ struct tapdisk_control_info message_infos[] = { + [TAPDISK_MESSAGE_QUERY_COMMIT_JOB] = { + .handler = tapdisk_control_query_commit_job, + .flags = TAPDISK_MSG_VERBOSE, ++ }, ++ [TAPDISK_MESSAGE_CANCEL_COMMIT_JOB] = { ++ .handler = tapdisk_control_cancel_commit_job, ++ .flags = TAPDISK_MSG_VERBOSE, + } + }; + +diff --git a/drivers/tapdisk-interface.c b/drivers/tapdisk-interface.c +index 428ee98f..4f3c79d3 100644 +--- a/drivers/tapdisk-interface.c ++++ b/drivers/tapdisk-interface.c +@@ -320,6 +320,31 @@ td_query_commit_job(td_image_t *image, td_query_t *query) + return driver->ops->td_query_commit_job(driver, query); + } + ++int ++td_cancel_commit_job(td_image_t *image, bool wait) ++{ ++ td_driver_t *driver; ++ ++ if (!image) { ++ return -ENODEV; ++ } ++ ++ driver = image->driver; ++ if (!driver) { ++ return -ENODEV; ++ } ++ ++ if (!td_flag_test(driver->state, TD_DRIVER_OPEN)) { ++ return -EBADF; ++ } ++ ++ if (!driver->ops->td_cancel_commit_job) { ++ return -EOPNOTSUPP; ++ } ++ ++ return driver->ops->td_cancel_commit_job(driver, wait); ++} ++ + void + td_forward_request(td_request_t treq) + { +diff --git a/drivers/tapdisk-interface.h b/drivers/tapdisk-interface.h +index b3ff6e13..d4ddf4d6 100644 +--- a/drivers/tapdisk-interface.h ++++ b/drivers/tapdisk-interface.h +@@ -44,6 +44,7 @@ int td_get_parent_id(td_image_t *, td_disk_id_t *); + int td_validate_parent(td_image_t *, td_image_t *); + int td_commit(td_image_t *, const char *); + int td_query_commit_job(td_image_t *, td_query_t *); ++int td_cancel_commit_job(td_image_t *, bool); + + void td_queue_write(td_image_t *, td_request_t); + void td_queue_read(td_image_t *, td_request_t); +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 7da25b6e..d082cc8d 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -1164,6 +1164,22 @@ tapdisk_vbd_query_commit_job(td_vbd_t *vbd, td_query_t *query) + return err; + } + ++int ++tapdisk_vbd_cancel_commit_job(td_vbd_t *vbd, bool wait) ++{ ++ int err; ++ ++ if (log) { ++ INFO("cancel commit job.\n"); ++ } ++ ++ err = td_cancel_commit_job(tapdisk_vbd_first_image(vbd), wait); ++ ++ INFO("cancel commit job (%d)\n", err); ++ ++ return err; ++} ++ + static int + tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, + const struct timeval *now) +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index fb948255..415e0d1f 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -232,6 +232,7 @@ void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); + int tapdisk_vbd_commit(td_vbd_t *, const char *); + int tapdisk_vbd_query_commit_job(td_vbd_t *, td_query_t *); ++int tapdisk_vbd_cancel_commit_job(td_vbd_t *, bool); + + void tapdisk_vbd_complete_td_request(td_request_t, int); + int add_extent(tapdisk_extents_t *, td_request_t *); +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index 06891584..d32e36b5 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -237,6 +237,7 @@ struct tap_disk { + void (*td_stats) (td_driver_t *, td_stats_t *); + int (*td_commit) (td_driver_t *, const char *); + int (*td_query_commit_job) (td_driver_t *, td_query_t *); ++ int (*td_cancel_commit_job) (td_driver_t *, bool); + + /** + * Callback to produce RRD output. +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index 8749b28c..f093912d 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -148,6 +148,7 @@ int tap_ctl_blk_major(void); + */ + int tap_ctl_commit(const int id, const int minor, const char *params); + int tap_ctl_query_commit_job(const int id, const int minor); ++int tap_ctl_cancel_commit_job(const int id, const int minor, bool wait); + + /** + * Instructs a tapdisk to connect to the shared ring. +diff --git a/include/tapdisk-message.h b/include/tapdisk-message.h +index 7645553a..22756eec 100644 +--- a/include/tapdisk-message.h ++++ b/include/tapdisk-message.h +@@ -247,6 +247,8 @@ enum tapdisk_message_id { + TAPDISK_MESSAGE_COMMIT_RSP, + TAPDISK_MESSAGE_QUERY_COMMIT_JOB, + TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP, ++ TAPDISK_MESSAGE_CANCEL_COMMIT_JOB, ++ TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP, + TAPDISK_MESSAGE_MAX /* This value must be the last. */ + }; + +@@ -353,6 +355,12 @@ tapdisk_message_name(enum tapdisk_message_id id) + case TAPDISK_MESSAGE_QUERY_COMMIT_JOB_RSP: + return "query commit job response"; + ++ case TAPDISK_MESSAGE_CANCEL_COMMIT_JOB: ++ return "cancel commit job"; ++ ++ case TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP: ++ return "cancel commit job response"; ++ + default: + return "unknown"; + } diff --git a/SOURCES/0045-qcow2-support-cancel-command.patch b/SOURCES/0045-qcow2-support-cancel-command.patch new file mode 100644 index 0000000..65cc327 --- /dev/null +++ b/SOURCES/0045-qcow2-support-cancel-command.patch @@ -0,0 +1,173 @@ +From 106e420cdca9ce4f310129ca8ad32c75d6003d0e Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 5 Mar 2025 18:22:56 +0100 +Subject: [PATCH] qcow2: support cancel command + +This command will cancel the coalesce job in background. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 92 ++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 91 insertions(+), 1 deletion(-) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index 37e7c324..eb9f77b1 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -100,6 +100,7 @@ enum qcow2_ops { + QCOW2_OP_WRITE, + QCOW2_OP_COMMIT, + QCOW2_OP_QUERY, ++ QCOW2_OP_CANCEL_COMMIT, + }; + + struct qcow2_state; +@@ -115,6 +116,8 @@ struct qcow2_request { + td_request_t treq; + /* OP_COMMIT */ + char * top; ++ /* OP_CANCEL_COMMIT */ ++ bool sync; + }; + struct qcow2_state *state; + BlockAIOCB *aiocb; +@@ -200,6 +203,8 @@ static inline void do_aio_read(struct qcow2_state *s, struct qcow2_request *req) + static inline void do_aio_write(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_commit(struct qcow2_state *s, struct qcow2_request *req); + static inline void do_query_commit_job(struct qcow2_state *s, struct qcow2_request *req); ++static inline void do_cancel_commit_job(struct qcow2_state *s, struct qcow2_request *req); ++static int qcow2_cancel_commit_job(td_driver_t *driver, bool wait); + + static int + qcow2_initialize(struct qcow2_state *s, Error **perr) +@@ -260,6 +265,9 @@ static void qcow2_handle_requests(struct qcow2_state *s) + case QCOW2_OP_QUERY: + do_query_commit_job(s, req); + break; ++ case QCOW2_OP_CANCEL_COMMIT: ++ do_cancel_commit_job(s, req); ++ break; + } + pthread_mutex_lock(&s->lock); + } +@@ -431,6 +439,18 @@ qcow2_open(void *opaque) + } + pthread_mutex_unlock(&s->lock); + ++ job_lock(); ++ BlockJob *bjob = block_job_get_locked(COMMIT_JOB_ID); ++ if (bjob) { ++ Job *job = &bjob->job; ++ job_dismiss_locked(&job, &local_err); ++ if (local_err) { ++ DPRINTF("Qcow2: job dismiss error: %s\n", error_get_pretty(local_err)); ++ error_free(local_err); ++ } ++ } ++ job_unlock(); ++ + blk_set_aio_context(conf->blk, qemu_get_aio_context(), &error_abort); + + blk_drain_all(); +@@ -516,6 +536,8 @@ _qcow2_close(td_driver_t *driver) + int err; + struct qcow2_state *s = (struct qcow2_state *)driver->data; + ++ qcow2_cancel_commit_job(driver, true); ++ + DBG(TLOG_WARN, "qcow2_close\n"); + + pthread_mutex_lock(&s->lock); +@@ -1061,9 +1083,77 @@ signal: + pthread_mutex_unlock(&s->commit_lock); + } + ++ ++int ++qcow2_cancel_commit_job(td_driver_t *driver, bool wait) ++{ ++ struct qcow2_state *s = (struct qcow2_state *)driver->data; ++ struct qcow2_request *req; ++ int err; ++ ++ DBG(TLOG_DBG, "Qcow2: cancel commit.\n"); ++ ++ req = alloc_qcow2_request(s); ++ if (!req) ++ return -EBUSY; ++ ++ req->op = QCOW2_OP_CANCEL_COMMIT; ++ req->sync = wait; ++ ++ pthread_mutex_lock(&s->lock); ++ QSIMPLEQ_INSERT_TAIL(&s->inflight, req, list); ++ pthread_mutex_unlock(&s->lock); ++ ++ pthread_mutex_lock(&s->commit_lock); ++ qemu_bh_schedule(s->bh); ++ ++ pthread_cond_wait(&s->commit_cond, &s->commit_lock); ++ err = req->error; ++ pthread_mutex_unlock(&s->commit_lock); ++ ++ DBG(TLOG_WARN, "Qcow2: cancel commit done (%d).\n", err); ++ ++ free_qcow2_request(s, req); ++ ++ return err; ++} ++ ++static inline void ++do_cancel_commit_job(struct qcow2_state *s, struct qcow2_request *req) ++{ ++ int err = 0; ++ BlockJob *bjob; ++ ++ job_lock(); ++ bjob = block_job_get_locked(COMMIT_JOB_ID); ++ if (!bjob) { ++ job_unlock(); ++ DPRINTF("Qcow2: no job to cancel.\n"); ++ goto signal; ++ } ++ ++ if (bjob->job.status == JOB_STATUS_RUNNING) { ++ if (req->sync == false) { ++ job_cancel_locked(&bjob->job, false); ++ } else { ++ err = job_cancel_sync_locked(&bjob->job, false); ++ } ++ } ++ job_unlock(); ++ ++ DBG(TLOG_WARN, "Qcow2: cancel %s (%d).\n", req->sync ? "sync" : "async", err); ++ ++signal: ++ pthread_mutex_lock(&s->commit_lock); ++ req->error = err; ++ pthread_cond_signal(&s->commit_cond); ++ pthread_mutex_unlock(&s->commit_lock); ++} ++ + void + qcow2_debug(td_driver_t *driver) + { ++#if DEBUGGING != 0 + struct qcow2_state *s = (struct qcow2_state *)driver->data; + + DBG(TLOG_WARN, "Qcow2: %s: queued %lu, completed %lu, returned %lu, " +@@ -1075,7 +1165,6 @@ qcow2_debug(td_driver_t *driver) + s->writes, (s->writes ? ((float)s->write_size / s->writes) : 0.0), + s->schedule, s->kick); + +-#if DEBUGGING != 0 + print_latencies(s); + #endif + } +@@ -1093,5 +1182,6 @@ struct tap_disk tapdisk_qcow = { + .td_validate_parent = qcow2_validate_parent, + .td_commit = qcow2_commit, + .td_query_commit_job = qcow2_query_commit_job, ++ .td_cancel_commit_job = qcow2_cancel_commit_job, + .td_debug = qcow2_debug, + }; diff --git a/SOURCES/0046-libqcow2-fix-abort-commit-without-crash.patch b/SOURCES/0046-libqcow2-fix-abort-commit-without-crash.patch new file mode 100644 index 0000000..b2f487d --- /dev/null +++ b/SOURCES/0046-libqcow2-fix-abort-commit-without-crash.patch @@ -0,0 +1,25 @@ +From 0ed27d616b9cb3fe3ab7fc9c5b27b24c30234839 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Wed, 12 Mar 2025 10:28:42 +0100 +Subject: [PATCH] libqcow2: fix abort commit without crash + +Signed-off-by: Anthoine Bourgeois +--- + qcow2/lib/block/commit.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/qcow2/lib/block/commit.c b/qcow2/lib/block/commit.c +index 6b96a4c6..2e3705ba 100644 +--- a/qcow2/lib/block/commit.c ++++ b/qcow2/lib/block/commit.c +@@ -98,6 +98,10 @@ static void commit_abort(Job *job) + * after the failed/cancelled commit job is gone? If we already wrote + * something to base, the intermediate images aren't valid any more. */ + bdrv_graph_rdlock_main_loop(); ++ if (!s->commit_top_bs->backing) { ++ bdrv_graph_rdunlock_main_loop(); ++ return; ++ } + commit_top_backing_bs = s->commit_top_bs->backing->bs; + bdrv_graph_rdunlock_main_loop(); + diff --git a/SOURCES/0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch b/SOURCES/Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch similarity index 100% rename from SOURCES/0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch rename to SOURCES/Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 7b37cb2..61e365f 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.3%{?dist} +Release: %{?xsrel}.4%{?dist} License: BSD Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -26,6 +26,7 @@ BuildRequires: openssl-devel >= 3.0.9 BuildRequires: devtoolset-11-gcc BuildRequires: devtoolset-11-binutils BuildRequires: devtoolset-11-liblsan-devel +BuildRequires: glib2-devel, gnutls-devel, libzstd-devel %{?_cov_buildrequires} Requires(post): systemd Requires(post): /sbin/ldconfig @@ -38,11 +39,54 @@ Conflicts: sm < 3.0.1 Provides: blktap(nbd) = 2.0 # XCP-ng patches +# git format-patch XS-v3.55.5-1..v3.55.5-qcow2 --no-signature --no-numbered +Patch1001: 0001-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch # Required by sm (qcow2). Upstream PR: https://github.com/xapi-project/blktap/pull/417 -Patch1001: 0001-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch - -# Upstream commit: https://github.com/xapi-project/blktap/commit/b132675928ff991aa332d4fba3e95cad9dfb0aad -Patch1002: 0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch +Patch1002: 0002-Add-an-option-to-use-backup-footer-when-vhd-util-que.patch +Patch1003: 0003-tapdisk-deduplicate-double-assignment-code.patch +Patch1004: 0004-blktap-fix-a-typo-in-libaio-backend.h-header.patch +Patch1005: 0005-tapdisk-document-final-param-in-__tapdisk_xenblkif_r.patch +Patch1006: 0006-tapdisk-use-tapdisk_vbd_for_each_blkif-abstraction.patch +Patch1007: 0007-blkif-Avoid-use-after-free-on-BLKIF_OP_WRITE_BARRIER.patch +Patch1008: 0008-tapdisk-vbd-remove-double-assignment-of-error-variab.patch +Patch1009: 0009-tapdisk-replace-flag-number-by-its-name.patch +Patch1010: 0010-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch +Patch1011: 0011-tapdisk-remove-unused-file-tapdisk-diff.c.patch +Patch1012: 0012-blkif-add-a-comment-on-memory-barrier-usage.patch +Patch1013: 0013-tapback-Synchronise-usage-with-code.patch +Patch1014: 0014-tap-ctl-fix-comments-of-tap_ctl_info-function.patch +Patch1015: 0015-tapdisk-fix-hardcoded-array-size-with-a-macro.patch +Patch1016: 0016-tapdisk-Replace-structure-name-in-sizeof.patch +Patch1017: 0017-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch +Patch1018: 0018-tapdisk-Fix-a-typo-in-util.h-header.patch +Patch1019: 0019-tapdisk-rename-field-pool-to-pool_name.patch +Patch1020: 0020-td-req-remove-unused-field-gref.patch +Patch1021: 0021-td-req-rename-tapreq-as-req.patch +Patch1022: 0022-td-req-remove-old-code.patch +Patch1023: 0023-td-req-fix-typo-in-a-comment.patch +Patch1024: 0024-libqcow2-manage-libqcow2-sources-import.patch +Patch1025: 0025-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch +Patch1026: 0026-libqcow2-build-qcow2-library-for-tapdisk.patch +Patch1027: 0027-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch +Patch1028: 0028-tapdisk-protect-td_vbd_t-structure.patch +Patch1029: 0029-tapdisk-protect-td_blktap_t-structure.patch +Patch1030: 0030-tapdisk-protect-td_xenblkif-structure.patch +Patch1031: 0031-tapdisk-protect-scheduler-structure.patch +Patch1032: 0032-mocka-fix-scheduler-tests-according-mutex-protection.patch +Patch1033: 0033-tapdisk-protect-td_nbdserver-structures.patch +Patch1034: 0034-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch +Patch1035: 0035-libqcow2-mask-signals-used-by-tapdisk.patch +Patch1036: 0036-tapdisk-replace-signals-handling-by-signalfd.patch +Patch1037: 0037-qcow2-driver-support-qcow2-images-in-tapdisk.patch +Patch1038: 0038-vbd-wake-up-scheduler-to-force-check-ring.patch +Patch1039: 0039-blktap.spec-add-qcow2-dependencies.patch +Patch1040: 0040-tapdisk-support-new-commit-command.patch +Patch1041: 0041-qcow2-support-commit-command.patch +Patch1042: 0042-tapdisk-support-new-query-command.patch +Patch1043: 0043-qcow2-support-query-command.patch +Patch1044: 0044-tapdisk-support-new-cancel-command.patch +Patch1045: 0045-qcow2-support-cancel-command.patch +Patch1046: 0046-libqcow2-fix-abort-commit-without-crash.patch %description Blktap creates kernel block devices which realize I/O requests to @@ -83,9 +127,10 @@ sh autogen.sh %{?_cov_wrap} make %{?coverage:GCOV=true} %check +source /opt/rh/devtoolset-11/enable make clean make check GCOV=true || (find mockatests -name \*.log -print -exec cat {} \; && false) -./collect-test-results.sh %{buildroot}/testresults +#./collect-test-results.sh %{buildroot}/testresults %install rm -rf %{buildroot} @@ -113,6 +158,7 @@ cat /usr/lib/udev/rules.d/65-md-incremental.rules >> /etc/udev/rules.d/65-md-inc %{_bindir}/vhd-index %{_bindir}/tapback %{_bindir}/cpumond +%{_bindir}/qemu-img %{_sbindir}/cbt-util %{_sbindir}/lvm-util %{_sbindir}/tap-ctl @@ -156,19 +202,8 @@ fi # The posttrans invocation of ldconfig is needed because older # versions of blktap did not have ldconfig in their postun script. -%posttrans -p /sbin/ldconfig - -%{?_cov_results_package} - -%package testresults -Group: System/Hypervisor -Summary: test results for blktap package - -%description testresults -The package contains the build time test results for the blktap package - -%files testresults -/testresults +%posttrans +/sbin/ldconfig %package -n vhd-util-standalone Group: System/Hypervisor @@ -185,6 +220,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Tue Apr 07 2026 Damien Thenot - 3.55.5-6.4 +- Release of QCOW2 support + * Thu Feb 26 2026 Mathieu Labourier - 3.55.5.6.3 - Prevent segfault of vhd-util scan on VHD with corrupt footer From 7690a4a8be6885ca62205898d11e238d03a9ddc9 Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Wed, 1 Apr 2026 16:13:53 +0200 Subject: [PATCH 13/20] Fix scriptlet to use udev rule aligned to mdadm This change aligns file path used in blktap's scriptlet to path from latest mdadm update. Note that the GOTO is different in both rules because files are not 100% aligned. The issue can reproduced by executing the scriptlet explicitly: sudo yum reinstall blktap Installing : blktap-3.55.5-6.3.xcpng8.3.x86_64 1/1 cat: /usr/lib/udev/rules.d/65-md-incremental.rules: No such file or directory warning: %triggerin(blktap-3.55.5-6.3.xcpng8.3.x86_64) scriptlet failed, exit status 1 Non-fatal scriptlet failure in rpm package blktap-3.55.5-6.3.xcpng8.3.x86_64 Or alternatively: sudo yum reinstall mdadm Installing : mdadm-4.2-5.xcpng8.3.x86_64 1/1 cat: /usr/lib/udev/rules.d/65-md-incremental.rules: No such file or directory warning: %triggerin(blktap-3.55.5-6.3.xcpng8.3.x86_64) scriptlet failed, exit status 1 Non-fatal scriptlet failure in rpm package mdadm-4.2-5.xcpng8.3.x86_64 The cause is a mdadm's udev rule that was relocated and scriptlet fixed accordingly in related change. On latest mdadm update, packager decided to drop downstream rule file (as known as `65-md-incremental.rules` and relies on upstream one which has a different naming `64-md-raid-assembly.rules`. This renaming path broke blktap, this change realign to upstream. From the installed state (xcp-ng-8.3.0-20250606.iso) - blktap-3.55.5-2.1.xcpng8.3 + mdadm-4.0-13.el7 ~ /e/u/r/65* The following scenari have been tested: - mdadm-4.0-13.el7 (old from iso) then blktap-3.55.5-6.5.xcpng8.3 ~ /e/u/r/65* - blktap-3.55.5-6.5.xcpng8.3 then mdadm-4.2-5.xcpng8.3 (update) ~ /e/u/r/64* - blktap-3.55.5-6.5.xcpng8.3 then mdadm-4.0-13.el7 (downgrade) ~ /e/u/r/65* - blktap-3.55.5-6.5.xcpng8.3 then mdadm-4.2-5.xcpng8.3 (update) ~ /e/u/r/64* The issue can be now reproduced again with downgrade of blktap (as observed before). - mdadm-4.2-5.xcpng8.3 then blktap-3.55.5-6.3.xcpng8.3.x86_64 ! looking 65 Side notes It is not necessary to update mdadm because trigger will be run update of blktap as explained at: - https://rpm-software-management.github.io/rpm/man/rpm-scriptlets.7 - https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#ordering Update/downgrade scriptlet can not be grouped into single one to detect update or downgrade, because: - 1: Relying on presence of files is not possible, because scriptlets are evaluated before RPM "Cleanup" task and both files can be present. - 2: Relying on RPM query of mdadm is not possible between states. Origin: https://github.com/xcp-ng-rpms/blktap/pull/17 Relate-to: XCPNG-3024 Relate-to: https://src.fedoraproject.org/rpms/mdadm/c/cdf21d08ef36728f088f69a79e30aa996ccd5a67 Relate-to: https://github.com/xcp-ng-rpms/mdadm/commit/c1f033316320c6e44546f9567b9837bbfee2ed8b#r181097962 Relate-to: https://github.com/xcp-ng-rpms/mdadm/pull/2 Relate-to: https://github.com/md-raid-utilities/mdadm/commit/2452f13a41d3e6fb00665a2d27fbfbf4f51a7464#diff-cebbb0bcfc268c2e841300de2a6bdeb0c4c3110ff0f2072b782ca3a2a336a9afR19 Thanks-to: Anthoine Bourgeois Signed-off-by: Philippe Coval --- SPECS/blktap.spec | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 61e365f..1c9b7a6 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.4%{?dist} +Release: %{?xsrel}.5%{?dist} License: BSD Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -145,9 +145,27 @@ rm -f %{buildroot}%{_libdir}/*.la ## Remove static libraries; they should not be used by other packages rm -f %{buildroot}%{_libdir}/*.a -%triggerin -- mdadm -echo 'KERNEL=="td[a-z]*", GOTO="md_end"' > /etc/udev/rules.d/65-md-incremental.rules -cat /usr/lib/udev/rules.d/65-md-incremental.rules >> /etc/udev/rules.d/65-md-incremental.rules +%triggerin -- mdadm <= 4.0 +OLD="65-md-incremental.rules" +NEW="64-md-raid-assembly.rules" +SRC="/usr/lib/udev/rules.d/$OLD" +DST="/etc/udev/rules.d/$OLD" +echo "# File generated by blktap's RPM trigger from mdadm's $SRC" > "$DST" +echo 'KERNEL=="td[a-z]*", GOTO="md_end"' >> "$DST" +cat "$SRC" >> "$DST" +# Remove file generated by blktap from newer mdadm if present (downgrade) +rm -f "/etc/udev/rules.d/$NEW" + +%triggerin -- mdadm > 4.0 +OLD="65-md-incremental.rules" +NEW="64-md-raid-assembly.rules" +SRC="/usr/lib/udev/rules.d/$NEW" +DST="/etc/udev/rules.d/$NEW" +echo "# File generated by blktap's RPM trigger from mdadm's $SRC" > "$DST" +echo 'KERNEL=="td[a-z]*", GOTO="md_inc_end"' >> "$DST" +cat "$SRC" >> "$DST" +# Remove file generated by blktap from older mdadm if present (update) +rm -f "/etc/udev/rules.d/$OLD" %files %defattr(-,root,root,-) @@ -220,6 +238,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Thu Apr 09 2026 Philippe Coval - 3.55.5-6.5 +- Fix scriptlet to use udev rule aligned to mdadm + * Tue Apr 07 2026 Damien Thenot - 3.55.5-6.4 - Release of QCOW2 support From 7ffbf3765cd899b93f4ed32b6fd5dbcfa8d57b7c Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Fri, 24 Apr 2026 09:52:06 +0200 Subject: [PATCH 14/20] Release 3.55.5-6.6: Dual license blktap package with GPLv2 libqcow2 included in blktap package is exclusively GPLv2 sources. Reflect this state in the RPM license list. Signed-off-by: Anthoine Bourgeois --- SPECS/blktap.spec | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 1c9b7a6..4f6d5c1 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,8 +7,8 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.5%{?dist} -License: BSD +Release: %{?xsrel}.6%{?dist} +License: BSD AND GPL-2.0-or-later Group: System/Hypervisor URL: https://github.com/xapi-project/blktap Source0: blktap-3.55.5.tar.gz @@ -238,6 +238,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Fri Apr 24 2026 Anthoine Bourgeois - 3.55.5-6.6 +- Add GPLv2 to the RPM license list as libqcow2 use this license + * Thu Apr 09 2026 Philippe Coval - 3.55.5-6.5 - Fix scriptlet to use udev rule aligned to mdadm From cab57a802bb964cb83256060029c9d7f988424dd Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Mon, 27 Apr 2026 14:49:20 +0200 Subject: [PATCH 15/20] qcow2: fix coalesce 2 issues fixed: - crash of tapdisk in ready state - infinite coalesce on loaded guest Signed-off-by: Anthoine Bourgeois --- ...ize-commit-job-to-avoid-never-ending.patch | 60 +++++++++++++++++++ SPECS/blktap.spec | 6 +- 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0047-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch diff --git a/SOURCES/0047-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch b/SOURCES/0047-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch new file mode 100644 index 0000000..6efa55f --- /dev/null +++ b/SOURCES/0047-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch @@ -0,0 +1,60 @@ +From 2118e999404c8bfadf7a04ae5a529dd2d8419130 Mon Sep 17 00:00:00 2001 +From: Anthoine Bourgeois +Date: Tue, 14 Apr 2026 09:40:11 +0200 +Subject: [PATCH] qcow2: Auto finalize commit job to avoid never ending job + +With this patch the qemu code is responsible for the finalize operation +that make the state transition from ready to pending. The query command +is still responsible of dismiss the job to conclue it. + +Signed-off-by: Anthoine Bourgeois +--- + drivers/block-qcow2.c | 5 +++-- + qcow2/lib/util/main-loop.c | 4 ++-- + 2 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c +index eb9f77b1..437c69f4 100644 +--- a/drivers/block-qcow2.c ++++ b/drivers/block-qcow2.c +@@ -971,7 +971,7 @@ do_commit(struct qcow2_state *s, struct qcow2_request *req) + + qmp_block_commit(COMMIT_JOB_ID, node, base_node, NULL, top_node, NULL, NULL, + false, false, false, 0, false, BLOCKDEV_ON_ERROR_REPORT, +- NULL, false, false, true, false, &local_err); ++ NULL, true, true, true, false, &local_err); + + if (local_err) { + DPRINTF("qcow2_commit: error: %s\n", error_get_pretty(local_err)); +@@ -1132,7 +1132,8 @@ do_cancel_commit_job(struct qcow2_state *s, struct qcow2_request *req) + goto signal; + } + +- if (bjob->job.status == JOB_STATUS_RUNNING) { ++ if (bjob->job.status == JOB_STATUS_RUNNING || ++ bjob->job.status == JOB_STATUS_READY) { + if (req->sync == false) { + job_cancel_locked(&bjob->job, false); + } else { +diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c +index 82d737fe..740a8164 100644 +--- a/qcow2/lib/util/main-loop.c ++++ b/qcow2/lib/util/main-loop.c +@@ -210,12 +210,12 @@ int qemu_deinit_main_loop(void) + + src = iohandler_get_g_source(); + g_source_unref(src); +- g_source_remove(g_source_get_id(src)); ++ g_source_destroy(src); + g_source_unref(src); + + src = aio_get_g_source(qemu_aio_context); + g_source_unref(src); +- g_source_remove(g_source_get_id(src)); ++ g_source_destroy(src); + g_source_unref(src); + + g_array_free(gpollfds, TRUE); +-- +2.53.0 + diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 4f6d5c1..2aaf6a2 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.6%{?dist} +Release: %{?xsrel}.7%{?dist} License: BSD AND GPL-2.0-or-later Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -87,6 +87,7 @@ Patch1043: 0043-qcow2-support-query-command.patch Patch1044: 0044-tapdisk-support-new-cancel-command.patch Patch1045: 0045-qcow2-support-cancel-command.patch Patch1046: 0046-libqcow2-fix-abort-commit-without-crash.patch +Patch1047: 0047-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch %description Blktap creates kernel block devices which realize I/O requests to @@ -238,6 +239,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Thu Apr 30 2026 Anthoine Bourgeois - 3.55.5-6.7 +- Fix tapdisk crash and prevent infinite coalesce + * Fri Apr 24 2026 Anthoine Bourgeois - 3.55.5-6.6 - Add GPLv2 to the RPM license list as libqcow2 use this license From 0e45979900f73d9419647177978846bff74986e3 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Wed, 20 May 2026 17:15:10 +0200 Subject: [PATCH 16/20] Import blktap-3.55.5-9.xs8.src.rpm Signed-off-by: Anthoine Bourgeois --- ...util_scan_on_vhd_with_corrupt_footer.patch | 55 +++++++++++++++++++ SPECS/blktap.spec | 15 +++-- 2 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 SOURCES/prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch diff --git a/SOURCES/prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch b/SOURCES/prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch new file mode 100644 index 0000000..8360de0 --- /dev/null +++ b/SOURCES/prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch @@ -0,0 +1,55 @@ +Prevent segfault of vhd-util scan on VHD with corrupt footer + +From: Mathieu Labourier + +vhd-util scan can crash when ran against a VHD with a corrupt footer. + +Happens when the footer reports a VHD type that should have a parent +(like HD_TYPE_DIFF) while having none, causing the segfault. + +Signed-off-by: Mathieu Labourier + +diff --git a/vhd/lib/vhd-util-scan.c b/vhd/lib/vhd-util-scan.c +index eae9d68..17392ab 100644 +--- a/vhd/lib/vhd-util-scan.c ++++ b/vhd/lib/vhd-util-scan.c +@@ -46,6 +46,7 @@ + #include + #include + ++#include "debug.h" + #include "list.h" + #include "libvhd.h" + #include "lvm-util.h" +@@ -482,6 +483,14 @@ copy_name(char *dst, const char *src) + static int + vhd_util_scan_extract_volume_name(char *dst, const char *src, size_t size) + { ++ ASSERT(dst); ++ ASSERT(src); ++ ++ if (!*src) { ++ EPRINTF("parent name is empty\n"); ++ return -EINVAL; ++ } ++ + char copy[VHD_MAX_NAME_LEN], *name, *s, *c; + + name = strrchr(src, '/'); +@@ -509,6 +518,7 @@ vhd_util_scan_extract_volume_name(char *dst, const char *src, size_t size) + return -EINVAL; + } + ++ ASSERT(c && *c == '/'); + safe_strncpy(dst, ++c, size); + return 0; + } +@@ -542,7 +552,7 @@ found: + if (!err) + return copy_name(image->parent, name); + +- return 0; ++ return err; + } + + static int diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index ea9024e..109eaf2 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -1,6 +1,6 @@ -%global package_speccommit 4dac0dd72bca87df4f25fc1e6d2f18713ecc53c1 +%global package_speccommit 284310adc02e3a383ece9d885a11231c1cc56374 %global usver 3.55.5 -%global xsver 7 +%global xsver 9 %global xsrel %{xsver}%{?xscount}%{?xshash} %global package_srccommit v3.55.5 @@ -18,6 +18,7 @@ Patch2: ca-408175__distinguish_logging_for_long_nbd_operations.patch Patch3: CP-308382_fix_sign_conversion_in_coalesce Patch4: fix_coalesced_size_conversion_in_vhd-util-coalesce.patch Patch5: ca-416464__return_blkif_rsp_eopnotsupp_for_eopnotsupp.patch +Patch6: prevent_segfault_of_vhd-util_scan_on_vhd_with_corrupt_footer.patch BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot Obsoletes: xen-blktap < 4 @@ -94,10 +95,6 @@ rm -f %{buildroot}%{_libdir}/*.la ## Remove static libraries; they should not be used by other packages rm -f %{buildroot}%{_libdir}/*.a -%triggerin -- mdadm -echo 'KERNEL=="td[a-z]*", GOTO="md_end"' > /etc/udev/rules.d/65-md-incremental.rules -cat /usr/lib/udev/rules.d/65-md-incremental.rules >> /etc/udev/rules.d/65-md-incremental.rules - %files %defattr(-,root,root,-) %docdir /usr/share/doc/%{name} @@ -179,6 +176,12 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Thu Apr 09 2026 Mark Syms - 3.55.5-9 +- Remove old, obsolete, udev rule override. + +* Wed Jan 28 2026 Mark Syms - 3.55.5-8 +- Prevent segfault of vhd-util scan on VHD with corrupt footer + * Thu Aug 28 2025 Mark Syms - 3.55.5-7 - CA-416464: return BLKIF_RSP_EOPNOTSUPP for EOPNOTSUPP From a39d95fa9605ce0297299792b74e13b207cc6709 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 18 Jun 2026 11:15:23 +0200 Subject: [PATCH 17/20] Release 3.55.5-9.2: Qcow2 fixes Signed-off-by: Anthoine Bourgeois --- ...disk-replace-flag-number-by-its-name.patch | 60 +- ...ric-TAPDISK_MESSAGE_MAX-limit-inside.patch | 2 +- ...sk-remove-unused-file-tapdisk-diff.c.patch | 2 +- ...dd-a-comment-on-memory-barrier-usage.patch | 2 +- ...-tapback-Synchronise-usage-with-code.patch | 2 +- ...ix-comments-of-tap_ctl_info-function.patch | 2 +- ...ix-hardcoded-array-size-with-a-macro.patch | 2 +- ...isk-Replace-structure-name-in-sizeof.patch | 2 +- ...-RD-macros-are-defined-in-ring.h-sin.patch | 2 +- ...-tapdisk-Fix-a-typo-in-util.h-header.patch | 2 +- ...pdisk-rename-field-pool-to-pool_name.patch | 2 +- ...0019-td-req-remove-unused-field-gref.patch | 2 +- .../0020-td-req-rename-tapreq-as-req.patch | 2 +- SOURCES/0021-td-req-remove-old-code.patch | 2 +- .../0022-td-req-fix-typo-in-a-comment.patch | 2 +- ...qcow2-manage-libqcow2-sources-import.patch | 38 +- ...port-vanilla-sources-from-qemu-9.1.1.patch | 6492 +---------------- ...cow2-build-qcow2-library-for-tapdisk.patch | 3908 ++-------- ...port-for-old-components-gcc-glibc-gl.patch | 23 +- ...7-tapdisk-protect-td_vbd_t-structure.patch | 76 +- ...apdisk-protect-td_blktap_t-structure.patch | 64 +- ...apdisk-protect-td_xenblkif-structure.patch | 103 +- ...-tapdisk-protect-scheduler-structure.patch | 2 +- ...ler-tests-according-mutex-protection.patch | 2 +- ...disk-protect-td_nbdserver-structures.patch | 48 +- ...-proper-cleanup-of-libqcow2-on-close.patch | 24 +- ...ibqcow2-mask-signals-used-by-tapdisk.patch | 4 +- ...replace-signals-handling-by-signalfd.patch | 2 +- ...iver-support-qcow2-images-in-tapdisk.patch | 61 +- ...ake-up-scheduler-to-force-check-ring.patch | 39 +- ...8-blktap.spec-add-qcow2-dependencies.patch | 22 - ...-tapdisk-support-new-commit-command.patch} | 14 +- ...> 0039-qcow2-support-commit-command.patch} | 25 +- ...0-tapdisk-support-new-query-command.patch} | 12 +- ...=> 0041-qcow2-support-query-command.patch} | 22 +- ...-tapdisk-support-new-cancel-command.patch} | 12 +- ...> 0043-qcow2-support-cancel-command.patch} | 21 +- ...cow2-fix-abort-commit-without-crash.patch} | 4 +- ...ize-commit-job-to-avoid-never-ending.patch | 60 - SPECS/blktap.spec | 23 +- 40 files changed, 970 insertions(+), 10219 deletions(-) delete mode 100644 SOURCES/0038-blktap.spec-add-qcow2-dependencies.patch rename SOURCES/{0039-tapdisk-support-new-commit-command.patch => 0038-tapdisk-support-new-commit-command.patch} (97%) rename SOURCES/{0040-qcow2-support-commit-command.patch => 0039-qcow2-support-commit-command.patch} (88%) rename SOURCES/{0041-tapdisk-support-new-query-command.patch => 0040-tapdisk-support-new-query-command.patch} (97%) rename SOURCES/{0042-qcow2-support-query-command.patch => 0041-qcow2-support-query-command.patch} (88%) rename SOURCES/{0043-tapdisk-support-new-cancel-command.patch => 0042-tapdisk-support-new-cancel-command.patch} (97%) rename SOURCES/{0044-qcow2-support-cancel-command.patch => 0043-qcow2-support-cancel-command.patch} (89%) rename SOURCES/{0045-libqcow2-fix-abort-commit-without-crash.patch => 0044-libqcow2-fix-abort-commit-without-crash.patch} (90%) delete mode 100644 SOURCES/0046-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch diff --git a/SOURCES/0008-tapdisk-replace-flag-number-by-its-name.patch b/SOURCES/0008-tapdisk-replace-flag-number-by-its-name.patch index 9b554b7..6847f61 100644 --- a/SOURCES/0008-tapdisk-replace-flag-number-by-its-name.patch +++ b/SOURCES/0008-tapdisk-replace-flag-number-by-its-name.patch @@ -1,4 +1,4 @@ -From 8edd8f5e5797a3d88b27b24fb85c90cd878aff34 Mon Sep 17 00:00:00 2001 +From a99dad47e85f999c9029d433e1b58a9a6fa09892 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Tue, 21 Jan 2025 10:34:16 +0100 Subject: [PATCH] tapdisk: replace flag number by its name @@ -7,51 +7,19 @@ No functional change expected. Signed-off-by: Anthoine Bourgeois --- - drivers/tapdisk-disktype.c | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) + drivers/tapdisk-disktype.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/drivers/tapdisk-disktype.c b/drivers/tapdisk-disktype.c -index 8ab96c4f..4a60c756 100644 ---- a/drivers/tapdisk-disktype.c -+++ b/drivers/tapdisk-disktype.c -@@ -54,13 +54,13 @@ static const disk_info_t sync_disk = { - static const disk_info_t vmdk_disk = { - "vmdk", - "vmware image (vmdk)", -- 1, -+ DISK_TYPE_SINGLE_CONTROLLER, - }; +diff --git a/drivers/tapdisk-disktype.h b/drivers/tapdisk-disktype.h +index 2490cce4..bb6a8d34 100644 +--- a/drivers/tapdisk-disktype.h ++++ b/drivers/tapdisk-disktype.h +@@ -61,7 +61,7 @@ extern const disk_info_t *tapdisk_disk_types[]; + extern const struct tap_disk *tapdisk_disk_drivers[]; - static const disk_info_t vhdsync_disk = { - "vhdsync", - "virtual server image (vhd) - synchronous", -- 1, -+ DISK_TYPE_SINGLE_CONTROLLER, - }; + /* one single controller for all instances of disk type */ +-#define DISK_TYPE_SINGLE_CONTROLLER (1<<0) ++/*#define DISK_TYPE_SINGLE_CONTROLLER (1<<0) - Deprecated */ - static const disk_info_t vhd_disk = { -@@ -73,7 +73,7 @@ static const disk_info_t vhd_disk = { - static const disk_info_t ram_disk = { - "ram", - "ramdisk image (ram)", -- 1, -+ DISK_TYPE_SINGLE_CONTROLLER, - }; - - static const disk_info_t qcow_disk = { -@@ -85,13 +85,13 @@ static const disk_info_t qcow_disk = { - static const disk_info_t block_cache_disk = { - "bc", - "block cache image (bc)", -- 1, -+ DISK_TYPE_SINGLE_CONTROLLER, - }; - - static const disk_info_t vhd_index_disk = { - "vhdi", - "vhd index image (vhdi)", -- 1, -+ DISK_TYPE_SINGLE_CONTROLLER, - }; - - static const disk_info_t log_disk = { + /* filter driver without physical image data */ + #define DISK_TYPE_FILTER (1<<1) diff --git a/SOURCES/0009-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch b/SOURCES/0009-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch index 0dbef44..8d36bf3 100644 --- a/SOURCES/0009-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch +++ b/SOURCES/0009-tapdisk-set-generic-TAPDISK_MESSAGE_MAX-limit-inside.patch @@ -1,4 +1,4 @@ -From 774d320fad1f9cc37491b9e7dd157335d6109244 Mon Sep 17 00:00:00 2001 +From 1a8b94001cde20402e268e33263b8fb3a407a9a7 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 13 Feb 2025 14:52:10 +0100 Subject: [PATCH] tapdisk: set generic TAPDISK_MESSAGE_MAX limit inside the diff --git a/SOURCES/0010-tapdisk-remove-unused-file-tapdisk-diff.c.patch b/SOURCES/0010-tapdisk-remove-unused-file-tapdisk-diff.c.patch index 7cc55a9..a75cc93 100644 --- a/SOURCES/0010-tapdisk-remove-unused-file-tapdisk-diff.c.patch +++ b/SOURCES/0010-tapdisk-remove-unused-file-tapdisk-diff.c.patch @@ -1,4 +1,4 @@ -From 3f2ca1e82015a9f44cb7c9c5524ee11f29d4d18c Mon Sep 17 00:00:00 2001 +From b90bdb551e1605ac319ed3024d0c5d26297d1e24 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 16 Oct 2025 18:43:42 +0200 Subject: [PATCH] tapdisk: remove unused file tapdisk-diff.c diff --git a/SOURCES/0011-blkif-add-a-comment-on-memory-barrier-usage.patch b/SOURCES/0011-blkif-add-a-comment-on-memory-barrier-usage.patch index f4a2513..3d69a77 100644 --- a/SOURCES/0011-blkif-add-a-comment-on-memory-barrier-usage.patch +++ b/SOURCES/0011-blkif-add-a-comment-on-memory-barrier-usage.patch @@ -1,4 +1,4 @@ -From a46de7a8960adb6e5bd74dc5d9326e6db3278b4c Mon Sep 17 00:00:00 2001 +From badd991784d27dc6cb07b0cc034bf31a0f07de93 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 27 Feb 2025 17:41:36 +0100 Subject: [PATCH] blkif: add a comment on memory barrier usage diff --git a/SOURCES/0012-tapback-Synchronise-usage-with-code.patch b/SOURCES/0012-tapback-Synchronise-usage-with-code.patch index b886c4b..c5b3424 100644 --- a/SOURCES/0012-tapback-Synchronise-usage-with-code.patch +++ b/SOURCES/0012-tapback-Synchronise-usage-with-code.patch @@ -1,4 +1,4 @@ -From 49eb6fc28cb80f7e1f14fb1add074a36b7457476 Mon Sep 17 00:00:00 2001 +From a088cdc3040c5c627c6a089384aa007c8865d59b Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Sat, 15 Mar 2025 12:25:11 +0100 Subject: [PATCH] tapback: Synchronise usage with code diff --git a/SOURCES/0013-tap-ctl-fix-comments-of-tap_ctl_info-function.patch b/SOURCES/0013-tap-ctl-fix-comments-of-tap_ctl_info-function.patch index f5d1888..c57fd66 100644 --- a/SOURCES/0013-tap-ctl-fix-comments-of-tap_ctl_info-function.patch +++ b/SOURCES/0013-tap-ctl-fix-comments-of-tap_ctl_info-function.patch @@ -1,4 +1,4 @@ -From 862a1d549ac9e5ce8750d31bd1ce3027b050d240 Mon Sep 17 00:00:00 2001 +From a74e38ffa8b049bd5a2bf2ef121737664eda3366 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Sat, 15 Mar 2025 12:26:27 +0100 Subject: [PATCH] tap-ctl: fix comments of tap_ctl_info function diff --git a/SOURCES/0014-tapdisk-fix-hardcoded-array-size-with-a-macro.patch b/SOURCES/0014-tapdisk-fix-hardcoded-array-size-with-a-macro.patch index 50b6065..136fc71 100644 --- a/SOURCES/0014-tapdisk-fix-hardcoded-array-size-with-a-macro.patch +++ b/SOURCES/0014-tapdisk-fix-hardcoded-array-size-with-a-macro.patch @@ -1,4 +1,4 @@ -From a2ff9b3636d8b30040d96d13b1f9a08706804ec3 Mon Sep 17 00:00:00 2001 +From ea5a1543e987283abac46a62222243b179c9da19 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Mon, 17 Mar 2025 14:48:06 +0100 Subject: [PATCH] tapdisk: fix hardcoded array size with a macro diff --git a/SOURCES/0015-tapdisk-Replace-structure-name-in-sizeof.patch b/SOURCES/0015-tapdisk-Replace-structure-name-in-sizeof.patch index a10a7b5..cd87172 100644 --- a/SOURCES/0015-tapdisk-Replace-structure-name-in-sizeof.patch +++ b/SOURCES/0015-tapdisk-Replace-structure-name-in-sizeof.patch @@ -1,4 +1,4 @@ -From 54c7fafd406c72c9f56893bc5e82af311b29de80 Mon Sep 17 00:00:00 2001 +From c889ec008cc58053db48262c8a9ee8730a5cd14a Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Tue, 18 Mar 2025 19:28:22 +0100 Subject: [PATCH] tapdisk: Replace structure name in sizeof diff --git a/SOURCES/0016-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch b/SOURCES/0016-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch index 7430dd7..ba41697 100644 --- a/SOURCES/0016-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch +++ b/SOURCES/0016-tapdisk-check-if-RD-macros-are-defined-in-ring.h-sin.patch @@ -1,4 +1,4 @@ -From ea55c11cee3c9e77349c0fa7ea19e7e8202634b9 Mon Sep 17 00:00:00 2001 +From 636c4a76d8032b29e7f6b9299e602fd61bdd5811 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Fri, 14 Mar 2025 15:10:25 +0100 Subject: [PATCH] tapdisk: check if RD macros are defined in ring.h (since diff --git a/SOURCES/0017-tapdisk-Fix-a-typo-in-util.h-header.patch b/SOURCES/0017-tapdisk-Fix-a-typo-in-util.h-header.patch index 73ae479..b20b9ef 100644 --- a/SOURCES/0017-tapdisk-Fix-a-typo-in-util.h-header.patch +++ b/SOURCES/0017-tapdisk-Fix-a-typo-in-util.h-header.patch @@ -1,4 +1,4 @@ -From b19df24862e0e322a10a13b186425a73fdfb712a Mon Sep 17 00:00:00 2001 +From 04283cb976ac46341687704b790cf571cb2d1680 Mon Sep 17 00:00:00 2001 From: Emmanuel Varagnat Date: Thu, 8 Jan 2026 19:06:03 +0100 Subject: [PATCH] tapdisk: Fix a typo in util.h header diff --git a/SOURCES/0018-tapdisk-rename-field-pool-to-pool_name.patch b/SOURCES/0018-tapdisk-rename-field-pool-to-pool_name.patch index bd9890f..5e89066 100644 --- a/SOURCES/0018-tapdisk-rename-field-pool-to-pool_name.patch +++ b/SOURCES/0018-tapdisk-rename-field-pool-to-pool_name.patch @@ -1,4 +1,4 @@ -From e7bebd4424888e750116f32952e6b9c726558482 Mon Sep 17 00:00:00 2001 +From 2e4ee8039cc272d2abb0dbe589a98e6ed271a373 Mon Sep 17 00:00:00 2001 From: Emmanuel Varagnat Date: Mon, 5 Jan 2026 16:17:57 +0100 Subject: [PATCH] tapdisk: rename field pool to pool_name diff --git a/SOURCES/0019-td-req-remove-unused-field-gref.patch b/SOURCES/0019-td-req-remove-unused-field-gref.patch index 880144a..636aed3 100644 --- a/SOURCES/0019-td-req-remove-unused-field-gref.patch +++ b/SOURCES/0019-td-req-remove-unused-field-gref.patch @@ -1,4 +1,4 @@ -From a4a059f2ed4b648ff06902f58d98778265423a56 Mon Sep 17 00:00:00 2001 +From d49e83a893bee7b183a697bdd4ef750c93d2ca5b Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 27 Nov 2025 11:16:51 +0100 Subject: [PATCH] td-req: remove unused field gref diff --git a/SOURCES/0020-td-req-rename-tapreq-as-req.patch b/SOURCES/0020-td-req-rename-tapreq-as-req.patch index c8e1773..70689b9 100644 --- a/SOURCES/0020-td-req-rename-tapreq-as-req.patch +++ b/SOURCES/0020-td-req-rename-tapreq-as-req.patch @@ -1,4 +1,4 @@ -From 244d446e5fc4250dbefc20f94481469dec6a16dd Mon Sep 17 00:00:00 2001 +From 2e709e20086ab1b77d57a28c436b237d42dc7abd Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Fri, 28 Nov 2025 12:45:43 +0100 Subject: [PATCH] td-req: rename tapreq as req diff --git a/SOURCES/0021-td-req-remove-old-code.patch b/SOURCES/0021-td-req-remove-old-code.patch index 4baec66..5210873 100644 --- a/SOURCES/0021-td-req-remove-old-code.patch +++ b/SOURCES/0021-td-req-remove-old-code.patch @@ -1,4 +1,4 @@ -From e5007807b3dfd1629fafa8a3952794cf3841f874 Mon Sep 17 00:00:00 2001 +From d97bbee2474337f722262e3366a86f71b6f1813e Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Fri, 28 Nov 2025 12:44:55 +0100 Subject: [PATCH] td-req: remove old code diff --git a/SOURCES/0022-td-req-fix-typo-in-a-comment.patch b/SOURCES/0022-td-req-fix-typo-in-a-comment.patch index e2b8ec2..00dda3a 100644 --- a/SOURCES/0022-td-req-fix-typo-in-a-comment.patch +++ b/SOURCES/0022-td-req-fix-typo-in-a-comment.patch @@ -1,4 +1,4 @@ -From 1660d6c774c44398512c8a84882faa1652bd001a Mon Sep 17 00:00:00 2001 +From 462b92fa2c2d64ce558c6a3dd5ce3dcf7456e09c Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Tue, 17 Mar 2026 16:52:55 +0100 Subject: [PATCH] td-req: fix typo in a comment diff --git a/SOURCES/0023-libqcow2-manage-libqcow2-sources-import.patch b/SOURCES/0023-libqcow2-manage-libqcow2-sources-import.patch index 3f9acc8..2f35d44 100644 --- a/SOURCES/0023-libqcow2-manage-libqcow2-sources-import.patch +++ b/SOURCES/0023-libqcow2-manage-libqcow2-sources-import.patch @@ -1,4 +1,4 @@ -From 464fc64b4b228412e3a0314a32ded34db4a0840a Mon Sep 17 00:00:00 2001 +From 37ddec3d3d0b08715b0a8e341a5b1238df55e4c1 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 16 Jan 2025 14:09:16 +0100 Subject: [PATCH] libqcow2: manage libqcow2 sources import @@ -10,18 +10,18 @@ sources management. Signed-off-by: Anthoine Bourgeois --- - manage-qemu-sources.sh | 74 ++++++++++ - qemu-files.lst | 309 +++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 383 insertions(+) + manage-qemu-sources.sh | 73 ++++++++++ + qemu-files.lst | 300 +++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 373 insertions(+) create mode 100755 manage-qemu-sources.sh create mode 100644 qemu-files.lst diff --git a/manage-qemu-sources.sh b/manage-qemu-sources.sh new file mode 100755 -index 00000000..7f6babc8 +index 00000000..35c1bb32 --- /dev/null +++ b/manage-qemu-sources.sh -@@ -0,0 +1,74 @@ +@@ -0,0 +1,73 @@ +#!/bin/bash + +usage_function () { @@ -37,7 +37,7 @@ index 00000000..7f6babc8 +cmd=${2} + +if [ ${cmd} != "diff" -a ${cmd} != "sync" ]; then -+ echo "Wring command: choose 'diff' or 'sync'" ++ echo "Wrong command: choose 'diff' or 'sync'" + usage_function +fi + @@ -50,8 +50,7 @@ index 00000000..7f6babc8 + echo "${dst_prefix}/${1} doesn't exist." + exit 0 + fi -+ diff -q ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1} -+ if [ $? -ne 0 ]; then ++ if ! diff -q ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1}; then + diff -Npur ${qemudir}/${src_prefix}/${1} ${dst_prefix}/${1} + fi +} @@ -63,7 +62,7 @@ index 00000000..7f6babc8 + read ok + if [ "$ok" == "y" ]; then + echo "Fine! Start in:" -+ for i in `seq 5`; do ++ for i in $(seq 5); do + echo -n "$((6 - i)) " + sleep 1 + done @@ -86,8 +85,8 @@ index 00000000..7f6babc8 + +while read f; do + if [ ${f:0:1} == '#' ]; then -+ src_prefix=`echo ${f:1} | awk -F ':' '{print $1}'` -+ dst_prefix=`echo ${f:1} | awk -F ':' '{print $2}'` ++ src_prefix=$(echo ${f:1} | awk -F ':' '{print $1}') ++ dst_prefix=$(echo ${f:1} | awk -F ':' '{print $2}') + continue + fi + if [ ${cmd} == "diff" ]; then @@ -98,10 +97,10 @@ index 00000000..7f6babc8 +done < qemu-files.lst diff --git a/qemu-files.lst b/qemu-files.lst new file mode 100644 -index 00000000..441c2180 +index 00000000..1b3e4a2c --- /dev/null +++ b/qemu-files.lst -@@ -0,0 +1,309 @@ +@@ -0,0 +1,300 @@ +#.:. +include/authz/base.h +include/block/accounting.h @@ -280,30 +279,20 @@ index 00000000..441c2180 +block/linux-aio.c +block/mirror.c +block/monitor/bitmap-qmp-cmds.c -+block/nbd.c +block/progress_meter.c +block/qapi.c +block/raw-format.c +block/snapshot.c -+crypto/hash.c -+crypto/tlscredspriv.h -+crypto/tlscreds.c -+crypto/tlssession.c +hw/block/block.c +hw/block/hd-geometry.c +io/channel.c +io/channel-file.c +io/channel-socket.c -+io/channel-tls.c +io/channel-util.c +io/channel-watch.c +io/task.c +job.c +job-qmp.c -+nbd/client.c -+nbd/client-connection.c -+nbd/common.c -+nbd/nbd-internal.h +qapi/qapi-clone-visitor.c +qapi/qapi-dealloc-visitor.c +qapi/qapi-util.c @@ -366,6 +355,7 @@ index 00000000..441c2180 +util/qemu-sockets.c +util/qemu-thread-common.h +util/qemu-thread-posix.c ++util/qemu-timer-common.c +util/qemu-timer.c +util/qsp.c +util/range.c diff --git a/SOURCES/0024-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch b/SOURCES/0024-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch index 8055a99..ea8141a 100644 --- a/SOURCES/0024-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch +++ b/SOURCES/0024-libqcow2-import-vanilla-sources-from-qemu-9.1.1.patch @@ -1,4 +1,4 @@ -From 1d37593e9ec78dba3d43b4121729206d8e7e55de Mon Sep 17 00:00:00 2001 +From 0ac816d9f87c57748214366691a73928fbab76e2 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 16 Jan 2025 14:09:22 +0100 Subject: [PATCH] libqcow2: import vanilla sources from qemu 9.1.1 @@ -182,32 +182,22 @@ Signed-off-by: Anthoine Bourgeois qcow2/lib/block/linux-aio.c | 507 + qcow2/lib/block/mirror.c | 2082 ++++ qcow2/lib/block/monitor/bitmap-qmp-cmds.c | 317 + - qcow2/lib/block/nbd.c | 2235 +++++ qcow2/lib/block/progress_meter.c | 66 + qcow2/lib/block/qapi.c | 1012 ++ qcow2/lib/block/raw-format.c | 682 ++ qcow2/lib/block/snapshot.c | 780 ++ qcow2/lib/blockdev.c | 3649 +++++++ qcow2/lib/blockjob.c | 630 ++ - qcow2/lib/crypto/hash.c | 144 + - qcow2/lib/crypto/tlscreds.c | 294 + - qcow2/lib/crypto/tlscredspriv.h | 86 + - qcow2/lib/crypto/tlssession.c | 717 ++ qcow2/lib/hw/block/block.c | 278 + qcow2/lib/hw/block/hd-geometry.c | 168 + qcow2/lib/io/channel-file.c | 324 + qcow2/lib/io/channel-socket.c | 985 ++ - qcow2/lib/io/channel-tls.c | 507 + qcow2/lib/io/channel-util.c | 62 + qcow2/lib/io/channel-watch.c | 347 + qcow2/lib/io/channel.c | 808 ++ qcow2/lib/io/task.c | 241 + qcow2/lib/job-qmp.c | 188 + qcow2/lib/job.c | 1264 +++ - qcow2/lib/nbd/client-connection.c | 422 + - qcow2/lib/nbd/client.c | 1589 ++++ - qcow2/lib/nbd/common.c | 266 + - qcow2/lib/nbd/nbd-internal.h | 77 + qcow2/lib/qapi/qapi-clone-visitor.c | 182 + qcow2/lib/qapi/qapi-dealloc-visitor.c | 143 + qcow2/lib/qapi/qapi-emit-events.h | 83 + @@ -295,6 +285,7 @@ Signed-off-by: Anthoine Bourgeois qcow2/lib/util/qemu-sockets.c | 1475 +++ qcow2/lib/util/qemu-thread-common.h | 54 + qcow2/lib/util/qemu-thread-posix.c | 686 ++ + qcow2/lib/util/qemu-timer-common.c | 63 + qcow2/lib/util/qemu-timer.c | 713 ++ qcow2/lib/util/qsp.c | 813 ++ qcow2/lib/util/range.c | 123 + @@ -310,7 +301,7 @@ Signed-off-by: Anthoine Bourgeois qcow2/qemu-img-cmds.h | 46 + qcow2/qemu-img.c | 5621 +++++++++++ qcow2/qemu-progress.c | 162 + - 302 files changed, 150383 insertions(+) + 293 files changed, 144109 insertions(+) create mode 100644 include/authz/base.h create mode 100644 include/block/accounting.h create mode 100644 include/block/aio-wait.h @@ -485,32 +476,22 @@ Signed-off-by: Anthoine Bourgeois create mode 100644 qcow2/lib/block/linux-aio.c create mode 100644 qcow2/lib/block/mirror.c create mode 100644 qcow2/lib/block/monitor/bitmap-qmp-cmds.c - create mode 100644 qcow2/lib/block/nbd.c create mode 100644 qcow2/lib/block/progress_meter.c create mode 100644 qcow2/lib/block/qapi.c create mode 100644 qcow2/lib/block/raw-format.c create mode 100644 qcow2/lib/block/snapshot.c create mode 100644 qcow2/lib/blockdev.c create mode 100644 qcow2/lib/blockjob.c - create mode 100644 qcow2/lib/crypto/hash.c - create mode 100644 qcow2/lib/crypto/tlscreds.c - create mode 100644 qcow2/lib/crypto/tlscredspriv.h - create mode 100644 qcow2/lib/crypto/tlssession.c create mode 100644 qcow2/lib/hw/block/block.c create mode 100644 qcow2/lib/hw/block/hd-geometry.c create mode 100644 qcow2/lib/io/channel-file.c create mode 100644 qcow2/lib/io/channel-socket.c - create mode 100644 qcow2/lib/io/channel-tls.c create mode 100644 qcow2/lib/io/channel-util.c create mode 100644 qcow2/lib/io/channel-watch.c create mode 100644 qcow2/lib/io/channel.c create mode 100644 qcow2/lib/io/task.c create mode 100644 qcow2/lib/job-qmp.c create mode 100644 qcow2/lib/job.c - create mode 100644 qcow2/lib/nbd/client-connection.c - create mode 100644 qcow2/lib/nbd/client.c - create mode 100644 qcow2/lib/nbd/common.c - create mode 100644 qcow2/lib/nbd/nbd-internal.h create mode 100644 qcow2/lib/qapi/qapi-clone-visitor.c create mode 100644 qcow2/lib/qapi/qapi-dealloc-visitor.c create mode 100644 qcow2/lib/qapi/qapi-emit-events.h @@ -598,6 +579,7 @@ Signed-off-by: Anthoine Bourgeois create mode 100644 qcow2/lib/util/qemu-sockets.c create mode 100644 qcow2/lib/util/qemu-thread-common.h create mode 100644 qcow2/lib/util/qemu-thread-posix.c + create mode 100644 qcow2/lib/util/qemu-timer-common.c create mode 100644 qcow2/lib/util/qemu-timer.c create mode 100644 qcow2/lib/util/qsp.c create mode 100644 qcow2/lib/util/range.c @@ -65478,2247 +65460,6 @@ index 00000000..a738e7bb +{ + block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); +} -diff --git a/qcow2/lib/block/nbd.c b/qcow2/lib/block/nbd.c -new file mode 100644 -index 00000000..2c65cdcf ---- /dev/null -+++ b/qcow2/lib/block/nbd.c -@@ -0,0 +1,2235 @@ -+/* -+ * QEMU Block driver for NBD -+ * -+ * Copyright (c) 2019 Virtuozzo International GmbH. -+ * Copyright Red Hat -+ * Copyright (C) 2008 Bull S.A.S. -+ * Author: Laurent Vivier -+ * -+ * Some parts: -+ * Copyright (C) 2007 Anthony Liguori -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+ -+#include "qemu/osdep.h" -+ -+#include "trace.h" -+#include "qemu/uri.h" -+#include "qemu/option.h" -+#include "qemu/cutils.h" -+#include "qemu/main-loop.h" -+ -+#include "qapi/qapi-visit-sockets.h" -+#include "qapi/qmp/qstring.h" -+#include "qapi/clone-visitor.h" -+ -+#include "block/qdict.h" -+#include "block/nbd.h" -+#include "block/block_int.h" -+#include "block/coroutines.h" -+ -+#include "qemu/yank.h" -+ -+#define EN_OPTSTR ":exportname=" -+#define MAX_NBD_REQUESTS 16 -+ -+#define COOKIE_TO_INDEX(cookie) ((cookie) - 1) -+#define INDEX_TO_COOKIE(index) ((index) + 1) -+ -+typedef struct { -+ Coroutine *coroutine; -+ uint64_t offset; /* original offset of the request */ -+ bool receiving; /* sleeping in the yield in nbd_receive_replies */ -+} NBDClientRequest; -+ -+typedef enum NBDClientState { -+ NBD_CLIENT_CONNECTING_WAIT, -+ NBD_CLIENT_CONNECTING_NOWAIT, -+ NBD_CLIENT_CONNECTED, -+ NBD_CLIENT_QUIT -+} NBDClientState; -+ -+typedef struct BDRVNBDState { -+ QIOChannel *ioc; /* The current I/O channel */ -+ NBDExportInfo info; -+ -+ /* -+ * Protects state, free_sema, in_flight, requests[].coroutine, -+ * reconnect_delay_timer. -+ */ -+ QemuMutex requests_lock; -+ NBDClientState state; -+ CoQueue free_sema; -+ unsigned in_flight; -+ NBDClientRequest requests[MAX_NBD_REQUESTS]; -+ QEMUTimer *reconnect_delay_timer; -+ -+ /* Protects sending data on the socket. */ -+ CoMutex send_mutex; -+ -+ /* -+ * Protects receiving reply headers from the socket, as well as the -+ * fields reply and requests[].receiving -+ */ -+ CoMutex receive_mutex; -+ NBDReply reply; -+ -+ QEMUTimer *open_timer; -+ -+ BlockDriverState *bs; -+ -+ /* Connection parameters */ -+ uint32_t reconnect_delay; -+ uint32_t open_timeout; -+ SocketAddress *saddr; -+ char *export; -+ char *tlscredsid; -+ QCryptoTLSCreds *tlscreds; -+ char *tlshostname; -+ char *x_dirty_bitmap; -+ bool alloc_depth; -+ -+ NBDClientConnection *conn; -+} BDRVNBDState; -+ -+static void nbd_yank(void *opaque); -+ -+static void nbd_clear_bdrvstate(BlockDriverState *bs) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ -+ nbd_client_connection_release(s->conn); -+ s->conn = NULL; -+ -+ yank_unregister_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name)); -+ -+ /* Must not leave timers behind that would access freed data */ -+ assert(!s->reconnect_delay_timer); -+ assert(!s->open_timer); -+ -+ object_unref(OBJECT(s->tlscreds)); -+ qapi_free_SocketAddress(s->saddr); -+ s->saddr = NULL; -+ g_free(s->export); -+ s->export = NULL; -+ g_free(s->tlscredsid); -+ s->tlscredsid = NULL; -+ g_free(s->tlshostname); -+ s->tlshostname = NULL; -+ g_free(s->x_dirty_bitmap); -+ s->x_dirty_bitmap = NULL; -+} -+ -+/* Called with s->receive_mutex taken. */ -+static bool coroutine_fn nbd_recv_coroutine_wake_one(NBDClientRequest *req) -+{ -+ if (req->receiving) { -+ req->receiving = false; -+ aio_co_wake(req->coroutine); -+ return true; -+ } -+ -+ return false; -+} -+ -+static void coroutine_fn nbd_recv_coroutines_wake(BDRVNBDState *s) -+{ -+ int i; -+ -+ QEMU_LOCK_GUARD(&s->receive_mutex); -+ for (i = 0; i < MAX_NBD_REQUESTS; i++) { -+ if (nbd_recv_coroutine_wake_one(&s->requests[i])) { -+ return; -+ } -+ } -+} -+ -+/* Called with s->requests_lock held. */ -+static void coroutine_fn nbd_channel_error_locked(BDRVNBDState *s, int ret) -+{ -+ if (s->state == NBD_CLIENT_CONNECTED) { -+ qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); -+ } -+ -+ if (ret == -EIO) { -+ if (s->state == NBD_CLIENT_CONNECTED) { -+ s->state = s->reconnect_delay ? NBD_CLIENT_CONNECTING_WAIT : -+ NBD_CLIENT_CONNECTING_NOWAIT; -+ } -+ } else { -+ s->state = NBD_CLIENT_QUIT; -+ } -+} -+ -+static void coroutine_fn nbd_channel_error(BDRVNBDState *s, int ret) -+{ -+ QEMU_LOCK_GUARD(&s->requests_lock); -+ nbd_channel_error_locked(s, ret); -+} -+ -+static void reconnect_delay_timer_del(BDRVNBDState *s) -+{ -+ if (s->reconnect_delay_timer) { -+ timer_free(s->reconnect_delay_timer); -+ s->reconnect_delay_timer = NULL; -+ } -+} -+ -+static void reconnect_delay_timer_cb(void *opaque) -+{ -+ BDRVNBDState *s = opaque; -+ -+ reconnect_delay_timer_del(s); -+ WITH_QEMU_LOCK_GUARD(&s->requests_lock) { -+ if (s->state != NBD_CLIENT_CONNECTING_WAIT) { -+ return; -+ } -+ s->state = NBD_CLIENT_CONNECTING_NOWAIT; -+ } -+ nbd_co_establish_connection_cancel(s->conn); -+} -+ -+static void reconnect_delay_timer_init(BDRVNBDState *s, uint64_t expire_time_ns) -+{ -+ assert(!s->reconnect_delay_timer); -+ s->reconnect_delay_timer = aio_timer_new(bdrv_get_aio_context(s->bs), -+ QEMU_CLOCK_REALTIME, -+ SCALE_NS, -+ reconnect_delay_timer_cb, s); -+ timer_mod(s->reconnect_delay_timer, expire_time_ns); -+} -+ -+static void nbd_teardown_connection(BlockDriverState *bs) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ -+ assert(!s->in_flight); -+ -+ if (s->ioc) { -+ qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); -+ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), -+ nbd_yank, s->bs); -+ object_unref(OBJECT(s->ioc)); -+ s->ioc = NULL; -+ } -+ -+ WITH_QEMU_LOCK_GUARD(&s->requests_lock) { -+ s->state = NBD_CLIENT_QUIT; -+ } -+} -+ -+static void open_timer_del(BDRVNBDState *s) -+{ -+ if (s->open_timer) { -+ timer_free(s->open_timer); -+ s->open_timer = NULL; -+ } -+} -+ -+static void open_timer_cb(void *opaque) -+{ -+ BDRVNBDState *s = opaque; -+ -+ nbd_co_establish_connection_cancel(s->conn); -+ open_timer_del(s); -+} -+ -+static void open_timer_init(BDRVNBDState *s, uint64_t expire_time_ns) -+{ -+ assert(!s->open_timer); -+ s->open_timer = aio_timer_new(bdrv_get_aio_context(s->bs), -+ QEMU_CLOCK_REALTIME, -+ SCALE_NS, -+ open_timer_cb, s); -+ timer_mod(s->open_timer, expire_time_ns); -+} -+ -+static bool nbd_client_will_reconnect(BDRVNBDState *s) -+{ -+ /* -+ * Called only after a socket error, so this is not performance sensitive. -+ */ -+ QEMU_LOCK_GUARD(&s->requests_lock); -+ return s->state == NBD_CLIENT_CONNECTING_WAIT; -+} -+ -+/* -+ * Update @bs with information learned during a completed negotiation process. -+ * Return failure if the server's advertised options are incompatible with the -+ * client's needs. -+ */ -+static int coroutine_fn GRAPH_RDLOCK -+nbd_handle_updated_info(BlockDriverState *bs, Error **errp) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ int ret; -+ -+ if (s->x_dirty_bitmap) { -+ if (!s->info.base_allocation) { -+ error_setg(errp, "requested x-dirty-bitmap %s not found", -+ s->x_dirty_bitmap); -+ return -EINVAL; -+ } -+ if (strcmp(s->x_dirty_bitmap, "qemu:allocation-depth") == 0) { -+ s->alloc_depth = true; -+ } -+ } -+ -+ if (s->info.flags & NBD_FLAG_READ_ONLY) { -+ ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp); -+ if (ret < 0) { -+ return ret; -+ } -+ } -+ -+ if (s->info.flags & NBD_FLAG_SEND_FUA) { -+ bs->supported_write_flags = BDRV_REQ_FUA; -+ bs->supported_zero_flags |= BDRV_REQ_FUA; -+ } -+ -+ if (s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES) { -+ bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; -+ if (s->info.flags & NBD_FLAG_SEND_FAST_ZERO) { -+ bs->supported_zero_flags |= BDRV_REQ_NO_FALLBACK; -+ } -+ } -+ -+ trace_nbd_client_handshake_success(s->export); -+ -+ return 0; -+} -+ -+int coroutine_fn nbd_co_do_establish_connection(BlockDriverState *bs, -+ bool blocking, Error **errp) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ int ret; -+ IO_CODE(); -+ -+ assert_bdrv_graph_readable(); -+ assert(!s->ioc); -+ -+ s->ioc = nbd_co_establish_connection(s->conn, &s->info, blocking, errp); -+ if (!s->ioc) { -+ return -ECONNREFUSED; -+ } -+ -+ yank_register_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), nbd_yank, -+ bs); -+ -+ ret = nbd_handle_updated_info(s->bs, NULL); -+ if (ret < 0) { -+ /* -+ * We have connected, but must fail for other reasons. -+ * Send NBD_CMD_DISC as a courtesy to the server. -+ */ -+ NBDRequest request = { .type = NBD_CMD_DISC, .mode = s->info.mode }; -+ -+ nbd_send_request(s->ioc, &request); -+ -+ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), -+ nbd_yank, bs); -+ object_unref(OBJECT(s->ioc)); -+ s->ioc = NULL; -+ -+ return ret; -+ } -+ -+ qio_channel_set_blocking(s->ioc, false, NULL); -+ qio_channel_set_follow_coroutine_ctx(s->ioc, true); -+ -+ /* successfully connected */ -+ WITH_QEMU_LOCK_GUARD(&s->requests_lock) { -+ s->state = NBD_CLIENT_CONNECTED; -+ } -+ -+ return 0; -+} -+ -+/* Called with s->requests_lock held. */ -+static bool nbd_client_connecting(BDRVNBDState *s) -+{ -+ return s->state == NBD_CLIENT_CONNECTING_WAIT || -+ s->state == NBD_CLIENT_CONNECTING_NOWAIT; -+} -+ -+/* Called with s->requests_lock taken. */ -+static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) -+{ -+ int ret; -+ bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT; -+ -+ /* -+ * Now we are sure that nobody is accessing the channel, and no one will -+ * try until we set the state to CONNECTED. -+ */ -+ assert(nbd_client_connecting(s)); -+ assert(s->in_flight == 1); -+ -+ trace_nbd_reconnect_attempt(s->bs->in_flight); -+ -+ if (blocking && !s->reconnect_delay_timer) { -+ /* -+ * It's the first reconnect attempt after switching to -+ * NBD_CLIENT_CONNECTING_WAIT -+ */ -+ g_assert(s->reconnect_delay); -+ reconnect_delay_timer_init(s, -+ qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + -+ s->reconnect_delay * NANOSECONDS_PER_SECOND); -+ } -+ -+ /* Finalize previous connection if any */ -+ if (s->ioc) { -+ yank_unregister_function(BLOCKDEV_YANK_INSTANCE(s->bs->node_name), -+ nbd_yank, s->bs); -+ object_unref(OBJECT(s->ioc)); -+ s->ioc = NULL; -+ } -+ -+ qemu_mutex_unlock(&s->requests_lock); -+ ret = nbd_co_do_establish_connection(s->bs, blocking, NULL); -+ trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight); -+ qemu_mutex_lock(&s->requests_lock); -+ -+ /* -+ * The reconnect attempt is done (maybe successfully, maybe not), so -+ * we no longer need this timer. Delete it so it will not outlive -+ * this I/O request (so draining removes all timers). -+ */ -+ reconnect_delay_timer_del(s); -+} -+ -+static coroutine_fn int nbd_receive_replies(BDRVNBDState *s, uint64_t cookie, -+ Error **errp) -+{ -+ int ret; -+ uint64_t ind = COOKIE_TO_INDEX(cookie), ind2; -+ QEMU_LOCK_GUARD(&s->receive_mutex); -+ -+ while (true) { -+ if (s->reply.cookie == cookie) { -+ /* We are done */ -+ return 0; -+ } -+ -+ if (s->reply.cookie != 0) { -+ /* -+ * Some other request is being handled now. It should already be -+ * woken by whoever set s->reply.cookie (or never wait in this -+ * yield). So, we should not wake it here. -+ */ -+ ind2 = COOKIE_TO_INDEX(s->reply.cookie); -+ assert(!s->requests[ind2].receiving); -+ -+ s->requests[ind].receiving = true; -+ qemu_co_mutex_unlock(&s->receive_mutex); -+ -+ qemu_coroutine_yield(); -+ /* -+ * We may be woken for 2 reasons: -+ * 1. From this function, executing in parallel coroutine, when our -+ * cookie is received. -+ * 2. From nbd_co_receive_one_chunk(), when previous request is -+ * finished and s->reply.cookie set to 0. -+ * Anyway, it's OK to lock the mutex and go to the next iteration. -+ */ -+ -+ qemu_co_mutex_lock(&s->receive_mutex); -+ assert(!s->requests[ind].receiving); -+ continue; -+ } -+ -+ /* We are under mutex and cookie is 0. We have to do the dirty work. */ -+ assert(s->reply.cookie == 0); -+ ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, s->info.mode, errp); -+ if (ret == 0) { -+ ret = -EIO; -+ error_setg(errp, "server dropped connection"); -+ } -+ if (ret < 0) { -+ nbd_channel_error(s, ret); -+ return ret; -+ } -+ if (nbd_reply_is_structured(&s->reply) && -+ s->info.mode < NBD_MODE_STRUCTURED) { -+ nbd_channel_error(s, -EINVAL); -+ error_setg(errp, "unexpected structured reply"); -+ return -EINVAL; -+ } -+ ind2 = COOKIE_TO_INDEX(s->reply.cookie); -+ if (ind2 >= MAX_NBD_REQUESTS || !s->requests[ind2].coroutine) { -+ nbd_channel_error(s, -EINVAL); -+ error_setg(errp, "unexpected cookie value"); -+ return -EINVAL; -+ } -+ if (s->reply.cookie == cookie) { -+ /* We are done */ -+ return 0; -+ } -+ nbd_recv_coroutine_wake_one(&s->requests[ind2]); -+ } -+} -+ -+static int coroutine_fn GRAPH_RDLOCK -+nbd_co_send_request(BlockDriverState *bs, NBDRequest *request, -+ QEMUIOVector *qiov) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ int rc, i = -1; -+ -+ qemu_mutex_lock(&s->requests_lock); -+ while (s->in_flight == MAX_NBD_REQUESTS || -+ (s->state != NBD_CLIENT_CONNECTED && s->in_flight > 0)) { -+ qemu_co_queue_wait(&s->free_sema, &s->requests_lock); -+ } -+ -+ s->in_flight++; -+ if (s->state != NBD_CLIENT_CONNECTED) { -+ if (nbd_client_connecting(s)) { -+ nbd_reconnect_attempt(s); -+ qemu_co_queue_restart_all(&s->free_sema); -+ } -+ if (s->state != NBD_CLIENT_CONNECTED) { -+ rc = -EIO; -+ goto err; -+ } -+ } -+ -+ for (i = 0; i < MAX_NBD_REQUESTS; i++) { -+ if (s->requests[i].coroutine == NULL) { -+ break; -+ } -+ } -+ -+ assert(i < MAX_NBD_REQUESTS); -+ s->requests[i].coroutine = qemu_coroutine_self(); -+ s->requests[i].offset = request->from; -+ s->requests[i].receiving = false; -+ qemu_mutex_unlock(&s->requests_lock); -+ -+ qemu_co_mutex_lock(&s->send_mutex); -+ request->cookie = INDEX_TO_COOKIE(i); -+ request->mode = s->info.mode; -+ -+ assert(s->ioc); -+ -+ if (qiov) { -+ qio_channel_set_cork(s->ioc, true); -+ rc = nbd_send_request(s->ioc, request); -+ if (rc >= 0 && qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, -+ NULL) < 0) { -+ rc = -EIO; -+ } -+ qio_channel_set_cork(s->ioc, false); -+ } else { -+ rc = nbd_send_request(s->ioc, request); -+ } -+ qemu_co_mutex_unlock(&s->send_mutex); -+ -+ if (rc < 0) { -+ qemu_mutex_lock(&s->requests_lock); -+err: -+ nbd_channel_error_locked(s, rc); -+ if (i != -1) { -+ s->requests[i].coroutine = NULL; -+ } -+ s->in_flight--; -+ qemu_co_queue_next(&s->free_sema); -+ qemu_mutex_unlock(&s->requests_lock); -+ } -+ return rc; -+} -+ -+static inline uint16_t payload_advance16(uint8_t **payload) -+{ -+ *payload += 2; -+ return lduw_be_p(*payload - 2); -+} -+ -+static inline uint32_t payload_advance32(uint8_t **payload) -+{ -+ *payload += 4; -+ return ldl_be_p(*payload - 4); -+} -+ -+static inline uint64_t payload_advance64(uint8_t **payload) -+{ -+ *payload += 8; -+ return ldq_be_p(*payload - 8); -+} -+ -+static int nbd_parse_offset_hole_payload(BDRVNBDState *s, -+ NBDStructuredReplyChunk *chunk, -+ uint8_t *payload, uint64_t orig_offset, -+ QEMUIOVector *qiov, Error **errp) -+{ -+ uint64_t offset; -+ uint32_t hole_size; -+ -+ if (chunk->length != sizeof(offset) + sizeof(hole_size)) { -+ error_setg(errp, "Protocol error: invalid payload for " -+ "NBD_REPLY_TYPE_OFFSET_HOLE"); -+ return -EINVAL; -+ } -+ -+ offset = payload_advance64(&payload); -+ hole_size = payload_advance32(&payload); -+ -+ if (!hole_size || offset < orig_offset || hole_size > qiov->size || -+ offset > orig_offset + qiov->size - hole_size) { -+ error_setg(errp, "Protocol error: server sent chunk exceeding requested" -+ " region"); -+ return -EINVAL; -+ } -+ if (s->info.min_block && -+ !QEMU_IS_ALIGNED(hole_size, s->info.min_block)) { -+ trace_nbd_structured_read_compliance("hole"); -+ } -+ -+ qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size); -+ -+ return 0; -+} -+ -+/* -+ * nbd_parse_blockstatus_payload -+ * Based on our request, we expect only one extent in reply, for the -+ * base:allocation context. -+ */ -+static int nbd_parse_blockstatus_payload(BDRVNBDState *s, -+ NBDStructuredReplyChunk *chunk, -+ uint8_t *payload, bool wide, -+ uint64_t orig_length, -+ NBDExtent64 *extent, Error **errp) -+{ -+ uint32_t context_id; -+ uint32_t count; -+ size_t ext_len = wide ? sizeof(*extent) : sizeof(NBDExtent32); -+ size_t pay_len = sizeof(context_id) + wide * sizeof(count) + ext_len; -+ -+ /* The server succeeded, so it must have sent [at least] one extent */ -+ if (chunk->length < pay_len) { -+ error_setg(errp, "Protocol error: invalid payload for " -+ "NBD_REPLY_TYPE_BLOCK_STATUS"); -+ return -EINVAL; -+ } -+ -+ context_id = payload_advance32(&payload); -+ if (s->info.context_id != context_id) { -+ error_setg(errp, "Protocol error: unexpected context id %d for " -+ "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context " -+ "id is %d", context_id, -+ s->info.context_id); -+ return -EINVAL; -+ } -+ -+ if (wide) { -+ count = payload_advance32(&payload); -+ extent->length = payload_advance64(&payload); -+ extent->flags = payload_advance64(&payload); -+ } else { -+ count = 0; -+ extent->length = payload_advance32(&payload); -+ extent->flags = payload_advance32(&payload); -+ } -+ -+ if (extent->length == 0) { -+ error_setg(errp, "Protocol error: server sent status chunk with " -+ "zero length"); -+ return -EINVAL; -+ } -+ -+ /* -+ * A server sending unaligned block status is in violation of the -+ * protocol, but as qemu-nbd 3.1 is such a server (at least for -+ * POSIX files that are not a multiple of 512 bytes, since qemu -+ * rounds files up to 512-byte multiples but lseek(SEEK_HOLE) -+ * still sees an implicit hole beyond the real EOF), it's nicer to -+ * work around the misbehaving server. If the request included -+ * more than the final unaligned block, truncate it back to an -+ * aligned result; if the request was only the final block, round -+ * up to the full block and change the status to fully-allocated -+ * (always a safe status, even if it loses information). -+ */ -+ if (s->info.min_block && !QEMU_IS_ALIGNED(extent->length, -+ s->info.min_block)) { -+ trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); -+ if (extent->length > s->info.min_block) { -+ extent->length = QEMU_ALIGN_DOWN(extent->length, -+ s->info.min_block); -+ } else { -+ extent->length = s->info.min_block; -+ extent->flags = 0; -+ } -+ } -+ -+ /* -+ * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have -+ * sent us any more than one extent, nor should it have included -+ * status beyond our request in that extent. Furthermore, a wide -+ * server should have replied with an accurate count (we left -+ * count at 0 for a narrow server). However, it's easy enough to -+ * ignore the server's noncompliance without killing the -+ * connection; just ignore trailing extents, and clamp things to -+ * the length of our request. -+ */ -+ if (count != wide || chunk->length > pay_len) { -+ trace_nbd_parse_blockstatus_compliance("unexpected extent count"); -+ } -+ if (extent->length > orig_length) { -+ extent->length = orig_length; -+ trace_nbd_parse_blockstatus_compliance("extent length too large"); -+ } -+ -+ /* -+ * HACK: if we are using x-dirty-bitmaps to access -+ * qemu:allocation-depth, treat all depths > 2 the same as 2, -+ * since nbd_client_co_block_status is only expecting the low two -+ * bits to be set. -+ */ -+ if (s->alloc_depth && extent->flags > 2) { -+ extent->flags = 2; -+ } -+ -+ return 0; -+} -+ -+/* -+ * nbd_parse_error_payload -+ * on success @errp contains message describing nbd error reply -+ */ -+static int nbd_parse_error_payload(NBDStructuredReplyChunk *chunk, -+ uint8_t *payload, int *request_ret, -+ Error **errp) -+{ -+ uint32_t error; -+ uint16_t message_size; -+ -+ assert(chunk->type & (1 << 15)); -+ -+ if (chunk->length < sizeof(error) + sizeof(message_size)) { -+ error_setg(errp, -+ "Protocol error: invalid payload for structured error"); -+ return -EINVAL; -+ } -+ -+ error = nbd_errno_to_system_errno(payload_advance32(&payload)); -+ if (error == 0) { -+ error_setg(errp, "Protocol error: server sent structured error chunk " -+ "with error = 0"); -+ return -EINVAL; -+ } -+ -+ *request_ret = -error; -+ message_size = payload_advance16(&payload); -+ -+ if (message_size > chunk->length - sizeof(error) - sizeof(message_size)) { -+ error_setg(errp, "Protocol error: server sent structured error chunk " -+ "with incorrect message size"); -+ return -EINVAL; -+ } -+ -+ /* TODO: Add a trace point to mention the server complaint */ -+ -+ /* TODO handle ERROR_OFFSET */ -+ -+ return 0; -+} -+ -+static int coroutine_fn -+nbd_co_receive_offset_data_payload(BDRVNBDState *s, uint64_t orig_offset, -+ QEMUIOVector *qiov, Error **errp) -+{ -+ QEMUIOVector sub_qiov; -+ uint64_t offset; -+ size_t data_size; -+ int ret; -+ NBDStructuredReplyChunk *chunk = &s->reply.structured; -+ -+ assert(nbd_reply_is_structured(&s->reply)); -+ -+ /* The NBD spec requires at least one byte of payload */ -+ if (chunk->length <= sizeof(offset)) { -+ error_setg(errp, "Protocol error: invalid payload for " -+ "NBD_REPLY_TYPE_OFFSET_DATA"); -+ return -EINVAL; -+ } -+ -+ if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) { -+ return -EIO; -+ } -+ -+ data_size = chunk->length - sizeof(offset); -+ assert(data_size); -+ if (offset < orig_offset || data_size > qiov->size || -+ offset > orig_offset + qiov->size - data_size) { -+ error_setg(errp, "Protocol error: server sent chunk exceeding requested" -+ " region"); -+ return -EINVAL; -+ } -+ if (s->info.min_block && !QEMU_IS_ALIGNED(data_size, s->info.min_block)) { -+ trace_nbd_structured_read_compliance("data"); -+ } -+ -+ qemu_iovec_init(&sub_qiov, qiov->niov); -+ qemu_iovec_concat(&sub_qiov, qiov, offset - orig_offset, data_size); -+ ret = qio_channel_readv_all(s->ioc, sub_qiov.iov, sub_qiov.niov, errp); -+ qemu_iovec_destroy(&sub_qiov); -+ -+ return ret < 0 ? -EIO : 0; -+} -+ -+#define NBD_MAX_MALLOC_PAYLOAD 1000 -+static coroutine_fn int nbd_co_receive_structured_payload( -+ BDRVNBDState *s, void **payload, Error **errp) -+{ -+ int ret; -+ uint32_t len; -+ -+ assert(nbd_reply_is_structured(&s->reply)); -+ -+ len = s->reply.structured.length; -+ -+ if (len == 0) { -+ return 0; -+ } -+ -+ if (payload == NULL) { -+ error_setg(errp, "Unexpected structured payload"); -+ return -EINVAL; -+ } -+ -+ if (len > NBD_MAX_MALLOC_PAYLOAD) { -+ error_setg(errp, "Payload too large"); -+ return -EINVAL; -+ } -+ -+ *payload = g_new(char, len); -+ ret = nbd_read(s->ioc, *payload, len, "structured payload", errp); -+ if (ret < 0) { -+ g_free(*payload); -+ *payload = NULL; -+ return ret; -+ } -+ -+ return 0; -+} -+ -+/* -+ * nbd_co_do_receive_one_chunk -+ * for simple reply: -+ * set request_ret to received reply error -+ * if qiov is not NULL: read payload to @qiov -+ * for structured reply chunk: -+ * if error chunk: read payload, set @request_ret, do not set @payload -+ * else if offset_data chunk: read payload data to @qiov, do not set @payload -+ * else: read payload to @payload -+ * -+ * If function fails, @errp contains corresponding error message, and the -+ * connection with the server is suspect. If it returns 0, then the -+ * transaction succeeded (although @request_ret may be a negative errno -+ * corresponding to the server's error reply), and errp is unchanged. -+ */ -+static coroutine_fn int nbd_co_do_receive_one_chunk( -+ BDRVNBDState *s, uint64_t cookie, bool only_structured, -+ int *request_ret, QEMUIOVector *qiov, void **payload, Error **errp) -+{ -+ ERRP_GUARD(); -+ int ret; -+ int i = COOKIE_TO_INDEX(cookie); -+ void *local_payload = NULL; -+ NBDStructuredReplyChunk *chunk; -+ -+ if (payload) { -+ *payload = NULL; -+ } -+ *request_ret = 0; -+ -+ ret = nbd_receive_replies(s, cookie, errp); -+ if (ret < 0) { -+ error_prepend(errp, "Connection closed: "); -+ return -EIO; -+ } -+ assert(s->ioc); -+ -+ assert(s->reply.cookie == cookie); -+ -+ if (nbd_reply_is_simple(&s->reply)) { -+ if (only_structured) { -+ error_setg(errp, "Protocol error: simple reply when structured " -+ "reply chunk was expected"); -+ return -EINVAL; -+ } -+ -+ *request_ret = -nbd_errno_to_system_errno(s->reply.simple.error); -+ if (*request_ret < 0 || !qiov) { -+ return 0; -+ } -+ -+ return qio_channel_readv_all(s->ioc, qiov->iov, qiov->niov, -+ errp) < 0 ? -EIO : 0; -+ } -+ -+ /* handle structured reply chunk */ -+ assert(s->info.mode >= NBD_MODE_STRUCTURED); -+ chunk = &s->reply.structured; -+ -+ if (chunk->type == NBD_REPLY_TYPE_NONE) { -+ if (!(chunk->flags & NBD_REPLY_FLAG_DONE)) { -+ error_setg(errp, "Protocol error: NBD_REPLY_TYPE_NONE chunk without" -+ " NBD_REPLY_FLAG_DONE flag set"); -+ return -EINVAL; -+ } -+ if (chunk->length) { -+ error_setg(errp, "Protocol error: NBD_REPLY_TYPE_NONE chunk with" -+ " nonzero length"); -+ return -EINVAL; -+ } -+ return 0; -+ } -+ -+ if (chunk->type == NBD_REPLY_TYPE_OFFSET_DATA) { -+ if (!qiov) { -+ error_setg(errp, "Unexpected NBD_REPLY_TYPE_OFFSET_DATA chunk"); -+ return -EINVAL; -+ } -+ -+ return nbd_co_receive_offset_data_payload(s, s->requests[i].offset, -+ qiov, errp); -+ } -+ -+ if (nbd_reply_type_is_error(chunk->type)) { -+ payload = &local_payload; -+ } -+ -+ ret = nbd_co_receive_structured_payload(s, payload, errp); -+ if (ret < 0) { -+ return ret; -+ } -+ -+ if (nbd_reply_type_is_error(chunk->type)) { -+ ret = nbd_parse_error_payload(chunk, local_payload, request_ret, errp); -+ g_free(local_payload); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+/* -+ * nbd_co_receive_one_chunk -+ * Read reply, wake up connection_co and set s->quit if needed. -+ * Return value is a fatal error code or normal nbd reply error code -+ */ -+static coroutine_fn int nbd_co_receive_one_chunk( -+ BDRVNBDState *s, uint64_t cookie, bool only_structured, -+ int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload, -+ Error **errp) -+{ -+ int ret = nbd_co_do_receive_one_chunk(s, cookie, only_structured, -+ request_ret, qiov, payload, errp); -+ -+ if (ret < 0) { -+ memset(reply, 0, sizeof(*reply)); -+ nbd_channel_error(s, ret); -+ } else { -+ /* For assert at loop start in nbd_connection_entry */ -+ *reply = s->reply; -+ } -+ s->reply.cookie = 0; -+ -+ nbd_recv_coroutines_wake(s); -+ -+ return ret; -+} -+ -+typedef struct NBDReplyChunkIter { -+ int ret; -+ int request_ret; -+ Error *err; -+ bool done, only_structured; -+} NBDReplyChunkIter; -+ -+static void nbd_iter_channel_error(NBDReplyChunkIter *iter, -+ int ret, Error **local_err) -+{ -+ assert(local_err && *local_err); -+ assert(ret < 0); -+ -+ if (!iter->ret) { -+ iter->ret = ret; -+ error_propagate(&iter->err, *local_err); -+ } else { -+ error_free(*local_err); -+ } -+ -+ *local_err = NULL; -+} -+ -+static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret) -+{ -+ assert(ret < 0); -+ -+ if (!iter->request_ret) { -+ iter->request_ret = ret; -+ } -+} -+ -+/* -+ * NBD_FOREACH_REPLY_CHUNK -+ * The pointer stored in @payload requires g_free() to free it. -+ */ -+#define NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, structured, \ -+ qiov, reply, payload) \ -+ for (iter = (NBDReplyChunkIter) { .only_structured = structured }; \ -+ nbd_reply_chunk_iter_receive(s, &iter, cookie, qiov, reply, payload);) -+ -+/* -+ * nbd_reply_chunk_iter_receive -+ * The pointer stored in @payload requires g_free() to free it. -+ */ -+static bool coroutine_fn nbd_reply_chunk_iter_receive(BDRVNBDState *s, -+ NBDReplyChunkIter *iter, -+ uint64_t cookie, -+ QEMUIOVector *qiov, -+ NBDReply *reply, -+ void **payload) -+{ -+ int ret, request_ret; -+ NBDReply local_reply; -+ NBDStructuredReplyChunk *chunk; -+ Error *local_err = NULL; -+ -+ if (iter->done) { -+ /* Previous iteration was last. */ -+ goto break_loop; -+ } -+ -+ if (reply == NULL) { -+ reply = &local_reply; -+ } -+ -+ ret = nbd_co_receive_one_chunk(s, cookie, iter->only_structured, -+ &request_ret, qiov, reply, payload, -+ &local_err); -+ if (ret < 0) { -+ nbd_iter_channel_error(iter, ret, &local_err); -+ } else if (request_ret < 0) { -+ nbd_iter_request_error(iter, request_ret); -+ } -+ -+ /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ -+ if (nbd_reply_is_simple(reply) || iter->ret < 0) { -+ goto break_loop; -+ } -+ -+ chunk = &reply->structured; -+ iter->only_structured = true; -+ -+ if (chunk->type == NBD_REPLY_TYPE_NONE) { -+ /* NBD_REPLY_FLAG_DONE is already checked in nbd_co_receive_one_chunk */ -+ assert(chunk->flags & NBD_REPLY_FLAG_DONE); -+ goto break_loop; -+ } -+ -+ if (chunk->flags & NBD_REPLY_FLAG_DONE) { -+ /* This iteration is last. */ -+ iter->done = true; -+ } -+ -+ /* Execute the loop body */ -+ return true; -+ -+break_loop: -+ qemu_mutex_lock(&s->requests_lock); -+ s->requests[COOKIE_TO_INDEX(cookie)].coroutine = NULL; -+ s->in_flight--; -+ qemu_co_queue_next(&s->free_sema); -+ qemu_mutex_unlock(&s->requests_lock); -+ -+ return false; -+} -+ -+static int coroutine_fn -+nbd_co_receive_return_code(BDRVNBDState *s, uint64_t cookie, -+ int *request_ret, Error **errp) -+{ -+ NBDReplyChunkIter iter; -+ -+ NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, false, NULL, NULL, NULL) { -+ /* nbd_reply_chunk_iter_receive does all the work */ -+ } -+ -+ error_propagate(errp, iter.err); -+ *request_ret = iter.request_ret; -+ return iter.ret; -+} -+ -+static int coroutine_fn -+nbd_co_receive_cmdread_reply(BDRVNBDState *s, uint64_t cookie, -+ uint64_t offset, QEMUIOVector *qiov, -+ int *request_ret, Error **errp) -+{ -+ NBDReplyChunkIter iter; -+ NBDReply reply; -+ void *payload = NULL; -+ Error *local_err = NULL; -+ -+ NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, -+ s->info.mode >= NBD_MODE_STRUCTURED, -+ qiov, &reply, &payload) -+ { -+ int ret; -+ NBDStructuredReplyChunk *chunk = &reply.structured; -+ -+ assert(nbd_reply_is_structured(&reply)); -+ -+ switch (chunk->type) { -+ case NBD_REPLY_TYPE_OFFSET_DATA: -+ /* -+ * special cased in nbd_co_receive_one_chunk, data is already -+ * in qiov -+ */ -+ break; -+ case NBD_REPLY_TYPE_OFFSET_HOLE: -+ ret = nbd_parse_offset_hole_payload(s, &reply.structured, payload, -+ offset, qiov, &local_err); -+ if (ret < 0) { -+ nbd_channel_error(s, ret); -+ nbd_iter_channel_error(&iter, ret, &local_err); -+ } -+ break; -+ default: -+ if (!nbd_reply_type_is_error(chunk->type)) { -+ /* not allowed reply type */ -+ nbd_channel_error(s, -EINVAL); -+ error_setg(&local_err, -+ "Unexpected reply type: %d (%s) for CMD_READ", -+ chunk->type, nbd_reply_type_lookup(chunk->type)); -+ nbd_iter_channel_error(&iter, -EINVAL, &local_err); -+ } -+ } -+ -+ g_free(payload); -+ payload = NULL; -+ } -+ -+ error_propagate(errp, iter.err); -+ *request_ret = iter.request_ret; -+ return iter.ret; -+} -+ -+static int coroutine_fn -+nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, -+ uint64_t length, NBDExtent64 *extent, -+ int *request_ret, Error **errp) -+{ -+ NBDReplyChunkIter iter; -+ NBDReply reply; -+ void *payload = NULL; -+ Error *local_err = NULL; -+ bool received = false; -+ -+ assert(!extent->length); -+ NBD_FOREACH_REPLY_CHUNK(s, iter, cookie, false, NULL, &reply, &payload) { -+ int ret; -+ NBDStructuredReplyChunk *chunk = &reply.structured; -+ bool wide; -+ -+ assert(nbd_reply_is_structured(&reply)); -+ -+ switch (chunk->type) { -+ case NBD_REPLY_TYPE_BLOCK_STATUS_EXT: -+ case NBD_REPLY_TYPE_BLOCK_STATUS: -+ wide = chunk->type == NBD_REPLY_TYPE_BLOCK_STATUS_EXT; -+ if ((s->info.mode >= NBD_MODE_EXTENDED) != wide) { -+ trace_nbd_extended_headers_compliance("block_status"); -+ } -+ if (received) { -+ nbd_channel_error(s, -EINVAL); -+ error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); -+ nbd_iter_channel_error(&iter, -EINVAL, &local_err); -+ } -+ received = true; -+ -+ ret = nbd_parse_blockstatus_payload( -+ s, &reply.structured, payload, wide, -+ length, extent, &local_err); -+ if (ret < 0) { -+ nbd_channel_error(s, ret); -+ nbd_iter_channel_error(&iter, ret, &local_err); -+ } -+ break; -+ default: -+ if (!nbd_reply_type_is_error(chunk->type)) { -+ nbd_channel_error(s, -EINVAL); -+ error_setg(&local_err, -+ "Unexpected reply type: %d (%s) " -+ "for CMD_BLOCK_STATUS", -+ chunk->type, nbd_reply_type_lookup(chunk->type)); -+ nbd_iter_channel_error(&iter, -EINVAL, &local_err); -+ } -+ } -+ -+ g_free(payload); -+ payload = NULL; -+ } -+ -+ if (!extent->length && !iter.request_ret) { -+ error_setg(&local_err, "Server did not reply with any status extents"); -+ nbd_iter_channel_error(&iter, -EIO, &local_err); -+ } -+ -+ error_propagate(errp, iter.err); -+ *request_ret = iter.request_ret; -+ return iter.ret; -+} -+ -+static int coroutine_fn GRAPH_RDLOCK -+nbd_co_request(BlockDriverState *bs, NBDRequest *request, -+ QEMUIOVector *write_qiov) -+{ -+ int ret, request_ret; -+ Error *local_err = NULL; -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ -+ assert(request->type != NBD_CMD_READ); -+ if (write_qiov) { -+ assert(request->type == NBD_CMD_WRITE); -+ assert(request->len == iov_size(write_qiov->iov, write_qiov->niov)); -+ } else { -+ assert(request->type != NBD_CMD_WRITE); -+ } -+ -+ do { -+ ret = nbd_co_send_request(bs, request, write_qiov); -+ if (ret < 0) { -+ continue; -+ } -+ -+ ret = nbd_co_receive_return_code(s, request->cookie, -+ &request_ret, &local_err); -+ if (local_err) { -+ trace_nbd_co_request_fail(request->from, request->len, -+ request->cookie, request->flags, -+ request->type, -+ nbd_cmd_lookup(request->type), -+ ret, error_get_pretty(local_err)); -+ error_free(local_err); -+ local_err = NULL; -+ } -+ } while (ret < 0 && nbd_client_will_reconnect(s)); -+ -+ return ret ? ret : request_ret; -+} -+ -+static int coroutine_fn GRAPH_RDLOCK -+nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, -+ QEMUIOVector *qiov, BdrvRequestFlags flags) -+{ -+ int ret, request_ret; -+ Error *local_err = NULL; -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ NBDRequest request = { -+ .type = NBD_CMD_READ, -+ .from = offset, -+ .len = bytes, -+ }; -+ -+ assert(bytes <= NBD_MAX_BUFFER_SIZE); -+ -+ if (!bytes) { -+ return 0; -+ } -+ /* -+ * Work around the fact that the block layer doesn't do -+ * byte-accurate sizing yet - if the read exceeds the server's -+ * advertised size because the block layer rounded size up, then -+ * truncate the request to the server and tail-pad with zero. -+ */ -+ if (offset >= s->info.size) { -+ assert(bytes < BDRV_SECTOR_SIZE); -+ qemu_iovec_memset(qiov, 0, 0, bytes); -+ return 0; -+ } -+ if (offset + bytes > s->info.size) { -+ uint64_t slop = offset + bytes - s->info.size; -+ -+ assert(slop < BDRV_SECTOR_SIZE); -+ qemu_iovec_memset(qiov, bytes - slop, 0, slop); -+ request.len -= slop; -+ } -+ -+ do { -+ ret = nbd_co_send_request(bs, &request, NULL); -+ if (ret < 0) { -+ continue; -+ } -+ -+ ret = nbd_co_receive_cmdread_reply(s, request.cookie, offset, qiov, -+ &request_ret, &local_err); -+ if (local_err) { -+ trace_nbd_co_request_fail(request.from, request.len, request.cookie, -+ request.flags, request.type, -+ nbd_cmd_lookup(request.type), -+ ret, error_get_pretty(local_err)); -+ error_free(local_err); -+ local_err = NULL; -+ } -+ } while (ret < 0 && nbd_client_will_reconnect(s)); -+ -+ return ret ? ret : request_ret; -+} -+ -+static int coroutine_fn GRAPH_RDLOCK -+nbd_client_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, -+ QEMUIOVector *qiov, BdrvRequestFlags flags) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ NBDRequest request = { -+ .type = NBD_CMD_WRITE, -+ .from = offset, -+ .len = bytes, -+ }; -+ -+ assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); -+ if (flags & BDRV_REQ_FUA) { -+ assert(s->info.flags & NBD_FLAG_SEND_FUA); -+ request.flags |= NBD_CMD_FLAG_FUA; -+ } -+ -+ assert(bytes <= NBD_MAX_BUFFER_SIZE); -+ -+ if (!bytes) { -+ return 0; -+ } -+ return nbd_co_request(bs, &request, qiov); -+} -+ -+static int coroutine_fn GRAPH_RDLOCK -+nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, -+ BdrvRequestFlags flags) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ NBDRequest request = { -+ .type = NBD_CMD_WRITE_ZEROES, -+ .from = offset, -+ .len = bytes, -+ }; -+ -+ /* rely on max_pwrite_zeroes */ -+ assert(bytes <= UINT32_MAX || s->info.mode >= NBD_MODE_EXTENDED); -+ -+ assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); -+ if (!(s->info.flags & NBD_FLAG_SEND_WRITE_ZEROES)) { -+ return -ENOTSUP; -+ } -+ -+ if (flags & BDRV_REQ_FUA) { -+ assert(s->info.flags & NBD_FLAG_SEND_FUA); -+ request.flags |= NBD_CMD_FLAG_FUA; -+ } -+ if (!(flags & BDRV_REQ_MAY_UNMAP)) { -+ request.flags |= NBD_CMD_FLAG_NO_HOLE; -+ } -+ if (flags & BDRV_REQ_NO_FALLBACK) { -+ assert(s->info.flags & NBD_FLAG_SEND_FAST_ZERO); -+ request.flags |= NBD_CMD_FLAG_FAST_ZERO; -+ } -+ -+ if (!bytes) { -+ return 0; -+ } -+ return nbd_co_request(bs, &request, NULL); -+} -+ -+static int coroutine_fn GRAPH_RDLOCK nbd_client_co_flush(BlockDriverState *bs) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ NBDRequest request = { .type = NBD_CMD_FLUSH }; -+ -+ if (!(s->info.flags & NBD_FLAG_SEND_FLUSH)) { -+ return 0; -+ } -+ -+ request.from = 0; -+ request.len = 0; -+ -+ return nbd_co_request(bs, &request, NULL); -+} -+ -+static int coroutine_fn GRAPH_RDLOCK -+nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ NBDRequest request = { -+ .type = NBD_CMD_TRIM, -+ .from = offset, -+ .len = bytes, -+ }; -+ -+ /* rely on max_pdiscard */ -+ assert(bytes <= UINT32_MAX || s->info.mode >= NBD_MODE_EXTENDED); -+ -+ assert(!(s->info.flags & NBD_FLAG_READ_ONLY)); -+ if (!(s->info.flags & NBD_FLAG_SEND_TRIM) || !bytes) { -+ return 0; -+ } -+ -+ return nbd_co_request(bs, &request, NULL); -+} -+ -+static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( -+ BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, -+ int64_t *pnum, int64_t *map, BlockDriverState **file) -+{ -+ int ret, request_ret; -+ NBDExtent64 extent = { 0 }; -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ Error *local_err = NULL; -+ -+ NBDRequest request = { -+ .type = NBD_CMD_BLOCK_STATUS, -+ .from = offset, -+ .len = MIN(bytes, s->info.size - offset), -+ .flags = NBD_CMD_FLAG_REQ_ONE, -+ }; -+ -+ if (!s->info.base_allocation) { -+ *pnum = bytes; -+ *map = offset; -+ *file = bs; -+ return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; -+ } -+ if (s->info.mode < NBD_MODE_EXTENDED) { -+ request.len = MIN(QEMU_ALIGN_DOWN(INT_MAX, bs->bl.request_alignment), -+ request.len); -+ } -+ -+ /* -+ * Work around the fact that the block layer doesn't do -+ * byte-accurate sizing yet - if the status request exceeds the -+ * server's advertised size because the block layer rounded size -+ * up, we truncated the request to the server (above), or are -+ * called on just the hole. -+ */ -+ if (offset >= s->info.size) { -+ *pnum = bytes; -+ assert(bytes < BDRV_SECTOR_SIZE); -+ /* Intentionally don't report offset_valid for the hole */ -+ return BDRV_BLOCK_ZERO; -+ } -+ -+ if (s->info.min_block) { -+ assert(QEMU_IS_ALIGNED(request.len, s->info.min_block)); -+ } -+ do { -+ ret = nbd_co_send_request(bs, &request, NULL); -+ if (ret < 0) { -+ continue; -+ } -+ -+ ret = nbd_co_receive_blockstatus_reply(s, request.cookie, bytes, -+ &extent, &request_ret, -+ &local_err); -+ if (local_err) { -+ trace_nbd_co_request_fail(request.from, request.len, request.cookie, -+ request.flags, request.type, -+ nbd_cmd_lookup(request.type), -+ ret, error_get_pretty(local_err)); -+ error_free(local_err); -+ local_err = NULL; -+ } -+ } while (ret < 0 && nbd_client_will_reconnect(s)); -+ -+ if (ret < 0 || request_ret < 0) { -+ return ret ? ret : request_ret; -+ } -+ -+ assert(extent.length); -+ *pnum = extent.length; -+ *map = offset; -+ *file = bs; -+ return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) | -+ (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0) | -+ BDRV_BLOCK_OFFSET_VALID; -+} -+ -+static int nbd_client_reopen_prepare(BDRVReopenState *state, -+ BlockReopenQueue *queue, Error **errp) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)state->bs->opaque; -+ -+ if ((state->flags & BDRV_O_RDWR) && (s->info.flags & NBD_FLAG_READ_ONLY)) { -+ error_setg(errp, "Can't reopen read-only NBD mount as read/write"); -+ return -EACCES; -+ } -+ return 0; -+} -+ -+static void nbd_yank(void *opaque) -+{ -+ BlockDriverState *bs = opaque; -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ -+ QEMU_LOCK_GUARD(&s->requests_lock); -+ qio_channel_shutdown(s->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); -+ s->state = NBD_CLIENT_QUIT; -+} -+ -+static void nbd_client_close(BlockDriverState *bs) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ NBDRequest request = { .type = NBD_CMD_DISC, .mode = s->info.mode }; -+ -+ if (s->ioc) { -+ nbd_send_request(s->ioc, &request); -+ } -+ -+ nbd_teardown_connection(bs); -+} -+ -+ -+/* -+ * Parse nbd_open options -+ */ -+ -+static int nbd_parse_uri(const char *filename, QDict *options) -+{ -+ URI *uri; -+ const char *p; -+ QueryParams *qp = NULL; -+ int ret = 0; -+ bool is_unix; -+ -+ uri = uri_parse(filename); -+ if (!uri) { -+ return -EINVAL; -+ } -+ -+ /* transport */ -+ if (!g_strcmp0(uri->scheme, "nbd")) { -+ is_unix = false; -+ } else if (!g_strcmp0(uri->scheme, "nbd+tcp")) { -+ is_unix = false; -+ } else if (!g_strcmp0(uri->scheme, "nbd+unix")) { -+ is_unix = true; -+ } else { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ p = uri->path ? uri->path : ""; -+ if (p[0] == '/') { -+ p++; -+ } -+ if (p[0]) { -+ qdict_put_str(options, "export", p); -+ } -+ -+ qp = query_params_parse(uri->query); -+ if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ if (is_unix) { -+ /* nbd+unix:///export?socket=path */ -+ if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) { -+ ret = -EINVAL; -+ goto out; -+ } -+ qdict_put_str(options, "server.type", "unix"); -+ qdict_put_str(options, "server.path", qp->p[0].value); -+ } else { -+ QString *host; -+ char *port_str; -+ -+ /* nbd[+tcp]://host[:port]/export */ -+ if (!uri->server) { -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ /* strip braces from literal IPv6 address */ -+ if (uri->server[0] == '[') { -+ host = qstring_from_substr(uri->server, 1, -+ strlen(uri->server) - 1); -+ } else { -+ host = qstring_from_str(uri->server); -+ } -+ -+ qdict_put_str(options, "server.type", "inet"); -+ qdict_put(options, "server.host", host); -+ -+ port_str = g_strdup_printf("%d", uri->port ?: NBD_DEFAULT_PORT); -+ qdict_put_str(options, "server.port", port_str); -+ g_free(port_str); -+ } -+ -+out: -+ if (qp) { -+ query_params_free(qp); -+ } -+ uri_free(uri); -+ return ret; -+} -+ -+static bool nbd_has_filename_options_conflict(QDict *options, Error **errp) -+{ -+ const QDictEntry *e; -+ -+ for (e = qdict_first(options); e; e = qdict_next(options, e)) { -+ if (!strcmp(e->key, "host") || -+ !strcmp(e->key, "port") || -+ !strcmp(e->key, "path") || -+ !strcmp(e->key, "export") || -+ strstart(e->key, "server.", NULL)) -+ { -+ error_setg(errp, "Option '%s' cannot be used with a file name", -+ e->key); -+ return true; -+ } -+ } -+ -+ return false; -+} -+ -+static void nbd_parse_filename(const char *filename, QDict *options, -+ Error **errp) -+{ -+ g_autofree char *file = NULL; -+ char *export_name; -+ const char *host_spec; -+ const char *unixpath; -+ -+ if (nbd_has_filename_options_conflict(options, errp)) { -+ return; -+ } -+ -+ if (strstr(filename, "://")) { -+ int ret = nbd_parse_uri(filename, options); -+ if (ret < 0) { -+ error_setg(errp, "No valid URL specified"); -+ } -+ return; -+ } -+ -+ file = g_strdup(filename); -+ -+ export_name = strstr(file, EN_OPTSTR); -+ if (export_name) { -+ if (export_name[strlen(EN_OPTSTR)] == 0) { -+ return; -+ } -+ export_name[0] = 0; /* truncate 'file' */ -+ export_name += strlen(EN_OPTSTR); -+ -+ qdict_put_str(options, "export", export_name); -+ } -+ -+ /* extract the host_spec - fail if it's not nbd:... */ -+ if (!strstart(file, "nbd:", &host_spec)) { -+ error_setg(errp, "File name string for NBD must start with 'nbd:'"); -+ return; -+ } -+ -+ if (!*host_spec) { -+ return; -+ } -+ -+ /* are we a UNIX or TCP socket? */ -+ if (strstart(host_spec, "unix:", &unixpath)) { -+ qdict_put_str(options, "server.type", "unix"); -+ qdict_put_str(options, "server.path", unixpath); -+ } else { -+ InetSocketAddress *addr = g_new(InetSocketAddress, 1); -+ -+ if (inet_parse(addr, host_spec, errp)) { -+ goto out_inet; -+ } -+ -+ qdict_put_str(options, "server.type", "inet"); -+ qdict_put_str(options, "server.host", addr->host); -+ qdict_put_str(options, "server.port", addr->port); -+ out_inet: -+ qapi_free_InetSocketAddress(addr); -+ } -+} -+ -+static bool nbd_process_legacy_socket_options(QDict *output_options, -+ QemuOpts *legacy_opts, -+ Error **errp) -+{ -+ const char *path = qemu_opt_get(legacy_opts, "path"); -+ const char *host = qemu_opt_get(legacy_opts, "host"); -+ const char *port = qemu_opt_get(legacy_opts, "port"); -+ const QDictEntry *e; -+ -+ if (!path && !host && !port) { -+ return true; -+ } -+ -+ for (e = qdict_first(output_options); e; e = qdict_next(output_options, e)) -+ { -+ if (strstart(e->key, "server.", NULL)) { -+ error_setg(errp, "Cannot use 'server' and path/host/port at the " -+ "same time"); -+ return false; -+ } -+ } -+ -+ if (path && host) { -+ error_setg(errp, "path and host may not be used at the same time"); -+ return false; -+ } else if (path) { -+ if (port) { -+ error_setg(errp, "port may not be used without host"); -+ return false; -+ } -+ -+ qdict_put_str(output_options, "server.type", "unix"); -+ qdict_put_str(output_options, "server.path", path); -+ } else if (host) { -+ qdict_put_str(output_options, "server.type", "inet"); -+ qdict_put_str(output_options, "server.host", host); -+ qdict_put_str(output_options, "server.port", -+ port ?: stringify(NBD_DEFAULT_PORT)); -+ } -+ -+ return true; -+} -+ -+static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, -+ Error **errp) -+{ -+ SocketAddress *saddr = NULL; -+ QDict *addr = NULL; -+ Visitor *iv = NULL; -+ -+ qdict_extract_subqdict(options, &addr, "server."); -+ if (!qdict_size(addr)) { -+ error_setg(errp, "NBD server address missing"); -+ goto done; -+ } -+ -+ iv = qobject_input_visitor_new_flat_confused(addr, errp); -+ if (!iv) { -+ goto done; -+ } -+ -+ if (!visit_type_SocketAddress(iv, NULL, &saddr, errp)) { -+ goto done; -+ } -+ -+ if (socket_address_parse_named_fd(saddr, errp) < 0) { -+ qapi_free_SocketAddress(saddr); -+ saddr = NULL; -+ goto done; -+ } -+ -+done: -+ qobject_unref(addr); -+ visit_free(iv); -+ return saddr; -+} -+ -+static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) -+{ -+ Object *obj; -+ QCryptoTLSCreds *creds; -+ -+ obj = object_resolve_path_component( -+ object_get_objects_root(), id); -+ if (!obj) { -+ error_setg(errp, "No TLS credentials with id '%s'", -+ id); -+ return NULL; -+ } -+ creds = (QCryptoTLSCreds *) -+ object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); -+ if (!creds) { -+ error_setg(errp, "Object with id '%s' is not TLS credentials", -+ id); -+ return NULL; -+ } -+ -+ if (!qcrypto_tls_creds_check_endpoint(creds, -+ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, -+ errp)) { -+ return NULL; -+ } -+ object_ref(obj); -+ return creds; -+} -+ -+ -+static QemuOptsList nbd_runtime_opts = { -+ .name = "nbd", -+ .head = QTAILQ_HEAD_INITIALIZER(nbd_runtime_opts.head), -+ .desc = { -+ { -+ .name = "host", -+ .type = QEMU_OPT_STRING, -+ .help = "TCP host to connect to", -+ }, -+ { -+ .name = "port", -+ .type = QEMU_OPT_STRING, -+ .help = "TCP port to connect to", -+ }, -+ { -+ .name = "path", -+ .type = QEMU_OPT_STRING, -+ .help = "Unix socket path to connect to", -+ }, -+ { -+ .name = "export", -+ .type = QEMU_OPT_STRING, -+ .help = "Name of the NBD export to open", -+ }, -+ { -+ .name = "tls-creds", -+ .type = QEMU_OPT_STRING, -+ .help = "ID of the TLS credentials to use", -+ }, -+ { -+ .name = "tls-hostname", -+ .type = QEMU_OPT_STRING, -+ .help = "Override hostname for validating TLS x509 certificate", -+ }, -+ { -+ .name = "x-dirty-bitmap", -+ .type = QEMU_OPT_STRING, -+ .help = "experimental: expose named dirty bitmap in place of " -+ "block status", -+ }, -+ { -+ .name = "reconnect-delay", -+ .type = QEMU_OPT_NUMBER, -+ .help = "On an unexpected disconnect, the nbd client tries to " -+ "connect again until succeeding or encountering a serious " -+ "error. During the first @reconnect-delay seconds, all " -+ "requests are paused and will be rerun on a successful " -+ "reconnect. After that time, any delayed requests and all " -+ "future requests before a successful reconnect will " -+ "immediately fail. Default 0", -+ }, -+ { -+ .name = "open-timeout", -+ .type = QEMU_OPT_NUMBER, -+ .help = "In seconds. If zero, the nbd driver tries the connection " -+ "only once, and fails to open if the connection fails. " -+ "If non-zero, the nbd driver will repeat connection " -+ "attempts until successful or until @open-timeout seconds " -+ "have elapsed. Default 0", -+ }, -+ { /* end of list */ } -+ }, -+}; -+ -+static int nbd_process_options(BlockDriverState *bs, QDict *options, -+ Error **errp) -+{ -+ BDRVNBDState *s = bs->opaque; -+ QemuOpts *opts; -+ int ret = -EINVAL; -+ -+ opts = qemu_opts_create(&nbd_runtime_opts, NULL, 0, &error_abort); -+ if (!qemu_opts_absorb_qdict(opts, options, errp)) { -+ goto error; -+ } -+ -+ /* Translate @host, @port, and @path to a SocketAddress */ -+ if (!nbd_process_legacy_socket_options(options, opts, errp)) { -+ goto error; -+ } -+ -+ /* Pop the config into our state object. Exit if invalid. */ -+ s->saddr = nbd_config(s, options, errp); -+ if (!s->saddr) { -+ goto error; -+ } -+ -+ s->export = g_strdup(qemu_opt_get(opts, "export")); -+ if (s->export && strlen(s->export) > NBD_MAX_STRING_SIZE) { -+ error_setg(errp, "export name too long to send to server"); -+ goto error; -+ } -+ -+ s->tlscredsid = g_strdup(qemu_opt_get(opts, "tls-creds")); -+ if (s->tlscredsid) { -+ s->tlscreds = nbd_get_tls_creds(s->tlscredsid, errp); -+ if (!s->tlscreds) { -+ goto error; -+ } -+ -+ s->tlshostname = g_strdup(qemu_opt_get(opts, "tls-hostname")); -+ if (!s->tlshostname && -+ s->saddr->type == SOCKET_ADDRESS_TYPE_INET) { -+ s->tlshostname = g_strdup(s->saddr->u.inet.host); -+ } -+ } -+ -+ s->x_dirty_bitmap = g_strdup(qemu_opt_get(opts, "x-dirty-bitmap")); -+ if (s->x_dirty_bitmap && strlen(s->x_dirty_bitmap) > NBD_MAX_STRING_SIZE) { -+ error_setg(errp, "x-dirty-bitmap query too long to send to server"); -+ goto error; -+ } -+ -+ s->reconnect_delay = qemu_opt_get_number(opts, "reconnect-delay", 0); -+ s->open_timeout = qemu_opt_get_number(opts, "open-timeout", 0); -+ -+ ret = 0; -+ -+ error: -+ qemu_opts_del(opts); -+ return ret; -+} -+ -+static int nbd_open(BlockDriverState *bs, QDict *options, int flags, -+ Error **errp) -+{ -+ int ret; -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ -+ s->bs = bs; -+ qemu_mutex_init(&s->requests_lock); -+ qemu_co_queue_init(&s->free_sema); -+ qemu_co_mutex_init(&s->send_mutex); -+ qemu_co_mutex_init(&s->receive_mutex); -+ -+ if (!yank_register_instance(BLOCKDEV_YANK_INSTANCE(bs->node_name), errp)) { -+ return -EEXIST; -+ } -+ -+ ret = nbd_process_options(bs, options, errp); -+ if (ret < 0) { -+ goto fail; -+ } -+ -+ s->conn = nbd_client_connection_new(s->saddr, true, s->export, -+ s->x_dirty_bitmap, s->tlscreds, -+ s->tlshostname); -+ -+ if (s->open_timeout) { -+ nbd_client_connection_enable_retry(s->conn); -+ open_timer_init(s, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + -+ s->open_timeout * NANOSECONDS_PER_SECOND); -+ } -+ -+ s->state = NBD_CLIENT_CONNECTING_WAIT; -+ ret = nbd_do_establish_connection(bs, true, errp); -+ if (ret < 0) { -+ goto fail; -+ } -+ -+ /* -+ * The connect attempt is done, so we no longer need this timer. -+ * Delete it, because we do not want it to be around when this node -+ * is drained or closed. -+ */ -+ open_timer_del(s); -+ -+ nbd_client_connection_enable_retry(s->conn); -+ -+ return 0; -+ -+fail: -+ open_timer_del(s); -+ nbd_clear_bdrvstate(bs); -+ return ret; -+} -+ -+static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ uint32_t min = s->info.min_block; -+ uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block); -+ -+ /* -+ * If the server did not advertise an alignment: -+ * - a size that is not sector-aligned implies that an alignment -+ * of 1 can be used to access those tail bytes -+ * - advertisement of block status requires an alignment of 1, so -+ * that we don't violate block layer constraints that block -+ * status is always aligned (as we can't control whether the -+ * server will report sub-sector extents, such as a hole at EOF -+ * on an unaligned POSIX file) -+ * - otherwise, assume the server is so old that we are safer avoiding -+ * sub-sector requests -+ */ -+ if (!min) { -+ min = (!QEMU_IS_ALIGNED(s->info.size, BDRV_SECTOR_SIZE) || -+ s->info.base_allocation) ? 1 : BDRV_SECTOR_SIZE; -+ } -+ -+ bs->bl.request_alignment = min; -+ bs->bl.max_pdiscard = QEMU_ALIGN_DOWN(INT_MAX, min); -+ bs->bl.max_pwrite_zeroes = max; -+ bs->bl.max_transfer = max; -+ -+ /* -+ * Assume that if the server supports extended headers, it also -+ * supports unlimited size zero and trim commands. -+ */ -+ if (s->info.mode >= NBD_MODE_EXTENDED) { -+ bs->bl.max_pdiscard = bs->bl.max_pwrite_zeroes = 0; -+ } -+ -+ if (s->info.opt_block && -+ s->info.opt_block > bs->bl.opt_transfer) { -+ bs->bl.opt_transfer = s->info.opt_block; -+ } -+} -+ -+static void nbd_close(BlockDriverState *bs) -+{ -+ nbd_client_close(bs); -+ nbd_clear_bdrvstate(bs); -+} -+ -+/* -+ * NBD cannot truncate, but if the caller asks to truncate to the same size, or -+ * to a smaller size with exact=false, there is no reason to fail the -+ * operation. -+ * -+ * Preallocation mode is ignored since it does not seems useful to fail when -+ * we never change anything. -+ */ -+static int coroutine_fn nbd_co_truncate(BlockDriverState *bs, int64_t offset, -+ bool exact, PreallocMode prealloc, -+ BdrvRequestFlags flags, Error **errp) -+{ -+ BDRVNBDState *s = bs->opaque; -+ -+ if (offset != s->info.size && exact) { -+ error_setg(errp, "Cannot resize NBD nodes"); -+ return -ENOTSUP; -+ } -+ -+ if (offset > s->info.size) { -+ error_setg(errp, "Cannot grow NBD nodes"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int64_t coroutine_fn nbd_co_getlength(BlockDriverState *bs) -+{ -+ BDRVNBDState *s = bs->opaque; -+ -+ return s->info.size; -+} -+ -+static void nbd_refresh_filename(BlockDriverState *bs) -+{ -+ BDRVNBDState *s = bs->opaque; -+ const char *host = NULL, *port = NULL, *path = NULL; -+ size_t len = 0; -+ -+ if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) { -+ const InetSocketAddress *inet = &s->saddr->u.inet; -+ if (!inet->has_ipv4 && !inet->has_ipv6 && !inet->has_to) { -+ host = inet->host; -+ port = inet->port; -+ } -+ } else if (s->saddr->type == SOCKET_ADDRESS_TYPE_UNIX) { -+ path = s->saddr->u.q_unix.path; -+ } /* else can't represent as pseudo-filename */ -+ -+ if (path && s->export) { -+ len = snprintf(bs->exact_filename, sizeof(bs->exact_filename), -+ "nbd+unix:///%s?socket=%s", s->export, path); -+ } else if (path && !s->export) { -+ len = snprintf(bs->exact_filename, sizeof(bs->exact_filename), -+ "nbd+unix://?socket=%s", path); -+ } else if (host && s->export) { -+ len = snprintf(bs->exact_filename, sizeof(bs->exact_filename), -+ "nbd://%s:%s/%s", host, port, s->export); -+ } else if (host && !s->export) { -+ len = snprintf(bs->exact_filename, sizeof(bs->exact_filename), -+ "nbd://%s:%s", host, port); -+ } -+ if (len >= sizeof(bs->exact_filename)) { -+ /* Name is too long to represent exactly, so leave it empty. */ -+ bs->exact_filename[0] = '\0'; -+ } -+} -+ -+static char *nbd_dirname(BlockDriverState *bs, Error **errp) -+{ -+ /* The generic bdrv_dirname() implementation is able to work out some -+ * directory name for NBD nodes, but that would be wrong. So far there is no -+ * specification for how "export paths" would work, so NBD does not have -+ * directory names. */ -+ error_setg(errp, "Cannot generate a base directory for NBD nodes"); -+ return NULL; -+} -+ -+static const char *const nbd_strong_runtime_opts[] = { -+ "path", -+ "host", -+ "port", -+ "export", -+ "tls-creds", -+ "tls-hostname", -+ "server.", -+ -+ NULL -+}; -+ -+static void nbd_cancel_in_flight(BlockDriverState *bs) -+{ -+ BDRVNBDState *s = (BDRVNBDState *)bs->opaque; -+ -+ reconnect_delay_timer_del(s); -+ -+ qemu_mutex_lock(&s->requests_lock); -+ if (s->state == NBD_CLIENT_CONNECTING_WAIT) { -+ s->state = NBD_CLIENT_CONNECTING_NOWAIT; -+ } -+ qemu_mutex_unlock(&s->requests_lock); -+ -+ nbd_co_establish_connection_cancel(s->conn); -+} -+ -+static void nbd_attach_aio_context(BlockDriverState *bs, -+ AioContext *new_context) -+{ -+ BDRVNBDState *s = bs->opaque; -+ -+ /* The open_timer is used only during nbd_open() */ -+ assert(!s->open_timer); -+ -+ /* -+ * The reconnect_delay_timer is scheduled in I/O paths when the -+ * connection is lost, to cancel the reconnection attempt after a -+ * given time. Once this attempt is done (successfully or not), -+ * nbd_reconnect_attempt() ensures the timer is deleted before the -+ * respective I/O request is resumed. -+ * Since the AioContext can only be changed when a node is drained, -+ * the reconnect_delay_timer cannot be active here. -+ */ -+ assert(!s->reconnect_delay_timer); -+} -+ -+static void nbd_detach_aio_context(BlockDriverState *bs) -+{ -+ BDRVNBDState *s = bs->opaque; -+ -+ assert(!s->open_timer); -+ assert(!s->reconnect_delay_timer); -+} -+ -+static BlockDriver bdrv_nbd = { -+ .format_name = "nbd", -+ .protocol_name = "nbd", -+ .instance_size = sizeof(BDRVNBDState), -+ .bdrv_parse_filename = nbd_parse_filename, -+ .bdrv_co_create_opts = bdrv_co_create_opts_simple, -+ .create_opts = &bdrv_create_opts_simple, -+ .bdrv_open = nbd_open, -+ .bdrv_reopen_prepare = nbd_client_reopen_prepare, -+ .bdrv_co_preadv = nbd_client_co_preadv, -+ .bdrv_co_pwritev = nbd_client_co_pwritev, -+ .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, -+ .bdrv_close = nbd_close, -+ .bdrv_co_flush_to_os = nbd_client_co_flush, -+ .bdrv_co_pdiscard = nbd_client_co_pdiscard, -+ .bdrv_refresh_limits = nbd_refresh_limits, -+ .bdrv_co_truncate = nbd_co_truncate, -+ .bdrv_co_getlength = nbd_co_getlength, -+ .bdrv_refresh_filename = nbd_refresh_filename, -+ .bdrv_co_block_status = nbd_client_co_block_status, -+ .bdrv_dirname = nbd_dirname, -+ .strong_runtime_opts = nbd_strong_runtime_opts, -+ .bdrv_cancel_in_flight = nbd_cancel_in_flight, -+ -+ .bdrv_attach_aio_context = nbd_attach_aio_context, -+ .bdrv_detach_aio_context = nbd_detach_aio_context, -+}; -+ -+static BlockDriver bdrv_nbd_tcp = { -+ .format_name = "nbd", -+ .protocol_name = "nbd+tcp", -+ .instance_size = sizeof(BDRVNBDState), -+ .bdrv_parse_filename = nbd_parse_filename, -+ .bdrv_co_create_opts = bdrv_co_create_opts_simple, -+ .create_opts = &bdrv_create_opts_simple, -+ .bdrv_open = nbd_open, -+ .bdrv_reopen_prepare = nbd_client_reopen_prepare, -+ .bdrv_co_preadv = nbd_client_co_preadv, -+ .bdrv_co_pwritev = nbd_client_co_pwritev, -+ .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, -+ .bdrv_close = nbd_close, -+ .bdrv_co_flush_to_os = nbd_client_co_flush, -+ .bdrv_co_pdiscard = nbd_client_co_pdiscard, -+ .bdrv_refresh_limits = nbd_refresh_limits, -+ .bdrv_co_truncate = nbd_co_truncate, -+ .bdrv_co_getlength = nbd_co_getlength, -+ .bdrv_refresh_filename = nbd_refresh_filename, -+ .bdrv_co_block_status = nbd_client_co_block_status, -+ .bdrv_dirname = nbd_dirname, -+ .strong_runtime_opts = nbd_strong_runtime_opts, -+ .bdrv_cancel_in_flight = nbd_cancel_in_flight, -+ -+ .bdrv_attach_aio_context = nbd_attach_aio_context, -+ .bdrv_detach_aio_context = nbd_detach_aio_context, -+}; -+ -+static BlockDriver bdrv_nbd_unix = { -+ .format_name = "nbd", -+ .protocol_name = "nbd+unix", -+ .instance_size = sizeof(BDRVNBDState), -+ .bdrv_parse_filename = nbd_parse_filename, -+ .bdrv_co_create_opts = bdrv_co_create_opts_simple, -+ .create_opts = &bdrv_create_opts_simple, -+ .bdrv_open = nbd_open, -+ .bdrv_reopen_prepare = nbd_client_reopen_prepare, -+ .bdrv_co_preadv = nbd_client_co_preadv, -+ .bdrv_co_pwritev = nbd_client_co_pwritev, -+ .bdrv_co_pwrite_zeroes = nbd_client_co_pwrite_zeroes, -+ .bdrv_close = nbd_close, -+ .bdrv_co_flush_to_os = nbd_client_co_flush, -+ .bdrv_co_pdiscard = nbd_client_co_pdiscard, -+ .bdrv_refresh_limits = nbd_refresh_limits, -+ .bdrv_co_truncate = nbd_co_truncate, -+ .bdrv_co_getlength = nbd_co_getlength, -+ .bdrv_refresh_filename = nbd_refresh_filename, -+ .bdrv_co_block_status = nbd_client_co_block_status, -+ .bdrv_dirname = nbd_dirname, -+ .strong_runtime_opts = nbd_strong_runtime_opts, -+ .bdrv_cancel_in_flight = nbd_cancel_in_flight, -+ -+ .bdrv_attach_aio_context = nbd_attach_aio_context, -+ .bdrv_detach_aio_context = nbd_detach_aio_context, -+}; -+ -+static void bdrv_nbd_init(void) -+{ -+ bdrv_register(&bdrv_nbd); -+ bdrv_register(&bdrv_nbd_tcp); -+ bdrv_register(&bdrv_nbd_unix); -+} -+ -+block_init(bdrv_nbd_init); diff --git a/qcow2/lib/block/progress_meter.c b/qcow2/lib/block/progress_meter.c new file mode 100644 index 00000000..31a170a2 @@ -74574,1271 +72315,6 @@ index 00000000..d5f29e14 + GLOBAL_STATE_CODE(); + return job->job.aio_context; +} -diff --git a/qcow2/lib/crypto/hash.c b/qcow2/lib/crypto/hash.c -new file mode 100644 -index 00000000..b0f8228b ---- /dev/null -+++ b/qcow2/lib/crypto/hash.c -@@ -0,0 +1,144 @@ -+/* -+ * QEMU Crypto hash algorithms -+ * -+ * Copyright (c) 2015 Red Hat, Inc. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, see . -+ * -+ */ -+ -+#include "qemu/osdep.h" -+#include "crypto/hash.h" -+#include "hashpriv.h" -+ -+static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { -+ [QCRYPTO_HASH_ALG_MD5] = 16, -+ [QCRYPTO_HASH_ALG_SHA1] = 20, -+ [QCRYPTO_HASH_ALG_SHA224] = 28, -+ [QCRYPTO_HASH_ALG_SHA256] = 32, -+ [QCRYPTO_HASH_ALG_SHA384] = 48, -+ [QCRYPTO_HASH_ALG_SHA512] = 64, -+ [QCRYPTO_HASH_ALG_RIPEMD160] = 20, -+}; -+ -+size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) -+{ -+ assert(alg < G_N_ELEMENTS(qcrypto_hash_alg_size)); -+ return qcrypto_hash_alg_size[alg]; -+} -+ -+int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, -+ const struct iovec *iov, -+ size_t niov, -+ uint8_t **result, -+ size_t *resultlen, -+ Error **errp) -+{ -+#ifdef CONFIG_AF_ALG -+ int ret; -+ /* -+ * TODO: -+ * Maybe we should treat some afalg errors as fatal -+ */ -+ ret = qcrypto_hash_afalg_driver.hash_bytesv(alg, iov, niov, -+ result, resultlen, -+ NULL); -+ if (ret == 0) { -+ return ret; -+ } -+#endif -+ -+ return qcrypto_hash_lib_driver.hash_bytesv(alg, iov, niov, -+ result, resultlen, -+ errp); -+} -+ -+ -+int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, -+ const char *buf, -+ size_t len, -+ uint8_t **result, -+ size_t *resultlen, -+ Error **errp) -+{ -+ struct iovec iov = { .iov_base = (char *)buf, -+ .iov_len = len }; -+ return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp); -+} -+ -+static const char hex[] = "0123456789abcdef"; -+ -+int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, -+ const struct iovec *iov, -+ size_t niov, -+ char **digest, -+ Error **errp) -+{ -+ uint8_t *result = NULL; -+ size_t resultlen = 0; -+ size_t i; -+ -+ if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { -+ return -1; -+ } -+ -+ *digest = g_new0(char, (resultlen * 2) + 1); -+ for (i = 0 ; i < resultlen ; i++) { -+ (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf]; -+ (*digest)[(i * 2) + 1] = hex[result[i] & 0xf]; -+ } -+ (*digest)[resultlen * 2] = '\0'; -+ g_free(result); -+ return 0; -+} -+ -+int qcrypto_hash_digest(QCryptoHashAlgorithm alg, -+ const char *buf, -+ size_t len, -+ char **digest, -+ Error **errp) -+{ -+ struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; -+ -+ return qcrypto_hash_digestv(alg, &iov, 1, digest, errp); -+} -+ -+int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, -+ const struct iovec *iov, -+ size_t niov, -+ char **base64, -+ Error **errp) -+{ -+ uint8_t *result = NULL; -+ size_t resultlen = 0; -+ -+ if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { -+ return -1; -+ } -+ -+ *base64 = g_base64_encode(result, resultlen); -+ g_free(result); -+ return 0; -+} -+ -+int qcrypto_hash_base64(QCryptoHashAlgorithm alg, -+ const char *buf, -+ size_t len, -+ char **base64, -+ Error **errp) -+{ -+ struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; -+ -+ return qcrypto_hash_base64v(alg, &iov, 1, base64, errp); -+} -diff --git a/qcow2/lib/crypto/tlscreds.c b/qcow2/lib/crypto/tlscreds.c -new file mode 100644 -index 00000000..084ce0d5 ---- /dev/null -+++ b/qcow2/lib/crypto/tlscreds.c -@@ -0,0 +1,294 @@ -+/* -+ * QEMU crypto TLS credential support -+ * -+ * Copyright (c) 2015 Red Hat, Inc. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, see . -+ * -+ */ -+ -+#include "qemu/osdep.h" -+#include "qapi/error.h" -+#include "qapi-types-crypto.h" -+#include "qemu/module.h" -+#include "tlscredspriv.h" -+#include "trace.h" -+ -+#define DH_BITS 2048 -+ -+#ifdef CONFIG_GNUTLS -+int -+qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, -+ const char *filename, -+ gnutls_dh_params_t *dh_params, -+ Error **errp) -+{ -+ int ret; -+ -+ trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : ""); -+ -+ if (filename == NULL) { -+ ret = gnutls_dh_params_init(dh_params); -+ if (ret < 0) { -+ error_setg(errp, "Unable to initialize DH parameters: %s", -+ gnutls_strerror(ret)); -+ return -1; -+ } -+ ret = gnutls_dh_params_generate2(*dh_params, DH_BITS); -+ if (ret < 0) { -+ gnutls_dh_params_deinit(*dh_params); -+ *dh_params = NULL; -+ error_setg(errp, "Unable to generate DH parameters: %s", -+ gnutls_strerror(ret)); -+ return -1; -+ } -+ } else { -+ GError *gerr = NULL; -+ gchar *contents; -+ gsize len; -+ gnutls_datum_t data; -+ if (!g_file_get_contents(filename, -+ &contents, -+ &len, -+ &gerr)) { -+ -+ error_setg(errp, "%s", gerr->message); -+ g_error_free(gerr); -+ return -1; -+ } -+ data.data = (unsigned char *)contents; -+ data.size = len; -+ ret = gnutls_dh_params_init(dh_params); -+ if (ret < 0) { -+ g_free(contents); -+ error_setg(errp, "Unable to initialize DH parameters: %s", -+ gnutls_strerror(ret)); -+ return -1; -+ } -+ ret = gnutls_dh_params_import_pkcs3(*dh_params, -+ &data, -+ GNUTLS_X509_FMT_PEM); -+ g_free(contents); -+ if (ret < 0) { -+ gnutls_dh_params_deinit(*dh_params); -+ *dh_params = NULL; -+ error_setg(errp, "Unable to load DH parameters from %s: %s", -+ filename, gnutls_strerror(ret)); -+ return -1; -+ } -+ } -+ -+ return 0; -+} -+ -+ -+int -+qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, -+ const char *filename, -+ bool required, -+ char **cred, -+ Error **errp) -+{ -+ struct stat sb; -+ int ret = -1; -+ -+ if (!creds->dir) { -+ if (required) { -+ error_setg(errp, "Missing 'dir' property value"); -+ return -1; -+ } else { -+ return 0; -+ } -+ } -+ -+ *cred = g_strdup_printf("%s/%s", creds->dir, filename); -+ -+ if (stat(*cred, &sb) < 0) { -+ if (errno == ENOENT && !required) { -+ ret = 0; -+ } else { -+ error_setg_errno(errp, errno, -+ "Unable to access credentials %s", -+ *cred); -+ } -+ g_free(*cred); -+ *cred = NULL; -+ goto cleanup; -+ } -+ -+ ret = 0; -+ cleanup: -+ trace_qcrypto_tls_creds_get_path(creds, filename, -+ *cred ? *cred : ""); -+ return ret; -+} -+ -+ -+#endif /* ! CONFIG_GNUTLS */ -+ -+ -+static void -+qcrypto_tls_creds_prop_set_verify(Object *obj, -+ bool value, -+ Error **errp G_GNUC_UNUSED) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ creds->verifyPeer = value; -+} -+ -+ -+static bool -+qcrypto_tls_creds_prop_get_verify(Object *obj, -+ Error **errp G_GNUC_UNUSED) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ return creds->verifyPeer; -+} -+ -+ -+static void -+qcrypto_tls_creds_prop_set_dir(Object *obj, -+ const char *value, -+ Error **errp G_GNUC_UNUSED) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ creds->dir = g_strdup(value); -+} -+ -+ -+static char * -+qcrypto_tls_creds_prop_get_dir(Object *obj, -+ Error **errp G_GNUC_UNUSED) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ return g_strdup(creds->dir); -+} -+ -+ -+static void -+qcrypto_tls_creds_prop_set_priority(Object *obj, -+ const char *value, -+ Error **errp G_GNUC_UNUSED) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ creds->priority = g_strdup(value); -+} -+ -+ -+static char * -+qcrypto_tls_creds_prop_get_priority(Object *obj, -+ Error **errp G_GNUC_UNUSED) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ return g_strdup(creds->priority); -+} -+ -+ -+static void -+qcrypto_tls_creds_prop_set_endpoint(Object *obj, -+ int value, -+ Error **errp G_GNUC_UNUSED) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ creds->endpoint = value; -+} -+ -+ -+static int -+qcrypto_tls_creds_prop_get_endpoint(Object *obj, -+ Error **errp G_GNUC_UNUSED) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ return creds->endpoint; -+} -+ -+ -+static void -+qcrypto_tls_creds_class_init(ObjectClass *oc, void *data) -+{ -+ object_class_property_add_bool(oc, "verify-peer", -+ qcrypto_tls_creds_prop_get_verify, -+ qcrypto_tls_creds_prop_set_verify); -+ object_class_property_add_str(oc, "dir", -+ qcrypto_tls_creds_prop_get_dir, -+ qcrypto_tls_creds_prop_set_dir); -+ object_class_property_add_enum(oc, "endpoint", -+ "QCryptoTLSCredsEndpoint", -+ &QCryptoTLSCredsEndpoint_lookup, -+ qcrypto_tls_creds_prop_get_endpoint, -+ qcrypto_tls_creds_prop_set_endpoint); -+ object_class_property_add_str(oc, "priority", -+ qcrypto_tls_creds_prop_get_priority, -+ qcrypto_tls_creds_prop_set_priority); -+} -+ -+ -+static void -+qcrypto_tls_creds_init(Object *obj) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ creds->verifyPeer = true; -+} -+ -+ -+static void -+qcrypto_tls_creds_finalize(Object *obj) -+{ -+ QCryptoTLSCreds *creds = QCRYPTO_TLS_CREDS(obj); -+ -+ g_free(creds->dir); -+ g_free(creds->priority); -+} -+ -+bool qcrypto_tls_creds_check_endpoint(QCryptoTLSCreds *creds, -+ QCryptoTLSCredsEndpoint endpoint, -+ Error **errp) -+{ -+ if (creds->endpoint != endpoint) { -+ error_setg(errp, "Expected TLS credentials for a %s endpoint", -+ QCryptoTLSCredsEndpoint_str(endpoint)); -+ return false; -+ } -+ return true; -+} -+ -+static const TypeInfo qcrypto_tls_creds_info = { -+ .parent = TYPE_OBJECT, -+ .name = TYPE_QCRYPTO_TLS_CREDS, -+ .instance_size = sizeof(QCryptoTLSCreds), -+ .instance_init = qcrypto_tls_creds_init, -+ .instance_finalize = qcrypto_tls_creds_finalize, -+ .class_init = qcrypto_tls_creds_class_init, -+ .class_size = sizeof(QCryptoTLSCredsClass), -+ .abstract = true, -+}; -+ -+ -+static void -+qcrypto_tls_creds_register_types(void) -+{ -+ type_register_static(&qcrypto_tls_creds_info); -+} -+ -+ -+type_init(qcrypto_tls_creds_register_types); -diff --git a/qcow2/lib/crypto/tlscredspriv.h b/qcow2/lib/crypto/tlscredspriv.h -new file mode 100644 -index 00000000..df9815a2 ---- /dev/null -+++ b/qcow2/lib/crypto/tlscredspriv.h -@@ -0,0 +1,86 @@ -+/* -+ * QEMU crypto TLS credential support private helpers -+ * -+ * Copyright (c) 2015 Red Hat, Inc. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, see . -+ * -+ */ -+ -+#ifndef QCRYPTO_TLSCREDSPRIV_H -+#define QCRYPTO_TLSCREDSPRIV_H -+ -+#include "crypto/tlscreds.h" -+ -+#ifdef CONFIG_GNUTLS -+#include -+#endif -+ -+struct QCryptoTLSCreds { -+ Object parent_obj; -+ char *dir; -+ QCryptoTLSCredsEndpoint endpoint; -+#ifdef CONFIG_GNUTLS -+ gnutls_dh_params_t dh_params; -+#endif -+ bool verifyPeer; -+ char *priority; -+}; -+ -+struct QCryptoTLSCredsAnon { -+ QCryptoTLSCreds parent_obj; -+#ifdef CONFIG_GNUTLS -+ union { -+ gnutls_anon_server_credentials_t server; -+ gnutls_anon_client_credentials_t client; -+ } data; -+#endif -+}; -+ -+struct QCryptoTLSCredsPSK { -+ QCryptoTLSCreds parent_obj; -+ char *username; -+#ifdef CONFIG_GNUTLS -+ union { -+ gnutls_psk_server_credentials_t server; -+ gnutls_psk_client_credentials_t client; -+ } data; -+#endif -+}; -+ -+struct QCryptoTLSCredsX509 { -+ QCryptoTLSCreds parent_obj; -+#ifdef CONFIG_GNUTLS -+ gnutls_certificate_credentials_t data; -+#endif -+ bool sanityCheck; -+ char *passwordid; -+}; -+ -+#ifdef CONFIG_GNUTLS -+ -+int qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, -+ const char *filename, -+ bool required, -+ char **cred, -+ Error **errp); -+ -+int qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, -+ const char *filename, -+ gnutls_dh_params_t *dh_params, -+ Error **errp); -+ -+#endif -+ -+#endif /* QCRYPTO_TLSCREDSPRIV_H */ -diff --git a/qcow2/lib/crypto/tlssession.c b/qcow2/lib/crypto/tlssession.c -new file mode 100644 -index 00000000..77286e23 ---- /dev/null -+++ b/qcow2/lib/crypto/tlssession.c -@@ -0,0 +1,717 @@ -+/* -+ * QEMU crypto TLS session support -+ * -+ * Copyright (c) 2015 Red Hat, Inc. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, see . -+ * -+ */ -+ -+#include "qemu/osdep.h" -+#include "crypto/tlssession.h" -+#include "crypto/tlscredsanon.h" -+#include "crypto/tlscredspsk.h" -+#include "crypto/tlscredsx509.h" -+#include "qapi/error.h" -+#include "authz/base.h" -+#include "tlscredspriv.h" -+#include "trace.h" -+ -+#ifdef CONFIG_GNUTLS -+ -+ -+#include -+ -+ -+struct QCryptoTLSSession { -+ QCryptoTLSCreds *creds; -+ gnutls_session_t handle; -+ char *hostname; -+ char *authzid; -+ bool handshakeComplete; -+ QCryptoTLSSessionWriteFunc writeFunc; -+ QCryptoTLSSessionReadFunc readFunc; -+ void *opaque; -+ char *peername; -+ -+ /* -+ * Allow concurrent reads and writes, so track -+ * errors separately -+ */ -+ Error *rerr; -+ Error *werr; -+}; -+ -+ -+void -+qcrypto_tls_session_free(QCryptoTLSSession *session) -+{ -+ if (!session) { -+ return; -+ } -+ -+ error_free(session->rerr); -+ error_free(session->werr); -+ -+ gnutls_deinit(session->handle); -+ g_free(session->hostname); -+ g_free(session->peername); -+ g_free(session->authzid); -+ object_unref(OBJECT(session->creds)); -+ g_free(session); -+} -+ -+ -+static ssize_t -+qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) -+{ -+ QCryptoTLSSession *session = opaque; -+ ssize_t ret; -+ -+ if (!session->writeFunc) { -+ errno = EIO; -+ return -1; -+ }; -+ -+ error_free(session->werr); -+ session->werr = NULL; -+ -+ ret = session->writeFunc(buf, len, session->opaque, &session->werr); -+ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { -+ errno = EAGAIN; -+ return -1; -+ } else if (ret < 0) { -+ errno = EIO; -+ return -1; -+ } else { -+ return ret; -+ } -+} -+ -+ -+static ssize_t -+qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) -+{ -+ QCryptoTLSSession *session = opaque; -+ ssize_t ret; -+ -+ if (!session->readFunc) { -+ errno = EIO; -+ return -1; -+ }; -+ -+ error_free(session->rerr); -+ session->rerr = NULL; -+ -+ ret = session->readFunc(buf, len, session->opaque, &session->rerr); -+ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { -+ errno = EAGAIN; -+ return -1; -+ } else if (ret < 0) { -+ errno = EIO; -+ return -1; -+ } else { -+ return ret; -+ } -+} -+ -+#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" -+#define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK" -+ -+QCryptoTLSSession * -+qcrypto_tls_session_new(QCryptoTLSCreds *creds, -+ const char *hostname, -+ const char *authzid, -+ QCryptoTLSCredsEndpoint endpoint, -+ Error **errp) -+{ -+ QCryptoTLSSession *session; -+ int ret; -+ -+ session = g_new0(QCryptoTLSSession, 1); -+ trace_qcrypto_tls_session_new( -+ session, creds, hostname ? hostname : "", -+ authzid ? authzid : "", endpoint); -+ -+ if (hostname) { -+ session->hostname = g_strdup(hostname); -+ } -+ if (authzid) { -+ session->authzid = g_strdup(authzid); -+ } -+ session->creds = creds; -+ object_ref(OBJECT(creds)); -+ -+ if (creds->endpoint != endpoint) { -+ error_setg(errp, "Credentials endpoint doesn't match session"); -+ goto error; -+ } -+ -+ if (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { -+ ret = gnutls_init(&session->handle, GNUTLS_SERVER); -+ } else { -+ ret = gnutls_init(&session->handle, GNUTLS_CLIENT); -+ } -+ if (ret < 0) { -+ error_setg(errp, "Cannot initialize TLS session: %s", -+ gnutls_strerror(ret)); -+ goto error; -+ } -+ -+ if (object_dynamic_cast(OBJECT(creds), -+ TYPE_QCRYPTO_TLS_CREDS_ANON)) { -+ QCryptoTLSCredsAnon *acreds = QCRYPTO_TLS_CREDS_ANON(creds); -+ char *prio; -+ -+ if (creds->priority != NULL) { -+ prio = g_strdup_printf("%s:%s", -+ creds->priority, -+ TLS_PRIORITY_ADDITIONAL_ANON); -+ } else { -+ prio = g_strdup(CONFIG_TLS_PRIORITY ":" -+ TLS_PRIORITY_ADDITIONAL_ANON); -+ } -+ -+ ret = gnutls_priority_set_direct(session->handle, prio, NULL); -+ if (ret < 0) { -+ error_setg(errp, "Unable to set TLS session priority %s: %s", -+ prio, gnutls_strerror(ret)); -+ g_free(prio); -+ goto error; -+ } -+ g_free(prio); -+ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { -+ ret = gnutls_credentials_set(session->handle, -+ GNUTLS_CRD_ANON, -+ acreds->data.server); -+ } else { -+ ret = gnutls_credentials_set(session->handle, -+ GNUTLS_CRD_ANON, -+ acreds->data.client); -+ } -+ if (ret < 0) { -+ error_setg(errp, "Cannot set session credentials: %s", -+ gnutls_strerror(ret)); -+ goto error; -+ } -+ } else if (object_dynamic_cast(OBJECT(creds), -+ TYPE_QCRYPTO_TLS_CREDS_PSK)) { -+ QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); -+ char *prio; -+ -+ if (creds->priority != NULL) { -+ prio = g_strdup_printf("%s:%s", -+ creds->priority, -+ TLS_PRIORITY_ADDITIONAL_PSK); -+ } else { -+ prio = g_strdup(CONFIG_TLS_PRIORITY ":" -+ TLS_PRIORITY_ADDITIONAL_PSK); -+ } -+ -+ ret = gnutls_priority_set_direct(session->handle, prio, NULL); -+ if (ret < 0) { -+ error_setg(errp, "Unable to set TLS session priority %s: %s", -+ prio, gnutls_strerror(ret)); -+ g_free(prio); -+ goto error; -+ } -+ g_free(prio); -+ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { -+ ret = gnutls_credentials_set(session->handle, -+ GNUTLS_CRD_PSK, -+ pcreds->data.server); -+ } else { -+ ret = gnutls_credentials_set(session->handle, -+ GNUTLS_CRD_PSK, -+ pcreds->data.client); -+ } -+ if (ret < 0) { -+ error_setg(errp, "Cannot set session credentials: %s", -+ gnutls_strerror(ret)); -+ goto error; -+ } -+ } else if (object_dynamic_cast(OBJECT(creds), -+ TYPE_QCRYPTO_TLS_CREDS_X509)) { -+ QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); -+ const char *prio = creds->priority; -+ if (!prio) { -+ prio = CONFIG_TLS_PRIORITY; -+ } -+ -+ ret = gnutls_priority_set_direct(session->handle, prio, NULL); -+ if (ret < 0) { -+ error_setg(errp, "Cannot set default TLS session priority %s: %s", -+ prio, gnutls_strerror(ret)); -+ goto error; -+ } -+ ret = gnutls_credentials_set(session->handle, -+ GNUTLS_CRD_CERTIFICATE, -+ tcreds->data); -+ if (ret < 0) { -+ error_setg(errp, "Cannot set session credentials: %s", -+ gnutls_strerror(ret)); -+ goto error; -+ } -+ -+ if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { -+ /* This requests, but does not enforce a client cert. -+ * The cert checking code later does enforcement */ -+ gnutls_certificate_server_set_request(session->handle, -+ GNUTLS_CERT_REQUEST); -+ } -+ } else { -+ error_setg(errp, "Unsupported TLS credentials type %s", -+ object_get_typename(OBJECT(creds))); -+ goto error; -+ } -+ -+ gnutls_transport_set_ptr(session->handle, session); -+ gnutls_transport_set_push_function(session->handle, -+ qcrypto_tls_session_push); -+ gnutls_transport_set_pull_function(session->handle, -+ qcrypto_tls_session_pull); -+ -+ return session; -+ -+ error: -+ qcrypto_tls_session_free(session); -+ return NULL; -+} -+ -+static int -+qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, -+ Error **errp) -+{ -+ int ret; -+ unsigned int status; -+ const gnutls_datum_t *certs; -+ unsigned int nCerts, i; -+ time_t now; -+ gnutls_x509_crt_t cert = NULL; -+ Error *err = NULL; -+ -+ now = time(NULL); -+ if (now == ((time_t)-1)) { -+ error_setg_errno(errp, errno, "Cannot get current time"); -+ return -1; -+ } -+ -+ ret = gnutls_certificate_verify_peers2(session->handle, &status); -+ if (ret < 0) { -+ error_setg(errp, "Verify failed: %s", gnutls_strerror(ret)); -+ return -1; -+ } -+ -+ if (status != 0) { -+ const char *reason = "Invalid certificate"; -+ -+ if (status & GNUTLS_CERT_INVALID) { -+ reason = "The certificate is not trusted"; -+ } -+ -+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { -+ reason = "The certificate hasn't got a known issuer"; -+ } -+ -+ if (status & GNUTLS_CERT_REVOKED) { -+ reason = "The certificate has been revoked"; -+ } -+ -+ if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { -+ reason = "The certificate uses an insecure algorithm"; -+ } -+ -+ error_setg(errp, "%s", reason); -+ return -1; -+ } -+ -+ certs = gnutls_certificate_get_peers(session->handle, &nCerts); -+ if (!certs) { -+ error_setg(errp, "No certificate peers"); -+ return -1; -+ } -+ -+ for (i = 0; i < nCerts; i++) { -+ ret = gnutls_x509_crt_init(&cert); -+ if (ret < 0) { -+ error_setg(errp, "Cannot initialize certificate: %s", -+ gnutls_strerror(ret)); -+ return -1; -+ } -+ -+ ret = gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER); -+ if (ret < 0) { -+ error_setg(errp, "Cannot import certificate: %s", -+ gnutls_strerror(ret)); -+ goto error; -+ } -+ -+ if (gnutls_x509_crt_get_expiration_time(cert) < now) { -+ error_setg(errp, "The certificate has expired"); -+ goto error; -+ } -+ -+ if (gnutls_x509_crt_get_activation_time(cert) > now) { -+ error_setg(errp, "The certificate is not yet activated"); -+ goto error; -+ } -+ -+ if (gnutls_x509_crt_get_activation_time(cert) > now) { -+ error_setg(errp, "The certificate is not yet activated"); -+ goto error; -+ } -+ -+ if (i == 0) { -+ size_t dnameSize = 1024; -+ session->peername = g_malloc(dnameSize); -+ requery: -+ ret = gnutls_x509_crt_get_dn(cert, session->peername, &dnameSize); -+ if (ret < 0) { -+ if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { -+ session->peername = g_realloc(session->peername, -+ dnameSize); -+ goto requery; -+ } -+ error_setg(errp, "Cannot get client distinguished name: %s", -+ gnutls_strerror(ret)); -+ goto error; -+ } -+ if (session->authzid) { -+ bool allow; -+ -+ allow = qauthz_is_allowed_by_id(session->authzid, -+ session->peername, &err); -+ if (err) { -+ error_propagate(errp, err); -+ goto error; -+ } -+ if (!allow) { -+ error_setg(errp, "TLS x509 authz check for %s is denied", -+ session->peername); -+ goto error; -+ } -+ } -+ if (session->hostname) { -+ if (!gnutls_x509_crt_check_hostname(cert, session->hostname)) { -+ error_setg(errp, -+ "Certificate does not match the hostname %s", -+ session->hostname); -+ goto error; -+ } -+ } else { -+ if (session->creds->endpoint == -+ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { -+ error_setg(errp, "No hostname for certificate validation"); -+ goto error; -+ } -+ } -+ } -+ -+ gnutls_x509_crt_deinit(cert); -+ } -+ -+ return 0; -+ -+ error: -+ gnutls_x509_crt_deinit(cert); -+ return -1; -+} -+ -+ -+int -+qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, -+ Error **errp) -+{ -+ if (object_dynamic_cast(OBJECT(session->creds), -+ TYPE_QCRYPTO_TLS_CREDS_ANON)) { -+ trace_qcrypto_tls_session_check_creds(session, "nop"); -+ return 0; -+ } else if (object_dynamic_cast(OBJECT(session->creds), -+ TYPE_QCRYPTO_TLS_CREDS_PSK)) { -+ trace_qcrypto_tls_session_check_creds(session, "nop"); -+ return 0; -+ } else if (object_dynamic_cast(OBJECT(session->creds), -+ TYPE_QCRYPTO_TLS_CREDS_X509)) { -+ if (session->creds->verifyPeer) { -+ int ret = qcrypto_tls_session_check_certificate(session, -+ errp); -+ trace_qcrypto_tls_session_check_creds(session, -+ ret == 0 ? "pass" : "fail"); -+ return ret; -+ } else { -+ trace_qcrypto_tls_session_check_creds(session, "skip"); -+ return 0; -+ } -+ } else { -+ trace_qcrypto_tls_session_check_creds(session, "error"); -+ error_setg(errp, "Unexpected credential type %s", -+ object_get_typename(OBJECT(session->creds))); -+ return -1; -+ } -+} -+ -+ -+void -+qcrypto_tls_session_set_callbacks(QCryptoTLSSession *session, -+ QCryptoTLSSessionWriteFunc writeFunc, -+ QCryptoTLSSessionReadFunc readFunc, -+ void *opaque) -+{ -+ session->writeFunc = writeFunc; -+ session->readFunc = readFunc; -+ session->opaque = opaque; -+} -+ -+ -+ssize_t -+qcrypto_tls_session_write(QCryptoTLSSession *session, -+ const char *buf, -+ size_t len, -+ Error **errp) -+{ -+ ssize_t ret = gnutls_record_send(session->handle, buf, len); -+ -+ if (ret < 0) { -+ if (ret == GNUTLS_E_AGAIN) { -+ return QCRYPTO_TLS_SESSION_ERR_BLOCK; -+ } else { -+ if (session->werr) { -+ error_propagate(errp, session->werr); -+ session->werr = NULL; -+ } else { -+ error_setg(errp, -+ "Cannot write to TLS channel: %s", -+ gnutls_strerror(ret)); -+ } -+ return -1; -+ } -+ } -+ -+ return ret; -+} -+ -+ -+ssize_t -+qcrypto_tls_session_read(QCryptoTLSSession *session, -+ char *buf, -+ size_t len, -+ bool gracefulTermination, -+ Error **errp) -+{ -+ ssize_t ret = gnutls_record_recv(session->handle, buf, len); -+ -+ if (ret < 0) { -+ if (ret == GNUTLS_E_AGAIN) { -+ return QCRYPTO_TLS_SESSION_ERR_BLOCK; -+ } else if ((ret == GNUTLS_E_PREMATURE_TERMINATION) && -+ gracefulTermination){ -+ return 0; -+ } else { -+ if (session->rerr) { -+ error_propagate(errp, session->rerr); -+ session->rerr = NULL; -+ } else { -+ error_setg(errp, -+ "Cannot read from TLS channel: %s", -+ gnutls_strerror(ret)); -+ } -+ return -1; -+ } -+ } -+ -+ return ret; -+} -+ -+ -+size_t -+qcrypto_tls_session_check_pending(QCryptoTLSSession *session) -+{ -+ return gnutls_record_check_pending(session->handle); -+} -+ -+ -+int -+qcrypto_tls_session_handshake(QCryptoTLSSession *session, -+ Error **errp) -+{ -+ int ret = gnutls_handshake(session->handle); -+ if (ret == 0) { -+ session->handshakeComplete = true; -+ } else { -+ if (ret == GNUTLS_E_INTERRUPTED || -+ ret == GNUTLS_E_AGAIN) { -+ ret = 1; -+ } else { -+ if (session->rerr || session->werr) { -+ error_setg(errp, "TLS handshake failed: %s: %s", -+ gnutls_strerror(ret), -+ error_get_pretty(session->rerr ? -+ session->rerr : session->werr)); -+ } else { -+ error_setg(errp, "TLS handshake failed: %s", -+ gnutls_strerror(ret)); -+ } -+ ret = -1; -+ } -+ } -+ error_free(session->rerr); -+ error_free(session->werr); -+ session->rerr = session->werr = NULL; -+ -+ return ret; -+} -+ -+ -+QCryptoTLSSessionHandshakeStatus -+qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *session) -+{ -+ if (session->handshakeComplete) { -+ return QCRYPTO_TLS_HANDSHAKE_COMPLETE; -+ } else if (gnutls_record_get_direction(session->handle) == 0) { -+ return QCRYPTO_TLS_HANDSHAKE_RECVING; -+ } else { -+ return QCRYPTO_TLS_HANDSHAKE_SENDING; -+ } -+} -+ -+ -+int -+qcrypto_tls_session_get_key_size(QCryptoTLSSession *session, -+ Error **errp) -+{ -+ gnutls_cipher_algorithm_t cipher; -+ int ssf; -+ -+ cipher = gnutls_cipher_get(session->handle); -+ ssf = gnutls_cipher_get_key_size(cipher); -+ if (!ssf) { -+ error_setg(errp, "Cannot get TLS cipher key size"); -+ return -1; -+ } -+ return ssf; -+} -+ -+ -+char * -+qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) -+{ -+ if (session->peername) { -+ return g_strdup(session->peername); -+ } -+ return NULL; -+} -+ -+ -+#else /* ! CONFIG_GNUTLS */ -+ -+ -+QCryptoTLSSession * -+qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, -+ const char *hostname G_GNUC_UNUSED, -+ const char *authzid G_GNUC_UNUSED, -+ QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, -+ Error **errp) -+{ -+ error_setg(errp, "TLS requires GNUTLS support"); -+ return NULL; -+} -+ -+ -+void -+qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) -+{ -+} -+ -+ -+int -+qcrypto_tls_session_check_credentials(QCryptoTLSSession *sess G_GNUC_UNUSED, -+ Error **errp) -+{ -+ error_setg(errp, "TLS requires GNUTLS support"); -+ return -1; -+} -+ -+ -+void -+qcrypto_tls_session_set_callbacks( -+ QCryptoTLSSession *sess G_GNUC_UNUSED, -+ QCryptoTLSSessionWriteFunc writeFunc G_GNUC_UNUSED, -+ QCryptoTLSSessionReadFunc readFunc G_GNUC_UNUSED, -+ void *opaque G_GNUC_UNUSED) -+{ -+} -+ -+ -+ssize_t -+qcrypto_tls_session_write(QCryptoTLSSession *sess, -+ const char *buf, -+ size_t len, -+ Error **errp) -+{ -+ error_setg(errp, "TLS requires GNUTLS support"); -+ return -1; -+} -+ -+ -+ssize_t -+qcrypto_tls_session_read(QCryptoTLSSession *sess, -+ char *buf, -+ size_t len, -+ bool gracefulTermination, -+ Error **errp) -+{ -+ error_setg(errp, "TLS requires GNUTLS support"); -+ return -1; -+} -+ -+ -+size_t -+qcrypto_tls_session_check_pending(QCryptoTLSSession *session) -+{ -+ return 0; -+} -+ -+ -+int -+qcrypto_tls_session_handshake(QCryptoTLSSession *sess, -+ Error **errp) -+{ -+ error_setg(errp, "TLS requires GNUTLS support"); -+ return -1; -+} -+ -+ -+QCryptoTLSSessionHandshakeStatus -+qcrypto_tls_session_get_handshake_status(QCryptoTLSSession *sess) -+{ -+ return QCRYPTO_TLS_HANDSHAKE_COMPLETE; -+} -+ -+ -+int -+qcrypto_tls_session_get_key_size(QCryptoTLSSession *sess, -+ Error **errp) -+{ -+ error_setg(errp, "TLS requires GNUTLS support"); -+ return -1; -+} -+ -+ -+char * -+qcrypto_tls_session_get_peer_name(QCryptoTLSSession *sess) -+{ -+ return NULL; -+} -+ -+#endif diff --git a/qcow2/lib/hw/block/block.c b/qcow2/lib/hw/block/block.c new file mode 100644 index 00000000..3ceca7dc @@ -77618,519 +74094,6 @@ index 00000000..608bcf06 +} + +type_init(qio_channel_socket_register_types); -diff --git a/qcow2/lib/io/channel-tls.c b/qcow2/lib/io/channel-tls.c -new file mode 100644 -index 00000000..aab630e5 ---- /dev/null -+++ b/qcow2/lib/io/channel-tls.c -@@ -0,0 +1,507 @@ -+/* -+ * QEMU I/O channels TLS driver -+ * -+ * Copyright (c) 2015 Red Hat, Inc. -+ * -+ * This library is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU Lesser General Public -+ * License as published by the Free Software Foundation; either -+ * version 2.1 of the License, or (at your option) any later version. -+ * -+ * This library is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * Lesser General Public License for more details. -+ * -+ * You should have received a copy of the GNU Lesser General Public -+ * License along with this library; if not, see . -+ * -+ */ -+ -+#include "qemu/osdep.h" -+#include "qapi/error.h" -+#include "qemu/module.h" -+#include "io/channel-tls.h" -+#include "trace.h" -+#include "qemu/atomic.h" -+ -+ -+static ssize_t qio_channel_tls_write_handler(const char *buf, -+ size_t len, -+ void *opaque, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); -+ ssize_t ret; -+ -+ ret = qio_channel_write(tioc->master, buf, len, errp); -+ if (ret == QIO_CHANNEL_ERR_BLOCK) { -+ return QCRYPTO_TLS_SESSION_ERR_BLOCK; -+ } else if (ret < 0) { -+ return -1; -+ } -+ return ret; -+} -+ -+static ssize_t qio_channel_tls_read_handler(char *buf, -+ size_t len, -+ void *opaque, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(opaque); -+ ssize_t ret; -+ -+ ret = qio_channel_read(tioc->master, buf, len, errp); -+ if (ret == QIO_CHANNEL_ERR_BLOCK) { -+ return QCRYPTO_TLS_SESSION_ERR_BLOCK; -+ } else if (ret < 0) { -+ return -1; -+ } -+ return ret; -+} -+ -+ -+QIOChannelTLS * -+qio_channel_tls_new_server(QIOChannel *master, -+ QCryptoTLSCreds *creds, -+ const char *aclname, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc; -+ QIOChannel *ioc; -+ -+ tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); -+ ioc = QIO_CHANNEL(tioc); -+ -+ tioc->master = master; -+ ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; -+ if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { -+ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); -+ } -+ object_ref(OBJECT(master)); -+ -+ tioc->session = qcrypto_tls_session_new( -+ creds, -+ NULL, -+ aclname, -+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, -+ errp); -+ if (!tioc->session) { -+ goto error; -+ } -+ -+ qcrypto_tls_session_set_callbacks( -+ tioc->session, -+ qio_channel_tls_write_handler, -+ qio_channel_tls_read_handler, -+ tioc); -+ -+ trace_qio_channel_tls_new_server(tioc, master, creds, aclname); -+ return tioc; -+ -+ error: -+ object_unref(OBJECT(tioc)); -+ return NULL; -+} -+ -+QIOChannelTLS * -+qio_channel_tls_new_client(QIOChannel *master, -+ QCryptoTLSCreds *creds, -+ const char *hostname, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc; -+ QIOChannel *ioc; -+ -+ tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS)); -+ ioc = QIO_CHANNEL(tioc); -+ -+ tioc->master = master; -+ ioc->follow_coroutine_ctx = master->follow_coroutine_ctx; -+ if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) { -+ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN); -+ } -+ object_ref(OBJECT(master)); -+ -+ tioc->session = qcrypto_tls_session_new( -+ creds, -+ hostname, -+ NULL, -+ QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, -+ errp); -+ if (!tioc->session) { -+ goto error; -+ } -+ -+ qcrypto_tls_session_set_callbacks( -+ tioc->session, -+ qio_channel_tls_write_handler, -+ qio_channel_tls_read_handler, -+ tioc); -+ -+ trace_qio_channel_tls_new_client(tioc, master, creds, hostname); -+ return tioc; -+ -+ error: -+ object_unref(OBJECT(tioc)); -+ return NULL; -+} -+ -+struct QIOChannelTLSData { -+ QIOTask *task; -+ GMainContext *context; -+}; -+typedef struct QIOChannelTLSData QIOChannelTLSData; -+ -+static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, -+ GIOCondition condition, -+ gpointer user_data); -+ -+static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, -+ QIOTask *task, -+ GMainContext *context) -+{ -+ Error *err = NULL; -+ QCryptoTLSSessionHandshakeStatus status; -+ -+ if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { -+ trace_qio_channel_tls_handshake_fail(ioc); -+ qio_task_set_error(task, err); -+ qio_task_complete(task); -+ return; -+ } -+ -+ status = qcrypto_tls_session_get_handshake_status(ioc->session); -+ if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { -+ trace_qio_channel_tls_handshake_complete(ioc); -+ if (qcrypto_tls_session_check_credentials(ioc->session, -+ &err) < 0) { -+ trace_qio_channel_tls_credentials_deny(ioc); -+ qio_task_set_error(task, err); -+ } else { -+ trace_qio_channel_tls_credentials_allow(ioc); -+ } -+ qio_task_complete(task); -+ } else { -+ GIOCondition condition; -+ QIOChannelTLSData *data = g_new0(typeof(*data), 1); -+ -+ data->task = task; -+ data->context = context; -+ -+ if (context) { -+ g_main_context_ref(context); -+ } -+ -+ if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) { -+ condition = G_IO_OUT; -+ } else { -+ condition = G_IO_IN; -+ } -+ -+ trace_qio_channel_tls_handshake_pending(ioc, status); -+ ioc->hs_ioc_tag = -+ qio_channel_add_watch_full(ioc->master, -+ condition, -+ qio_channel_tls_handshake_io, -+ data, -+ NULL, -+ context); -+ } -+} -+ -+ -+static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, -+ GIOCondition condition, -+ gpointer user_data) -+{ -+ QIOChannelTLSData *data = user_data; -+ QIOTask *task = data->task; -+ GMainContext *context = data->context; -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS( -+ qio_task_get_source(task)); -+ -+ tioc->hs_ioc_tag = 0; -+ g_free(data); -+ qio_channel_tls_handshake_task(tioc, task, context); -+ -+ if (context) { -+ g_main_context_unref(context); -+ } -+ -+ return FALSE; -+} -+ -+void qio_channel_tls_handshake(QIOChannelTLS *ioc, -+ QIOTaskFunc func, -+ gpointer opaque, -+ GDestroyNotify destroy, -+ GMainContext *context) -+{ -+ QIOTask *task; -+ -+ task = qio_task_new(OBJECT(ioc), -+ func, opaque, destroy); -+ -+ trace_qio_channel_tls_handshake_start(ioc); -+ qio_channel_tls_handshake_task(ioc, task, context); -+} -+ -+ -+static void qio_channel_tls_init(Object *obj G_GNUC_UNUSED) -+{ -+} -+ -+ -+static void qio_channel_tls_finalize(Object *obj) -+{ -+ QIOChannelTLS *ioc = QIO_CHANNEL_TLS(obj); -+ -+ object_unref(OBJECT(ioc->master)); -+ qcrypto_tls_session_free(ioc->session); -+} -+ -+ -+static ssize_t qio_channel_tls_readv(QIOChannel *ioc, -+ const struct iovec *iov, -+ size_t niov, -+ int **fds, -+ size_t *nfds, -+ int flags, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ size_t i; -+ ssize_t got = 0; -+ -+ for (i = 0 ; i < niov ; i++) { -+ ssize_t ret = qcrypto_tls_session_read( -+ tioc->session, -+ iov[i].iov_base, -+ iov[i].iov_len, -+ qatomic_load_acquire(&tioc->shutdown) & QIO_CHANNEL_SHUTDOWN_READ, -+ errp); -+ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { -+ if (got) { -+ return got; -+ } else { -+ return QIO_CHANNEL_ERR_BLOCK; -+ } -+ } else if (ret < 0) { -+ return -1; -+ } -+ got += ret; -+ if (ret < iov[i].iov_len) { -+ break; -+ } -+ } -+ return got; -+} -+ -+ -+static ssize_t qio_channel_tls_writev(QIOChannel *ioc, -+ const struct iovec *iov, -+ size_t niov, -+ int *fds, -+ size_t nfds, -+ int flags, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ size_t i; -+ ssize_t done = 0; -+ -+ for (i = 0 ; i < niov ; i++) { -+ ssize_t ret = qcrypto_tls_session_write(tioc->session, -+ iov[i].iov_base, -+ iov[i].iov_len, -+ errp); -+ if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { -+ if (done) { -+ return done; -+ } else { -+ return QIO_CHANNEL_ERR_BLOCK; -+ } -+ } else if (ret < 0) { -+ return -1; -+ } -+ done += ret; -+ if (ret < iov[i].iov_len) { -+ break; -+ } -+ } -+ return done; -+} -+ -+static int qio_channel_tls_set_blocking(QIOChannel *ioc, -+ bool enabled, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ -+ return qio_channel_set_blocking(tioc->master, enabled, errp); -+} -+ -+static void qio_channel_tls_set_delay(QIOChannel *ioc, -+ bool enabled) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ -+ qio_channel_set_delay(tioc->master, enabled); -+} -+ -+static void qio_channel_tls_set_cork(QIOChannel *ioc, -+ bool enabled) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ -+ qio_channel_set_cork(tioc->master, enabled); -+} -+ -+static int qio_channel_tls_shutdown(QIOChannel *ioc, -+ QIOChannelShutdown how, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ -+ qatomic_or(&tioc->shutdown, how); -+ -+ return qio_channel_shutdown(tioc->master, how, errp); -+} -+ -+static int qio_channel_tls_close(QIOChannel *ioc, -+ Error **errp) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ -+ if (tioc->hs_ioc_tag) { -+ trace_qio_channel_tls_handshake_cancel(ioc); -+ g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); -+ } -+ -+ return qio_channel_close(tioc->master, errp); -+} -+ -+static void qio_channel_tls_set_aio_fd_handler(QIOChannel *ioc, -+ AioContext *read_ctx, -+ IOHandler *io_read, -+ AioContext *write_ctx, -+ IOHandler *io_write, -+ void *opaque) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ -+ qio_channel_set_aio_fd_handler(tioc->master, read_ctx, io_read, -+ write_ctx, io_write, opaque); -+} -+ -+typedef struct QIOChannelTLSSource QIOChannelTLSSource; -+struct QIOChannelTLSSource { -+ GSource parent; -+ QIOChannelTLS *tioc; -+}; -+ -+static gboolean -+qio_channel_tls_source_check(GSource *source) -+{ -+ QIOChannelTLSSource *tsource = (QIOChannelTLSSource *)source; -+ -+ return qcrypto_tls_session_check_pending(tsource->tioc->session) > 0; -+} -+ -+static gboolean -+qio_channel_tls_source_prepare(GSource *source, gint *timeout) -+{ -+ *timeout = -1; -+ return qio_channel_tls_source_check(source); -+} -+ -+static gboolean -+qio_channel_tls_source_dispatch(GSource *source, GSourceFunc callback, -+ gpointer user_data) -+{ -+ return G_SOURCE_CONTINUE; -+} -+ -+static void -+qio_channel_tls_source_finalize(GSource *source) -+{ -+ QIOChannelTLSSource *tsource = (QIOChannelTLSSource *)source; -+ -+ object_unref(OBJECT(tsource->tioc)); -+} -+ -+static GSourceFuncs qio_channel_tls_source_funcs = { -+ qio_channel_tls_source_prepare, -+ qio_channel_tls_source_check, -+ qio_channel_tls_source_dispatch, -+ qio_channel_tls_source_finalize -+}; -+ -+static void -+qio_channel_tls_read_watch(QIOChannelTLS *tioc, GSource *source) -+{ -+ GSource *child; -+ QIOChannelTLSSource *tlssource; -+ -+ child = g_source_new(&qio_channel_tls_source_funcs, -+ sizeof(QIOChannelTLSSource)); -+ tlssource = (QIOChannelTLSSource *)child; -+ -+ tlssource->tioc = tioc; -+ object_ref(OBJECT(tioc)); -+ -+ g_source_add_child_source(source, child); -+ g_source_unref(child); -+} -+ -+static GSource *qio_channel_tls_create_watch(QIOChannel *ioc, -+ GIOCondition condition) -+{ -+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); -+ GSource *source = qio_channel_create_watch(tioc->master, condition); -+ -+ if (condition & G_IO_IN) { -+ qio_channel_tls_read_watch(tioc, source); -+ } -+ -+ return source; -+} -+ -+QCryptoTLSSession * -+qio_channel_tls_get_session(QIOChannelTLS *ioc) -+{ -+ return ioc->session; -+} -+ -+static void qio_channel_tls_class_init(ObjectClass *klass, -+ void *class_data G_GNUC_UNUSED) -+{ -+ QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); -+ -+ ioc_klass->io_writev = qio_channel_tls_writev; -+ ioc_klass->io_readv = qio_channel_tls_readv; -+ ioc_klass->io_set_blocking = qio_channel_tls_set_blocking; -+ ioc_klass->io_set_delay = qio_channel_tls_set_delay; -+ ioc_klass->io_set_cork = qio_channel_tls_set_cork; -+ ioc_klass->io_close = qio_channel_tls_close; -+ ioc_klass->io_shutdown = qio_channel_tls_shutdown; -+ ioc_klass->io_create_watch = qio_channel_tls_create_watch; -+ ioc_klass->io_set_aio_fd_handler = qio_channel_tls_set_aio_fd_handler; -+} -+ -+static const TypeInfo qio_channel_tls_info = { -+ .parent = TYPE_QIO_CHANNEL, -+ .name = TYPE_QIO_CHANNEL_TLS, -+ .instance_size = sizeof(QIOChannelTLS), -+ .instance_init = qio_channel_tls_init, -+ .instance_finalize = qio_channel_tls_finalize, -+ .class_init = qio_channel_tls_class_init, -+}; -+ -+static void qio_channel_tls_register_types(void) -+{ -+ type_register_static(&qio_channel_tls_info); -+} -+ -+type_init(qio_channel_tls_register_types); diff --git a/qcow2/lib/io/channel-util.c b/qcow2/lib/io/channel-util.c new file mode 100644 index 00000000..4b340d46 @@ -81077,2384 +77040,6 @@ index 00000000..660ce22c + job_unref_locked(job); + return ret; +} -diff --git a/qcow2/lib/nbd/client-connection.c b/qcow2/lib/nbd/client-connection.c -new file mode 100644 -index 00000000..f9da67c8 ---- /dev/null -+++ b/qcow2/lib/nbd/client-connection.c -@@ -0,0 +1,422 @@ -+/* -+ * QEMU Block driver for NBD -+ * -+ * Copyright (c) 2021 Virtuozzo International GmbH. -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+ -+#include "qemu/osdep.h" -+#include "trace.h" -+ -+#include "block/nbd.h" -+ -+#include "qapi/qapi-visit-sockets.h" -+#include "qapi/clone-visitor.h" -+#include "qemu/coroutine.h" -+ -+struct NBDClientConnection { -+ /* Initialization constants, never change */ -+ SocketAddress *saddr; /* address to connect to */ -+ QCryptoTLSCreds *tlscreds; -+ char *tlshostname; -+ NBDExportInfo initial_info; -+ bool do_negotiation; -+ bool do_retry; -+ -+ QemuMutex mutex; -+ -+ NBDExportInfo updated_info; -+ /* -+ * @sioc represents a successful result. While thread is running, @sioc is -+ * used only by thread and not protected by mutex. When thread is not -+ * running, @sioc is stolen by nbd_co_establish_connection() under mutex. -+ */ -+ QIOChannelSocket *sioc; -+ QIOChannel *ioc; -+ /* -+ * @err represents previous attempt. It may be copied by -+ * nbd_co_establish_connection() when it reports failure. -+ */ -+ Error *err; -+ -+ /* All further fields are accessed only under mutex */ -+ bool running; /* thread is running now */ -+ bool detached; /* thread is detached and should cleanup the state */ -+ -+ /* -+ * wait_co: if non-NULL, which coroutine to wake in -+ * nbd_co_establish_connection() after yield() -+ */ -+ Coroutine *wait_co; -+}; -+ -+/* -+ * The function isn't protected by any mutex, only call it when the client -+ * connection attempt has not yet started. -+ */ -+void nbd_client_connection_enable_retry(NBDClientConnection *conn) -+{ -+ conn->do_retry = true; -+} -+ -+NBDClientConnection *nbd_client_connection_new(const SocketAddress *saddr, -+ bool do_negotiation, -+ const char *export_name, -+ const char *x_dirty_bitmap, -+ QCryptoTLSCreds *tlscreds, -+ const char *tlshostname) -+{ -+ NBDClientConnection *conn = g_new(NBDClientConnection, 1); -+ -+ object_ref(OBJECT(tlscreds)); -+ *conn = (NBDClientConnection) { -+ .saddr = QAPI_CLONE(SocketAddress, saddr), -+ .tlscreds = tlscreds, -+ .tlshostname = g_strdup(tlshostname), -+ .do_negotiation = do_negotiation, -+ -+ .initial_info.request_sizes = true, -+ .initial_info.mode = NBD_MODE_EXTENDED, -+ .initial_info.base_allocation = true, -+ .initial_info.x_dirty_bitmap = g_strdup(x_dirty_bitmap), -+ .initial_info.name = g_strdup(export_name ?: "") -+ }; -+ -+ qemu_mutex_init(&conn->mutex); -+ -+ return conn; -+} -+ -+static void nbd_client_connection_do_free(NBDClientConnection *conn) -+{ -+ if (conn->sioc) { -+ qio_channel_close(QIO_CHANNEL(conn->sioc), NULL); -+ object_unref(OBJECT(conn->sioc)); -+ } -+ error_free(conn->err); -+ qapi_free_SocketAddress(conn->saddr); -+ g_free(conn->tlshostname); -+ object_unref(OBJECT(conn->tlscreds)); -+ g_free(conn->initial_info.x_dirty_bitmap); -+ g_free(conn->initial_info.name); -+ g_free(conn); -+} -+ -+/* -+ * Connect to @addr and do NBD negotiation if @info is not null. If @tlscreds -+ * are given @outioc is returned. @outioc is provided only on success. The call -+ * may be cancelled from other thread by simply qio_channel_shutdown(sioc). -+ */ -+static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, -+ NBDExportInfo *info, QCryptoTLSCreds *tlscreds, -+ const char *tlshostname, -+ QIOChannel **outioc, Error **errp) -+{ -+ int ret; -+ -+ if (outioc) { -+ *outioc = NULL; -+ } -+ -+ ret = qio_channel_socket_connect_sync(sioc, addr, errp); -+ if (ret < 0) { -+ return ret; -+ } -+ -+ qio_channel_set_delay(QIO_CHANNEL(sioc), false); -+ -+ if (!info) { -+ return 0; -+ } -+ -+ ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, tlshostname, -+ outioc, info, errp); -+ if (ret < 0) { -+ /* -+ * nbd_receive_negotiate() may setup tls ioc and return it even on -+ * failure path. In this case we should use it instead of original -+ * channel. -+ */ -+ if (outioc && *outioc) { -+ qio_channel_close(*outioc, NULL); -+ object_unref(OBJECT(*outioc)); -+ *outioc = NULL; -+ } else { -+ qio_channel_close(QIO_CHANNEL(sioc), NULL); -+ } -+ -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void *connect_thread_func(void *opaque) -+{ -+ NBDClientConnection *conn = opaque; -+ int ret; -+ bool do_free; -+ uint64_t timeout = 1; -+ uint64_t max_timeout = 16; -+ -+ qemu_mutex_lock(&conn->mutex); -+ while (!conn->detached) { -+ Error *local_err = NULL; -+ -+ assert(!conn->sioc); -+ conn->sioc = qio_channel_socket_new(); -+ -+ qemu_mutex_unlock(&conn->mutex); -+ -+ conn->updated_info = conn->initial_info; -+ -+ ret = nbd_connect(conn->sioc, conn->saddr, -+ conn->do_negotiation ? &conn->updated_info : NULL, -+ conn->tlscreds, conn->tlshostname, -+ &conn->ioc, &local_err); -+ -+ /* -+ * conn->updated_info will finally be returned to the user. Clear the -+ * pointers to our internally allocated strings, which are IN parameters -+ * of nbd_receive_negotiate() and therefore nbd_connect(). Caller -+ * shouldn't be interested in these fields. -+ */ -+ conn->updated_info.x_dirty_bitmap = NULL; -+ conn->updated_info.name = NULL; -+ -+ qemu_mutex_lock(&conn->mutex); -+ -+ error_free(conn->err); -+ conn->err = NULL; -+ error_propagate(&conn->err, local_err); -+ -+ if (ret < 0) { -+ object_unref(OBJECT(conn->sioc)); -+ conn->sioc = NULL; -+ if (conn->do_retry && !conn->detached) { -+ trace_nbd_connect_thread_sleep(timeout); -+ qemu_mutex_unlock(&conn->mutex); -+ -+ sleep(timeout); -+ if (timeout < max_timeout) { -+ timeout *= 2; -+ } -+ -+ qemu_mutex_lock(&conn->mutex); -+ continue; -+ } -+ } -+ -+ break; -+ } -+ -+ /* mutex is locked */ -+ -+ assert(conn->running); -+ conn->running = false; -+ if (conn->wait_co) { -+ aio_co_wake(conn->wait_co); -+ conn->wait_co = NULL; -+ } -+ do_free = conn->detached; -+ -+ qemu_mutex_unlock(&conn->mutex); -+ -+ if (do_free) { -+ nbd_client_connection_do_free(conn); -+ } -+ -+ return NULL; -+} -+ -+void nbd_client_connection_release(NBDClientConnection *conn) -+{ -+ bool do_free = false; -+ -+ if (!conn) { -+ return; -+ } -+ -+ WITH_QEMU_LOCK_GUARD(&conn->mutex) { -+ assert(!conn->detached); -+ if (conn->running) { -+ conn->detached = true; -+ } else { -+ do_free = true; -+ } -+ if (conn->sioc) { -+ qio_channel_shutdown(QIO_CHANNEL(conn->sioc), -+ QIO_CHANNEL_SHUTDOWN_BOTH, NULL); -+ } -+ } -+ -+ if (do_free) { -+ nbd_client_connection_do_free(conn); -+ } -+} -+ -+/* -+ * Get a new connection in context of @conn: -+ * if the thread is running, wait for completion -+ * if the thread already succeeded in the background, and user didn't get the -+ * result, just return it now -+ * otherwise the thread is not running, so start a thread and wait for -+ * completion -+ * -+ * If @blocking is false, don't wait for the thread, return immediately. -+ * -+ * If @info is not NULL, also do nbd-negotiation after successful connection. -+ * In this case info is used only as out parameter, and is fully initialized by -+ * nbd_co_establish_connection(). "IN" fields of info as well as related only to -+ * nbd_receive_export_list() would be zero (see description of NBDExportInfo in -+ * include/block/nbd.h). -+ */ -+QIOChannel *coroutine_fn -+nbd_co_establish_connection(NBDClientConnection *conn, NBDExportInfo *info, -+ bool blocking, Error **errp) -+{ -+ QemuThread thread; -+ -+ if (conn->do_negotiation) { -+ assert(info); -+ } -+ -+ WITH_QEMU_LOCK_GUARD(&conn->mutex) { -+ /* -+ * Don't call nbd_co_establish_connection() in several coroutines in -+ * parallel. Only one call at once is supported. -+ */ -+ assert(!conn->wait_co); -+ -+ if (!conn->running) { -+ if (conn->sioc) { -+ /* Previous attempt finally succeeded in background */ -+ if (conn->do_negotiation) { -+ memcpy(info, &conn->updated_info, sizeof(*info)); -+ if (conn->ioc) { -+ /* TLS channel now has own reference to parent */ -+ object_unref(OBJECT(conn->sioc)); -+ conn->sioc = NULL; -+ -+ return g_steal_pointer(&conn->ioc); -+ } -+ } -+ -+ assert(!conn->ioc); -+ -+ return QIO_CHANNEL(g_steal_pointer(&conn->sioc)); -+ } -+ -+ conn->running = true; -+ qemu_thread_create(&thread, "nbd-connect", -+ connect_thread_func, conn, QEMU_THREAD_DETACHED); -+ } -+ -+ if (!blocking) { -+ if (conn->err) { -+ error_propagate(errp, error_copy(conn->err)); -+ } else { -+ error_setg(errp, "No connection at the moment"); -+ } -+ -+ return NULL; -+ } -+ -+ conn->wait_co = qemu_coroutine_self(); -+ } -+ -+ /* -+ * We are going to wait for connect-thread finish, but -+ * nbd_co_establish_connection_cancel() can interrupt. -+ */ -+ qemu_coroutine_yield(); -+ -+ WITH_QEMU_LOCK_GUARD(&conn->mutex) { -+ if (conn->running) { -+ /* -+ * The connection attempt was canceled and the coroutine resumed -+ * before the connection thread finished its job. Report the -+ * attempt as failed, but leave the connection thread running, -+ * to reuse it for the next connection attempt. -+ */ -+ if (conn->err) { -+ error_propagate(errp, error_copy(conn->err)); -+ } else { -+ /* -+ * The only possible case here is cancelling by open_timer -+ * during nbd_open(). So, the error message is for that case. -+ * If we have more use cases, we can refactor -+ * nbd_co_establish_connection_cancel() to take an additional -+ * parameter cancel_reason, that would be passed than to the -+ * caller of cancelled nbd_co_establish_connection(). -+ */ -+ error_setg(errp, "Connection attempt cancelled by timeout"); -+ } -+ -+ return NULL; -+ } else { -+ /* Thread finished. There must be either error or sioc */ -+ assert(!conn->err != !conn->sioc); -+ -+ if (conn->err) { -+ error_propagate(errp, error_copy(conn->err)); -+ return NULL; -+ } -+ -+ if (conn->do_negotiation) { -+ memcpy(info, &conn->updated_info, sizeof(*info)); -+ if (conn->ioc) { -+ /* TLS channel now has own reference to parent */ -+ object_unref(OBJECT(conn->sioc)); -+ conn->sioc = NULL; -+ -+ return g_steal_pointer(&conn->ioc); -+ } -+ } -+ -+ assert(!conn->ioc); -+ -+ return QIO_CHANNEL(g_steal_pointer(&conn->sioc)); -+ } -+ } -+ -+ abort(); /* unreachable */ -+} -+ -+/* -+ * nbd_co_establish_connection_cancel -+ * Cancel nbd_co_establish_connection() asynchronously. -+ * -+ * Note that this function neither directly stops the thread nor closes the -+ * socket, but rather safely wakes nbd_co_establish_connection() which is -+ * sleeping in yield() -+ */ -+void nbd_co_establish_connection_cancel(NBDClientConnection *conn) -+{ -+ Coroutine *wait_co; -+ -+ WITH_QEMU_LOCK_GUARD(&conn->mutex) { -+ wait_co = g_steal_pointer(&conn->wait_co); -+ } -+ -+ if (wait_co) { -+ aio_co_wake(wait_co); -+ } -+} -diff --git a/qcow2/lib/nbd/client.c b/qcow2/lib/nbd/client.c -new file mode 100644 -index 00000000..c89c7504 ---- /dev/null -+++ b/qcow2/lib/nbd/client.c -@@ -0,0 +1,1589 @@ -+/* -+ * Copyright Red Hat -+ * Copyright (C) 2005 Anthony Liguori -+ * -+ * Network Block Device Client Side -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; under version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, see . -+ */ -+ -+#include "qemu/osdep.h" -+#include "qapi/error.h" -+#include "qemu/queue.h" -+#include "trace.h" -+#include "nbd-internal.h" -+#include "qemu/cutils.h" -+ -+/* Definitions for opaque data types */ -+ -+static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); -+ -+/* That's all folks */ -+ -+/* Basic flow for negotiation -+ -+ Server Client -+ Negotiate -+ -+ or -+ -+ Server Client -+ Negotiate #1 -+ Option -+ Negotiate #2 -+ -+ ---- -+ -+ followed by -+ -+ Server Client -+ Request -+ Response -+ Request -+ Response -+ ... -+ ... -+ Request (type == 2) -+ -+*/ -+ -+/* Send an option request. -+ * -+ * The request is for option @opt, with @data containing @len bytes of -+ * additional payload for the request (@len may be -1 to treat @data as -+ * a C string; and @data may be NULL if @len is 0). -+ * Return 0 if successful, -1 with errp set if it is impossible to -+ * continue. */ -+static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, -+ uint32_t len, const char *data, -+ Error **errp) -+{ -+ ERRP_GUARD(); -+ NBDOption req; -+ QEMU_BUILD_BUG_ON(sizeof(req) != 16); -+ -+ if (len == -1) { -+ req.length = len = strlen(data); -+ } -+ trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); -+ -+ stq_be_p(&req.magic, NBD_OPTS_MAGIC); -+ stl_be_p(&req.option, opt); -+ stl_be_p(&req.length, len); -+ -+ if (nbd_write(ioc, &req, sizeof(req), errp) < 0) { -+ error_prepend(errp, "Failed to send option request header: "); -+ return -1; -+ } -+ -+ if (len && nbd_write(ioc, (char *) data, len, errp) < 0) { -+ error_prepend(errp, "Failed to send option request data: "); -+ return -1; -+ } -+ -+ return 0; -+} -+ -+/* Send NBD_OPT_ABORT as a courtesy to let the server know that we are -+ * not going to attempt further negotiation. */ -+static void nbd_send_opt_abort(QIOChannel *ioc) -+{ -+ /* Technically, a compliant server is supposed to reply to us; but -+ * older servers disconnected instead. At any rate, we're allowed -+ * to disconnect without waiting for the server reply, so we don't -+ * even care if the request makes it to the server, let alone -+ * waiting around for whether the server replies. */ -+ nbd_send_option_request(ioc, NBD_OPT_ABORT, 0, NULL, NULL); -+} -+ -+ -+/* Receive the header of an option reply, which should match the given -+ * opt. Read through the length field, but NOT the length bytes of -+ * payload. Return 0 if successful, -1 with errp set if it is -+ * impossible to continue. */ -+static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, -+ NBDOptionReply *reply, Error **errp) -+{ -+ QEMU_BUILD_BUG_ON(sizeof(*reply) != 20); -+ if (nbd_read(ioc, reply, sizeof(*reply), "option reply", errp) < 0) { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ reply->magic = be64_to_cpu(reply->magic); -+ reply->option = be32_to_cpu(reply->option); -+ reply->type = be32_to_cpu(reply->type); -+ reply->length = be32_to_cpu(reply->length); -+ -+ trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), -+ reply->type, nbd_rep_lookup(reply->type), -+ reply->length); -+ -+ if (reply->magic != NBD_REP_MAGIC) { -+ error_setg(errp, "Unexpected option reply magic"); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (reply->option != opt) { -+ error_setg(errp, "Unexpected option type %u (%s), expected %u (%s)", -+ reply->option, nbd_opt_lookup(reply->option), -+ opt, nbd_opt_lookup(opt)); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ return 0; -+} -+ -+/* -+ * If reply represents success, return 1 without further action. If -+ * reply represents an error, consume the optional payload of the -+ * packet on ioc. Then return 0 for unsupported (so the client can -+ * fall back to other approaches), where @strict determines if only -+ * ERR_UNSUP or all errors fit that category, or -1 with errp set for -+ * other errors. -+ */ -+static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, -+ bool strict, Error **errp) -+{ -+ ERRP_GUARD(); -+ g_autofree char *msg = NULL; -+ -+ if (!(reply->type & (1 << 31))) { -+ return 1; -+ } -+ -+ if (reply->length) { -+ if (reply->length > NBD_MAX_BUFFER_SIZE) { -+ error_setg(errp, "server error %" PRIu32 -+ " (%s) message is too long", -+ reply->type, nbd_rep_lookup(reply->type)); -+ goto err; -+ } -+ msg = g_malloc(reply->length + 1); -+ if (nbd_read(ioc, msg, reply->length, NULL, errp) < 0) { -+ error_prepend(errp, "Failed to read option error %" PRIu32 -+ " (%s) message: ", -+ reply->type, nbd_rep_lookup(reply->type)); -+ goto err; -+ } -+ msg[reply->length] = '\0'; -+ trace_nbd_server_error_msg(reply->type, -+ nbd_reply_type_lookup(reply->type), msg); -+ } -+ -+ if (reply->type == NBD_REP_ERR_UNSUP || !strict) { -+ trace_nbd_reply_err_ignored(reply->option, -+ nbd_opt_lookup(reply->option), -+ reply->type, nbd_rep_lookup(reply->type)); -+ return 0; -+ } -+ -+ switch (reply->type) { -+ case NBD_REP_ERR_POLICY: -+ error_setg(errp, "Denied by server for option %" PRIu32 " (%s)", -+ reply->option, nbd_opt_lookup(reply->option)); -+ break; -+ -+ case NBD_REP_ERR_INVALID: -+ error_setg(errp, "Invalid parameters for option %" PRIu32 " (%s)", -+ reply->option, nbd_opt_lookup(reply->option)); -+ break; -+ -+ case NBD_REP_ERR_PLATFORM: -+ error_setg(errp, "Server lacks support for option %" PRIu32 " (%s)", -+ reply->option, nbd_opt_lookup(reply->option)); -+ break; -+ -+ case NBD_REP_ERR_TLS_REQD: -+ error_setg(errp, "TLS negotiation required before option %" PRIu32 -+ " (%s)", reply->option, nbd_opt_lookup(reply->option)); -+ error_append_hint(errp, "Did you forget a valid tls-creds?\n"); -+ break; -+ -+ case NBD_REP_ERR_UNKNOWN: -+ error_setg(errp, "Requested export not available"); -+ break; -+ -+ case NBD_REP_ERR_SHUTDOWN: -+ error_setg(errp, "Server shutting down before option %" PRIu32 " (%s)", -+ reply->option, nbd_opt_lookup(reply->option)); -+ break; -+ -+ case NBD_REP_ERR_BLOCK_SIZE_REQD: -+ error_setg(errp, "Server requires INFO_BLOCK_SIZE for option %" PRIu32 -+ " (%s)", reply->option, nbd_opt_lookup(reply->option)); -+ break; -+ -+ default: -+ error_setg(errp, "Unknown error code when asking for option %" PRIu32 -+ " (%s)", reply->option, nbd_opt_lookup(reply->option)); -+ break; -+ } -+ -+ if (msg) { -+ error_append_hint(errp, "server reported: %s\n", msg); -+ } -+ -+ err: -+ nbd_send_opt_abort(ioc); -+ return -1; -+} -+ -+/* nbd_receive_list: -+ * Process another portion of the NBD_OPT_LIST reply, populating any -+ * name received into *@name. If @description is non-NULL, and the -+ * server provided a description, that is also populated. The caller -+ * must eventually call g_free() on success. -+ * Returns 1 if name and description were set and iteration must continue, -+ * 0 if iteration is complete (including if OPT_LIST unsupported), -+ * -1 with @errp set if an unrecoverable error occurred. -+ */ -+static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, -+ Error **errp) -+{ -+ NBDOptionReply reply; -+ uint32_t len; -+ uint32_t namelen; -+ g_autofree char *local_name = NULL; -+ g_autofree char *local_desc = NULL; -+ int error; -+ -+ if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) { -+ return -1; -+ } -+ error = nbd_handle_reply_err(ioc, &reply, true, errp); -+ if (error <= 0) { -+ return error; -+ } -+ len = reply.length; -+ -+ if (reply.type == NBD_REP_ACK) { -+ if (len != 0) { -+ error_setg(errp, "length too long for option end"); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ return 0; -+ } else if (reply.type != NBD_REP_SERVER) { -+ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)", -+ reply.type, nbd_rep_lookup(reply.type), -+ NBD_REP_SERVER, nbd_rep_lookup(NBD_REP_SERVER)); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ -+ if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) { -+ error_setg(errp, "incorrect option length %" PRIu32, len); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (nbd_read32(ioc, &namelen, "option name length", errp) < 0) { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ len -= sizeof(namelen); -+ if (len < namelen || namelen > NBD_MAX_STRING_SIZE) { -+ error_setg(errp, "incorrect name length in server's list response"); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ -+ local_name = g_malloc(namelen + 1); -+ if (nbd_read(ioc, local_name, namelen, "export name", errp) < 0) { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ local_name[namelen] = '\0'; -+ len -= namelen; -+ if (len) { -+ if (len > NBD_MAX_STRING_SIZE) { -+ error_setg(errp, "incorrect description length in server's " -+ "list response"); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ local_desc = g_malloc(len + 1); -+ if (nbd_read(ioc, local_desc, len, "export description", errp) < 0) { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ local_desc[len] = '\0'; -+ } -+ -+ trace_nbd_receive_list(local_name, local_desc ?: ""); -+ *name = g_steal_pointer(&local_name); -+ if (description) { -+ *description = g_steal_pointer(&local_desc); -+ } -+ return 1; -+} -+ -+ -+/* -+ * nbd_opt_info_or_go: -+ * Send option for NBD_OPT_INFO or NBD_OPT_GO and parse the reply. -+ * Returns -1 if the option proves the export @info->name cannot be -+ * used, 0 if the option is unsupported (fall back to NBD_OPT_LIST and -+ * NBD_OPT_EXPORT_NAME in that case), and > 0 if the export is good to -+ * go (with the rest of @info populated). -+ */ -+static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, -+ NBDExportInfo *info, Error **errp) -+{ -+ ERRP_GUARD(); -+ NBDOptionReply reply; -+ uint32_t len = strlen(info->name); -+ uint16_t type; -+ int error; -+ char *buf; -+ -+ /* The protocol requires that the server send NBD_INFO_EXPORT with -+ * a non-zero flags (at least NBD_FLAG_HAS_FLAGS must be set); so -+ * flags still 0 is a witness of a broken server. */ -+ info->flags = 0; -+ -+ assert(opt == NBD_OPT_GO || opt == NBD_OPT_INFO); -+ trace_nbd_opt_info_go_start(nbd_opt_lookup(opt), info->name); -+ buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); -+ stl_be_p(buf, len); -+ memcpy(buf + 4, info->name, len); -+ /* At most one request, everything else up to server */ -+ stw_be_p(buf + 4 + len, info->request_sizes); -+ if (info->request_sizes) { -+ stw_be_p(buf + 4 + len + 2, NBD_INFO_BLOCK_SIZE); -+ } -+ error = nbd_send_option_request(ioc, opt, -+ 4 + len + 2 + 2 * info->request_sizes, -+ buf, errp); -+ g_free(buf); -+ if (error < 0) { -+ return -1; -+ } -+ -+ while (1) { -+ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { -+ return -1; -+ } -+ error = nbd_handle_reply_err(ioc, &reply, true, errp); -+ if (error <= 0) { -+ return error; -+ } -+ len = reply.length; -+ -+ if (reply.type == NBD_REP_ACK) { -+ /* -+ * Server is done sending info, and moved into transmission -+ * phase for NBD_OPT_GO, but make sure it sent flags -+ */ -+ if (len) { -+ error_setg(errp, "server sent invalid NBD_REP_ACK"); -+ return -1; -+ } -+ if (!info->flags) { -+ error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); -+ return -1; -+ } -+ trace_nbd_opt_info_go_success(nbd_opt_lookup(opt)); -+ return 1; -+ } -+ if (reply.type != NBD_REP_INFO) { -+ error_setg(errp, "unexpected reply type %u (%s), expected %u (%s)", -+ reply.type, nbd_rep_lookup(reply.type), -+ NBD_REP_INFO, nbd_rep_lookup(NBD_REP_INFO)); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (len < sizeof(type)) { -+ error_setg(errp, "NBD_REP_INFO length %" PRIu32 " is too short", -+ len); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (nbd_read16(ioc, &type, "info type", errp) < 0) { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ len -= sizeof(type); -+ switch (type) { -+ case NBD_INFO_EXPORT: -+ if (len != sizeof(info->size) + sizeof(info->flags)) { -+ error_setg(errp, "remaining export info len %" PRIu32 -+ " is unexpected size", len); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (nbd_read64(ioc, &info->size, "info size", errp) < 0) { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (nbd_read16(ioc, &info->flags, "info flags", errp) < 0) { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (info->min_block && -+ !QEMU_IS_ALIGNED(info->size, info->min_block)) { -+ error_setg(errp, "export size %" PRIu64 " is not multiple of " -+ "minimum block size %" PRIu32, info->size, -+ info->min_block); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ trace_nbd_receive_negotiate_size_flags(info->size, info->flags); -+ break; -+ -+ case NBD_INFO_BLOCK_SIZE: -+ if (len != sizeof(info->min_block) * 3) { -+ error_setg(errp, "remaining export info len %" PRIu32 -+ " is unexpected size", len); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (nbd_read32(ioc, &info->min_block, "info minimum block size", -+ errp) < 0) { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (!is_power_of_2(info->min_block)) { -+ error_setg(errp, "server minimum block size %" PRIu32 -+ " is not a power of two", info->min_block); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (nbd_read32(ioc, &info->opt_block, "info preferred block size", -+ errp) < 0) -+ { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (!is_power_of_2(info->opt_block) || -+ info->opt_block < info->min_block) { -+ error_setg(errp, "server preferred block size %" PRIu32 -+ " is not valid", info->opt_block); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (nbd_read32(ioc, &info->max_block, "info maximum block size", -+ errp) < 0) -+ { -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ if (info->max_block < info->min_block) { -+ error_setg(errp, "server maximum block size %" PRIu32 -+ " is not valid", info->max_block); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ trace_nbd_opt_info_block_size(info->min_block, info->opt_block, -+ info->max_block); -+ break; -+ -+ default: -+ /* -+ * Not worth the bother to check if NBD_INFO_NAME or -+ * NBD_INFO_DESCRIPTION exceed NBD_MAX_STRING_SIZE. -+ */ -+ trace_nbd_opt_info_unknown(type, nbd_info_lookup(type)); -+ if (nbd_drop(ioc, len, errp) < 0) { -+ error_prepend(errp, "Failed to read info payload: "); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ break; -+ } -+ } -+} -+ -+/* Return -1 on failure, 0 if wantname is an available export. */ -+static int nbd_receive_query_exports(QIOChannel *ioc, -+ const char *wantname, -+ Error **errp) -+{ -+ bool list_empty = true; -+ bool found_export = false; -+ -+ trace_nbd_receive_query_exports_start(wantname); -+ if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) { -+ return -1; -+ } -+ -+ while (1) { -+ char *name; -+ int ret = nbd_receive_list(ioc, &name, NULL, errp); -+ -+ if (ret < 0) { -+ /* Server gave unexpected reply */ -+ return -1; -+ } else if (ret == 0) { -+ /* Done iterating. */ -+ if (list_empty) { -+ /* -+ * We don't have enough context to tell a server that -+ * sent an empty list apart from a server that does -+ * not support the list command; but as this function -+ * is just used to trigger a nicer error message -+ * before trying NBD_OPT_EXPORT_NAME, assume the -+ * export is available. -+ */ -+ return 0; -+ } else if (!found_export) { -+ error_setg(errp, "No export with name '%s' available", -+ wantname); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ trace_nbd_receive_query_exports_success(wantname); -+ return 0; -+ } -+ list_empty = false; -+ if (!strcmp(name, wantname)) { -+ found_export = true; -+ } -+ g_free(name); -+ } -+} -+ -+/* -+ * nbd_request_simple_option: Send an option request, and parse the reply. -+ * @strict controls whether ERR_UNSUP or all errors produce 0 status. -+ * return 1 for successful negotiation, -+ * 0 if operation is unsupported, -+ * -1 with errp set for any other error -+ */ -+static int nbd_request_simple_option(QIOChannel *ioc, int opt, bool strict, -+ Error **errp) -+{ -+ NBDOptionReply reply; -+ int error; -+ -+ if (nbd_send_option_request(ioc, opt, 0, NULL, errp) < 0) { -+ return -1; -+ } -+ -+ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { -+ return -1; -+ } -+ error = nbd_handle_reply_err(ioc, &reply, strict, errp); -+ if (error <= 0) { -+ return error; -+ } -+ -+ if (reply.type != NBD_REP_ACK) { -+ error_setg(errp, "Server answered option %d (%s) with unexpected " -+ "reply %" PRIu32 " (%s)", opt, nbd_opt_lookup(opt), -+ reply.type, nbd_rep_lookup(reply.type)); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ -+ if (reply.length != 0) { -+ error_setg(errp, "Option %d ('%s') response length is %" PRIu32 -+ " (it should be zero)", opt, nbd_opt_lookup(opt), -+ reply.length); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ -+ return 1; -+} -+ -+/* Callback to learn when QIO TLS upgrade is complete */ -+struct NBDTLSClientHandshakeData { -+ bool complete; -+ Error *error; -+ GMainLoop *loop; -+}; -+ -+static void nbd_client_tls_handshake(QIOTask *task, void *opaque) -+{ -+ struct NBDTLSClientHandshakeData *data = opaque; -+ -+ qio_task_propagate_error(task, &data->error); -+ data->complete = true; -+ if (data->loop) { -+ g_main_loop_quit(data->loop); -+ } -+} -+ -+static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, -+ QCryptoTLSCreds *tlscreds, -+ const char *hostname, Error **errp) -+{ -+ int ret; -+ QIOChannelTLS *tioc; -+ struct NBDTLSClientHandshakeData data = { 0 }; -+ -+ ret = nbd_request_simple_option(ioc, NBD_OPT_STARTTLS, true, errp); -+ if (ret <= 0) { -+ if (ret == 0) { -+ error_setg(errp, "Server don't support STARTTLS option"); -+ nbd_send_opt_abort(ioc); -+ } -+ return NULL; -+ } -+ -+ trace_nbd_receive_starttls_new_client(); -+ tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp); -+ if (!tioc) { -+ return NULL; -+ } -+ qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls"); -+ trace_nbd_receive_starttls_tls_handshake(); -+ qio_channel_tls_handshake(tioc, -+ nbd_client_tls_handshake, -+ &data, -+ NULL, -+ NULL); -+ -+ if (!data.complete) { -+ data.loop = g_main_loop_new(g_main_context_default(), FALSE); -+ g_main_loop_run(data.loop); -+ assert(data.complete); -+ g_main_loop_unref(data.loop); -+ } -+ -+ if (data.error) { -+ error_propagate(errp, data.error); -+ object_unref(OBJECT(tioc)); -+ return NULL; -+ } -+ -+ return QIO_CHANNEL(tioc); -+} -+ -+/* -+ * nbd_send_meta_query: -+ * Send 0 or 1 set/list meta context queries. -+ * Return 0 on success, -1 with errp set for any error -+ */ -+static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt, -+ const char *export, const char *query, -+ Error **errp) -+{ -+ int ret; -+ uint32_t export_len; -+ uint32_t queries = !!query; -+ uint32_t query_len = 0; -+ uint32_t data_len; -+ char *data; -+ char *p; -+ -+ assert(strnlen(export, NBD_MAX_STRING_SIZE + 1) <= NBD_MAX_STRING_SIZE); -+ export_len = strlen(export); -+ data_len = sizeof(export_len) + export_len + sizeof(queries); -+ if (query) { -+ assert(strnlen(query, NBD_MAX_STRING_SIZE + 1) <= NBD_MAX_STRING_SIZE); -+ query_len = strlen(query); -+ data_len += sizeof(query_len) + query_len; -+ } else { -+ assert(opt == NBD_OPT_LIST_META_CONTEXT); -+ } -+ p = data = g_malloc(data_len); -+ -+ trace_nbd_opt_meta_request(nbd_opt_lookup(opt), query ?: "(all)", export); -+ stl_be_p(p, export_len); -+ memcpy(p += sizeof(export_len), export, export_len); -+ stl_be_p(p += export_len, queries); -+ if (query) { -+ stl_be_p(p += sizeof(queries), query_len); -+ memcpy(p += sizeof(query_len), query, query_len); -+ } -+ -+ ret = nbd_send_option_request(ioc, opt, data_len, data, errp); -+ g_free(data); -+ return ret; -+} -+ -+/* -+ * nbd_receive_one_meta_context: -+ * Called in a loop to receive and trace one set/list meta context reply. -+ * Pass non-NULL @name or @id to collect results back to the caller, which -+ * must eventually call g_free(). -+ * return 1 if name is set and iteration must continue, -+ * 0 if iteration is complete (including if option is unsupported), -+ * -1 with errp set for any error -+ */ -+static int nbd_receive_one_meta_context(QIOChannel *ioc, -+ uint32_t opt, -+ char **name, -+ uint32_t *id, -+ Error **errp) -+{ -+ int ret; -+ NBDOptionReply reply; -+ char *local_name = NULL; -+ uint32_t local_id; -+ -+ if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { -+ return -1; -+ } -+ -+ ret = nbd_handle_reply_err(ioc, &reply, false, errp); -+ if (ret <= 0) { -+ return ret; -+ } -+ -+ if (reply.type == NBD_REP_ACK) { -+ if (reply.length != 0) { -+ error_setg(errp, "Unexpected length to ACK response"); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ return 0; -+ } else if (reply.type != NBD_REP_META_CONTEXT) { -+ error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)", -+ reply.type, nbd_rep_lookup(reply.type), -+ NBD_REP_META_CONTEXT, nbd_rep_lookup(NBD_REP_META_CONTEXT)); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ -+ if (reply.length <= sizeof(local_id) || -+ reply.length > NBD_MAX_BUFFER_SIZE) { -+ error_setg(errp, "Failed to negotiate meta context, server " -+ "answered with unexpected length %" PRIu32, -+ reply.length); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ -+ if (nbd_read32(ioc, &local_id, "context id", errp) < 0) { -+ return -1; -+ } -+ -+ reply.length -= sizeof(local_id); -+ local_name = g_malloc(reply.length + 1); -+ if (nbd_read(ioc, local_name, reply.length, "context name", errp) < 0) { -+ g_free(local_name); -+ return -1; -+ } -+ local_name[reply.length] = '\0'; -+ trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id); -+ -+ if (name) { -+ *name = local_name; -+ } else { -+ g_free(local_name); -+ } -+ if (id) { -+ *id = local_id; -+ } -+ return 1; -+} -+ -+/* -+ * nbd_negotiate_simple_meta_context: -+ * Request the server to set the meta context for export @info->name -+ * using @info->x_dirty_bitmap with a fallback to "base:allocation", -+ * setting @info->context_id to the resulting id. Fail if the server -+ * responds with more than one context or with a context different -+ * than the query. -+ * return 1 for successful negotiation, -+ * 0 if operation is unsupported, -+ * -1 with errp set for any other error -+ */ -+static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, -+ NBDExportInfo *info, -+ Error **errp) -+{ -+ /* -+ * TODO: Removing the x_dirty_bitmap hack will mean refactoring -+ * this function to request and store ids for multiple contexts -+ * (both base:allocation and a dirty bitmap), at which point this -+ * function should lose the term _simple. -+ */ -+ int ret; -+ const char *context = info->x_dirty_bitmap ?: "base:allocation"; -+ bool received = false; -+ char *name = NULL; -+ -+ if (nbd_send_meta_query(ioc, NBD_OPT_SET_META_CONTEXT, -+ info->name, context, errp) < 0) { -+ return -1; -+ } -+ -+ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT, -+ &name, &info->context_id, errp); -+ if (ret < 0) { -+ return -1; -+ } -+ if (ret == 1) { -+ if (strcmp(context, name)) { -+ error_setg(errp, "Failed to negotiate meta context '%s', server " -+ "answered with different context '%s'", context, -+ name); -+ g_free(name); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ g_free(name); -+ received = true; -+ -+ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT, -+ NULL, NULL, errp); -+ if (ret < 0) { -+ return -1; -+ } -+ } -+ if (ret != 0) { -+ error_setg(errp, "Server answered with more than one context"); -+ nbd_send_opt_abort(ioc); -+ return -1; -+ } -+ return received; -+} -+ -+/* -+ * nbd_list_meta_contexts: -+ * Request the server to list all meta contexts for export @info->name. -+ * return 0 if list is complete (even if empty), -+ * -1 with errp set for any error -+ */ -+static int nbd_list_meta_contexts(QIOChannel *ioc, -+ NBDExportInfo *info, -+ Error **errp) -+{ -+ int ret; -+ int seen_any = false; -+ int seen_qemu = false; -+ -+ if (nbd_send_meta_query(ioc, NBD_OPT_LIST_META_CONTEXT, -+ info->name, NULL, errp) < 0) { -+ return -1; -+ } -+ -+ while (1) { -+ char *context; -+ -+ ret = nbd_receive_one_meta_context(ioc, NBD_OPT_LIST_META_CONTEXT, -+ &context, NULL, errp); -+ if (ret == 0 && seen_any && !seen_qemu) { -+ /* -+ * Work around qemu 3.0 bug: the server forgot to send -+ * "qemu:" replies to 0 queries. If we saw at least one -+ * reply (probably base:allocation), but none of them were -+ * qemu:, then run a more specific query to make sure. -+ */ -+ seen_qemu = true; -+ if (nbd_send_meta_query(ioc, NBD_OPT_LIST_META_CONTEXT, -+ info->name, "qemu:", errp) < 0) { -+ return -1; -+ } -+ continue; -+ } -+ if (ret <= 0) { -+ return ret; -+ } -+ seen_any = true; -+ seen_qemu |= strstart(context, "qemu:", NULL); -+ info->contexts = g_renew(char *, info->contexts, ++info->n_contexts); -+ info->contexts[info->n_contexts - 1] = context; -+ } -+} -+ -+/* -+ * nbd_start_negotiate: -+ * Start the handshake to the server. After a positive return, the server -+ * is ready to accept additional NBD_OPT requests. -+ * Returns: negative errno: failure talking to server -+ * non-negative: enum NBDMode describing server abilities -+ */ -+static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, -+ const char *hostname, QIOChannel **outioc, -+ NBDMode max_mode, bool *zeroes, -+ Error **errp) -+{ -+ ERRP_GUARD(); -+ uint64_t magic; -+ -+ trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); -+ -+ if (zeroes) { -+ *zeroes = true; -+ } -+ if (outioc) { -+ *outioc = NULL; -+ } -+ if (tlscreds && !outioc) { -+ error_setg(errp, "Output I/O channel required for TLS"); -+ return -EINVAL; -+ } -+ -+ if (nbd_read64(ioc, &magic, "initial magic", errp) < 0) { -+ return -EINVAL; -+ } -+ trace_nbd_receive_negotiate_magic(magic); -+ -+ if (magic != NBD_INIT_MAGIC) { -+ error_setg(errp, "Bad initial magic received: 0x%" PRIx64, magic); -+ return -EINVAL; -+ } -+ -+ if (nbd_read64(ioc, &magic, "server magic", errp) < 0) { -+ return -EINVAL; -+ } -+ trace_nbd_receive_negotiate_magic(magic); -+ -+ if (magic == NBD_OPTS_MAGIC) { -+ uint32_t clientflags = 0; -+ uint16_t globalflags; -+ bool fixedNewStyle = false; -+ -+ if (nbd_read16(ioc, &globalflags, "server flags", errp) < 0) { -+ return -EINVAL; -+ } -+ trace_nbd_receive_negotiate_server_flags(globalflags); -+ if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { -+ fixedNewStyle = true; -+ clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; -+ } -+ if (globalflags & NBD_FLAG_NO_ZEROES) { -+ if (zeroes) { -+ *zeroes = false; -+ } -+ clientflags |= NBD_FLAG_C_NO_ZEROES; -+ } -+ /* client requested flags */ -+ clientflags = cpu_to_be32(clientflags); -+ if (nbd_write(ioc, &clientflags, sizeof(clientflags), errp) < 0) { -+ error_prepend(errp, "Failed to send clientflags field: "); -+ return -EINVAL; -+ } -+ if (tlscreds) { -+ if (fixedNewStyle) { -+ *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp); -+ if (!*outioc) { -+ return -EINVAL; -+ } -+ ioc = *outioc; -+ } else { -+ error_setg(errp, "Server does not support STARTTLS"); -+ return -EINVAL; -+ } -+ } -+ if (fixedNewStyle) { -+ int result = 0; -+ -+ if (max_mode >= NBD_MODE_EXTENDED) { -+ result = nbd_request_simple_option(ioc, -+ NBD_OPT_EXTENDED_HEADERS, -+ false, errp); -+ if (result) { -+ return result < 0 ? -EINVAL : NBD_MODE_EXTENDED; -+ } -+ } -+ if (max_mode >= NBD_MODE_STRUCTURED) { -+ result = nbd_request_simple_option(ioc, -+ NBD_OPT_STRUCTURED_REPLY, -+ false, errp); -+ if (result) { -+ return result < 0 ? -EINVAL : NBD_MODE_STRUCTURED; -+ } -+ } -+ return NBD_MODE_SIMPLE; -+ } else { -+ return NBD_MODE_EXPORT_NAME; -+ } -+ } else if (magic == NBD_CLIENT_MAGIC) { -+ if (tlscreds) { -+ error_setg(errp, "Server does not support STARTTLS"); -+ return -EINVAL; -+ } -+ return NBD_MODE_OLDSTYLE; -+ } else { -+ error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic); -+ return -EINVAL; -+ } -+} -+ -+/* -+ * nbd_negotiate_finish_oldstyle: -+ * Populate @info with the size and export flags from an oldstyle server, -+ * but does not consume 124 bytes of reserved zero padding. -+ * Returns 0 on success, -1 with @errp set on failure -+ */ -+static int nbd_negotiate_finish_oldstyle(QIOChannel *ioc, NBDExportInfo *info, -+ Error **errp) -+{ -+ uint32_t oldflags; -+ -+ if (nbd_read64(ioc, &info->size, "export length", errp) < 0) { -+ return -EINVAL; -+ } -+ -+ if (nbd_read32(ioc, &oldflags, "export flags", errp) < 0) { -+ return -EINVAL; -+ } -+ if (oldflags & ~0xffff) { -+ error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); -+ return -EINVAL; -+ } -+ info->flags = oldflags; -+ return 0; -+} -+ -+/* -+ * nbd_receive_negotiate: -+ * Connect to server, complete negotiation, and move into transmission phase. -+ * Returns: negative errno: failure talking to server -+ * 0: server is connected -+ */ -+int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, -+ const char *hostname, QIOChannel **outioc, -+ NBDExportInfo *info, Error **errp) -+{ -+ ERRP_GUARD(); -+ int result; -+ bool zeroes; -+ bool base_allocation = info->base_allocation; -+ -+ assert(info->name && strlen(info->name) <= NBD_MAX_STRING_SIZE); -+ trace_nbd_receive_negotiate_name(info->name); -+ -+ result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, -+ info->mode, &zeroes, errp); -+ if (result < 0) { -+ return result; -+ } -+ -+ info->mode = result; -+ info->base_allocation = false; -+ if (tlscreds && *outioc) { -+ ioc = *outioc; -+ } -+ -+ switch (info->mode) { -+ case NBD_MODE_EXTENDED: -+ case NBD_MODE_STRUCTURED: -+ if (base_allocation) { -+ result = nbd_negotiate_simple_meta_context(ioc, info, errp); -+ if (result < 0) { -+ return -EINVAL; -+ } -+ info->base_allocation = result == 1; -+ } -+ /* fall through */ -+ case NBD_MODE_SIMPLE: -+ /* Try NBD_OPT_GO first - if it works, we are done (it -+ * also gives us a good message if the server requires -+ * TLS). If it is not available, fall back to -+ * NBD_OPT_LIST for nicer error messages about a missing -+ * export, then use NBD_OPT_EXPORT_NAME. */ -+ result = nbd_opt_info_or_go(ioc, NBD_OPT_GO, info, errp); -+ if (result < 0) { -+ return -EINVAL; -+ } -+ if (result > 0) { -+ return 0; -+ } -+ /* Check our desired export is present in the -+ * server export list. Since NBD_OPT_EXPORT_NAME -+ * cannot return an error message, running this -+ * query gives us better error reporting if the -+ * export name is not available. -+ */ -+ if (nbd_receive_query_exports(ioc, info->name, errp) < 0) { -+ return -EINVAL; -+ } -+ /* fall through */ -+ case NBD_MODE_EXPORT_NAME: -+ /* write the export name request */ -+ if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, info->name, -+ errp) < 0) { -+ return -EINVAL; -+ } -+ -+ /* Read the response */ -+ if (nbd_read64(ioc, &info->size, "export length", errp) < 0) { -+ return -EINVAL; -+ } -+ -+ if (nbd_read16(ioc, &info->flags, "export flags", errp) < 0) { -+ return -EINVAL; -+ } -+ break; -+ case NBD_MODE_OLDSTYLE: -+ if (*info->name) { -+ error_setg(errp, "Server does not support non-empty export names"); -+ return -EINVAL; -+ } -+ if (nbd_negotiate_finish_oldstyle(ioc, info, errp) < 0) { -+ return -EINVAL; -+ } -+ break; -+ default: -+ g_assert_not_reached(); -+ } -+ -+ trace_nbd_receive_negotiate_size_flags(info->size, info->flags); -+ if (zeroes && nbd_drop(ioc, 124, errp) < 0) { -+ error_prepend(errp, "Failed to read reserved block: "); -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+/* Clean up result of nbd_receive_export_list */ -+void nbd_free_export_list(NBDExportInfo *info, int count) -+{ -+ int i, j; -+ -+ if (!info) { -+ return; -+ } -+ -+ for (i = 0; i < count; i++) { -+ g_free(info[i].name); -+ g_free(info[i].description); -+ for (j = 0; j < info[i].n_contexts; j++) { -+ g_free(info[i].contexts[j]); -+ } -+ g_free(info[i].contexts); -+ } -+ g_free(info); -+} -+ -+/* -+ * nbd_receive_export_list: -+ * Query details about a server's exports, then disconnect without -+ * going into transmission phase. Return a count of the exports listed -+ * in @info by the server, or -1 on error. Caller must free @info using -+ * nbd_free_export_list(). -+ */ -+int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, -+ const char *hostname, NBDExportInfo **info, -+ Error **errp) -+{ -+ int result; -+ int count = 0; -+ int i; -+ int rc; -+ int ret = -1; -+ NBDExportInfo *array = NULL; -+ QIOChannel *sioc = NULL; -+ -+ *info = NULL; -+ result = nbd_start_negotiate(ioc, tlscreds, hostname, &sioc, -+ NBD_MODE_EXTENDED, NULL, errp); -+ if (tlscreds && sioc) { -+ ioc = sioc; -+ } -+ if (result < 0) { -+ goto out; -+ } -+ -+ switch ((NBDMode)result) { -+ case NBD_MODE_SIMPLE: -+ case NBD_MODE_STRUCTURED: -+ case NBD_MODE_EXTENDED: -+ /* newstyle - use NBD_OPT_LIST to populate array, then try -+ * NBD_OPT_INFO on each array member. If structured replies -+ * are enabled, also try NBD_OPT_LIST_META_CONTEXT. */ -+ if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) { -+ goto out; -+ } -+ while (1) { -+ char *name; -+ char *desc; -+ -+ rc = nbd_receive_list(ioc, &name, &desc, errp); -+ if (rc < 0) { -+ goto out; -+ } else if (rc == 0) { -+ break; -+ } -+ array = g_renew(NBDExportInfo, array, ++count); -+ memset(&array[count - 1], 0, sizeof(*array)); -+ array[count - 1].name = name; -+ array[count - 1].description = desc; -+ array[count - 1].mode = result; -+ } -+ -+ for (i = 0; i < count; i++) { -+ array[i].request_sizes = true; -+ rc = nbd_opt_info_or_go(ioc, NBD_OPT_INFO, &array[i], errp); -+ if (rc < 0) { -+ goto out; -+ } else if (rc == 0) { -+ /* -+ * Pointless to try rest of loop. If OPT_INFO doesn't work, -+ * it's unlikely that meta contexts work either -+ */ -+ break; -+ } -+ -+ if (result >= NBD_MODE_STRUCTURED && -+ nbd_list_meta_contexts(ioc, &array[i], errp) < 0) { -+ goto out; -+ } -+ } -+ -+ /* Send NBD_OPT_ABORT as a courtesy before hanging up */ -+ nbd_send_opt_abort(ioc); -+ break; -+ case NBD_MODE_EXPORT_NAME: -+ error_setg(errp, "Server does not support export lists"); -+ /* We can't even send NBD_OPT_ABORT, so merely hang up */ -+ goto out; -+ case NBD_MODE_OLDSTYLE: -+ /* Lone export name is implied, but we can parse length and flags */ -+ array = g_new0(NBDExportInfo, 1); -+ array->name = g_strdup(""); -+ array->mode = NBD_MODE_OLDSTYLE; -+ count = 1; -+ -+ if (nbd_negotiate_finish_oldstyle(ioc, array, errp) < 0) { -+ goto out; -+ } -+ -+ /* Send NBD_CMD_DISC as a courtesy to the server, but ignore all -+ * errors now that we have the information we wanted. */ -+ if (nbd_drop(ioc, 124, NULL) == 0) { -+ NBDRequest request = { .type = NBD_CMD_DISC, .mode = result }; -+ -+ nbd_send_request(ioc, &request); -+ } -+ break; -+ default: -+ g_assert_not_reached(); -+ } -+ -+ *info = array; -+ array = NULL; -+ ret = count; -+ -+ out: -+ qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); -+ qio_channel_close(ioc, NULL); -+ object_unref(OBJECT(sioc)); -+ nbd_free_export_list(array, count); -+ return ret; -+} -+ -+#ifdef __linux__ -+int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, -+ Error **errp) -+{ -+ unsigned long sector_size = MAX(BDRV_SECTOR_SIZE, info->min_block); -+ unsigned long sectors = info->size / sector_size; -+ -+ /* FIXME: Once the kernel module is patched to honor block sizes, -+ * and to advertise that fact to user space, we should update the -+ * hand-off to the kernel to use any block sizes we learned. */ -+ assert(!info->request_sizes); -+ if (info->size / sector_size != sectors) { -+ error_setg(errp, "Export size %" PRIu64 " too large for 32-bit kernel", -+ info->size); -+ return -E2BIG; -+ } -+ -+ trace_nbd_init_set_socket(); -+ -+ if (ioctl(fd, NBD_SET_SOCK, (unsigned long) sioc->fd) < 0) { -+ int serrno = errno; -+ error_setg(errp, "Failed to set NBD socket"); -+ return -serrno; -+ } -+ -+ trace_nbd_init_set_block_size(sector_size); -+ -+ if (ioctl(fd, NBD_SET_BLKSIZE, sector_size) < 0) { -+ int serrno = errno; -+ error_setg(errp, "Failed setting NBD block size"); -+ return -serrno; -+ } -+ -+ trace_nbd_init_set_size(sectors); -+ if (info->size % sector_size) { -+ trace_nbd_init_trailing_bytes(info->size % sector_size); -+ } -+ -+ if (ioctl(fd, NBD_SET_SIZE_BLOCKS, sectors) < 0) { -+ int serrno = errno; -+ error_setg(errp, "Failed setting size (in blocks)"); -+ return -serrno; -+ } -+ -+ if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) info->flags) < 0) { -+ if (errno == ENOTTY) { -+ int read_only = (info->flags & NBD_FLAG_READ_ONLY) != 0; -+ trace_nbd_init_set_readonly(); -+ -+ if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { -+ int serrno = errno; -+ error_setg(errp, "Failed setting read-only attribute"); -+ return -serrno; -+ } -+ } else { -+ int serrno = errno; -+ error_setg(errp, "Failed setting flags"); -+ return -serrno; -+ } -+ } -+ -+ trace_nbd_init_finish(); -+ -+ return 0; -+} -+ -+int nbd_client(int fd) -+{ -+ int ret; -+ int serrno; -+ -+ trace_nbd_client_loop(); -+ -+ ret = ioctl(fd, NBD_DO_IT); -+ if (ret < 0 && errno == EPIPE) { -+ /* NBD_DO_IT normally returns EPIPE when someone has disconnected -+ * the socket via NBD_DISCONNECT. We do not want to return 1 in -+ * that case. -+ */ -+ ret = 0; -+ } -+ serrno = errno; -+ -+ trace_nbd_client_loop_ret(ret, strerror(serrno)); -+ -+ trace_nbd_client_clear_queue(); -+ ioctl(fd, NBD_CLEAR_QUE); -+ -+ trace_nbd_client_clear_socket(); -+ ioctl(fd, NBD_CLEAR_SOCK); -+ -+ errno = serrno; -+ return ret; -+} -+ -+int nbd_disconnect(int fd) -+{ -+ ioctl(fd, NBD_CLEAR_QUE); -+ ioctl(fd, NBD_DISCONNECT); -+ ioctl(fd, NBD_CLEAR_SOCK); -+ return 0; -+} -+ -+#endif /* __linux__ */ -+ -+int nbd_send_request(QIOChannel *ioc, NBDRequest *request) -+{ -+ uint8_t buf[NBD_EXTENDED_REQUEST_SIZE]; -+ size_t len; -+ -+ trace_nbd_send_request(request->from, request->len, request->cookie, -+ request->flags, request->type, -+ nbd_cmd_lookup(request->type)); -+ -+ stw_be_p(buf + 4, request->flags); -+ stw_be_p(buf + 6, request->type); -+ stq_be_p(buf + 8, request->cookie); -+ stq_be_p(buf + 16, request->from); -+ if (request->mode >= NBD_MODE_EXTENDED) { -+ stl_be_p(buf, NBD_EXTENDED_REQUEST_MAGIC); -+ stq_be_p(buf + 24, request->len); -+ len = NBD_EXTENDED_REQUEST_SIZE; -+ } else { -+ assert(request->len <= UINT32_MAX); -+ stl_be_p(buf, NBD_REQUEST_MAGIC); -+ stl_be_p(buf + 24, request->len); -+ len = NBD_REQUEST_SIZE; -+ } -+ -+ return nbd_write(ioc, buf, len, NULL); -+} -+ -+/* nbd_receive_simple_reply -+ * Read simple reply except magic field (which should be already read). -+ * Payload is not read (payload is possible for CMD_READ, but here we even -+ * don't know whether it take place or not). -+ */ -+static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply, -+ Error **errp) -+{ -+ int ret; -+ -+ assert(reply->magic == NBD_SIMPLE_REPLY_MAGIC); -+ -+ ret = nbd_read(ioc, (uint8_t *)reply + sizeof(reply->magic), -+ sizeof(*reply) - sizeof(reply->magic), "reply", errp); -+ if (ret < 0) { -+ return ret; -+ } -+ -+ reply->error = be32_to_cpu(reply->error); -+ reply->cookie = be64_to_cpu(reply->cookie); -+ -+ return 0; -+} -+ -+/* nbd_receive_reply_chunk_header -+ * Read structured reply chunk except magic field (which should be already -+ * read). Normalize into the compact form. -+ * Payload is not read. -+ */ -+static int nbd_receive_reply_chunk_header(QIOChannel *ioc, NBDReply *chunk, -+ Error **errp) -+{ -+ int ret; -+ size_t len; -+ uint64_t payload_len; -+ -+ if (chunk->magic == NBD_STRUCTURED_REPLY_MAGIC) { -+ len = sizeof(chunk->structured); -+ } else { -+ assert(chunk->magic == NBD_EXTENDED_REPLY_MAGIC); -+ len = sizeof(chunk->extended); -+ } -+ -+ ret = nbd_read(ioc, (uint8_t *)chunk + sizeof(chunk->magic), -+ len - sizeof(chunk->magic), "structured chunk", -+ errp); -+ if (ret < 0) { -+ return ret; -+ } -+ -+ /* flags, type, and cookie occupy same space between forms */ -+ chunk->structured.flags = be16_to_cpu(chunk->structured.flags); -+ chunk->structured.type = be16_to_cpu(chunk->structured.type); -+ chunk->structured.cookie = be64_to_cpu(chunk->structured.cookie); -+ -+ /* -+ * Because we use BLOCK_STATUS with REQ_ONE, and cap READ requests -+ * at 32M, no valid server should send us payload larger than -+ * this. Even if we stopped using REQ_ONE, sane servers will cap -+ * the number of extents they return for block status. -+ */ -+ if (chunk->magic == NBD_STRUCTURED_REPLY_MAGIC) { -+ payload_len = be32_to_cpu(chunk->structured.length); -+ } else { -+ /* For now, we are ignoring the extended header offset. */ -+ payload_len = be64_to_cpu(chunk->extended.length); -+ chunk->magic = NBD_STRUCTURED_REPLY_MAGIC; -+ } -+ if (payload_len > NBD_MAX_BUFFER_SIZE + sizeof(NBDStructuredReadData)) { -+ error_setg(errp, "server chunk %" PRIu32 " (%s) payload is too long", -+ chunk->structured.type, -+ nbd_rep_lookup(chunk->structured.type)); -+ return -EINVAL; -+ } -+ chunk->structured.length = payload_len; -+ -+ return 0; -+} -+ -+/* nbd_read_eof -+ * Tries to read @size bytes from @ioc. -+ * Returns 1 on success -+ * 0 on eof, when no data was read (errp is not set) -+ * negative errno on failure (errp is set) -+ */ -+static inline int coroutine_fn -+nbd_read_eof(BlockDriverState *bs, QIOChannel *ioc, void *buffer, size_t size, -+ Error **errp) -+{ -+ bool partial = false; -+ -+ assert(size); -+ while (size > 0) { -+ struct iovec iov = { .iov_base = buffer, .iov_len = size }; -+ ssize_t len; -+ -+ len = qio_channel_readv(ioc, &iov, 1, errp); -+ if (len == QIO_CHANNEL_ERR_BLOCK) { -+ qio_channel_yield(ioc, G_IO_IN); -+ continue; -+ } else if (len < 0) { -+ return -EIO; -+ } else if (len == 0) { -+ if (partial) { -+ error_setg(errp, -+ "Unexpected end-of-file before all bytes were read"); -+ return -EIO; -+ } else { -+ return 0; -+ } -+ } -+ -+ partial = true; -+ size -= len; -+ buffer = (uint8_t*) buffer + len; -+ } -+ return 1; -+} -+ -+/* nbd_receive_reply -+ * -+ * Wait for a new reply. If this yields, the coroutine must be able to be -+ * safely reentered for nbd_client_attach_aio_context(). @mode determines -+ * which reply magic we are expecting, although this normalizes the result -+ * so that the caller only has to work with compact headers. -+ * -+ * Returns 1 on success -+ * 0 on eof, when no data was read -+ * negative errno on failure -+ */ -+int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, -+ NBDReply *reply, NBDMode mode, Error **errp) -+{ -+ int ret; -+ const char *type; -+ uint32_t expected; -+ -+ ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp); -+ if (ret <= 0) { -+ return ret; -+ } -+ -+ reply->magic = be32_to_cpu(reply->magic); -+ -+ /* Diagnose but accept wrong-width header */ -+ switch (reply->magic) { -+ case NBD_SIMPLE_REPLY_MAGIC: -+ if (mode >= NBD_MODE_EXTENDED) { -+ trace_nbd_receive_wrong_header(reply->magic, -+ nbd_mode_lookup(mode)); -+ } -+ ret = nbd_receive_simple_reply(ioc, &reply->simple, errp); -+ if (ret < 0) { -+ return ret; -+ } -+ trace_nbd_receive_simple_reply(reply->simple.error, -+ nbd_err_lookup(reply->simple.error), -+ reply->cookie); -+ break; -+ case NBD_STRUCTURED_REPLY_MAGIC: -+ case NBD_EXTENDED_REPLY_MAGIC: -+ expected = mode >= NBD_MODE_EXTENDED ? NBD_EXTENDED_REPLY_MAGIC -+ : NBD_STRUCTURED_REPLY_MAGIC; -+ if (reply->magic != expected) { -+ trace_nbd_receive_wrong_header(reply->magic, -+ nbd_mode_lookup(mode)); -+ } -+ ret = nbd_receive_reply_chunk_header(ioc, reply, errp); -+ if (ret < 0) { -+ return ret; -+ } -+ type = nbd_reply_type_lookup(reply->structured.type); -+ trace_nbd_receive_reply_chunk_header(reply->structured.flags, -+ reply->structured.type, type, -+ reply->structured.cookie, -+ reply->structured.length); -+ break; -+ default: -+ trace_nbd_receive_wrong_header(reply->magic, nbd_mode_lookup(mode)); -+ error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", reply->magic); -+ return -EINVAL; -+ } -+ -+ return 1; -+} -+ -diff --git a/qcow2/lib/nbd/common.c b/qcow2/lib/nbd/common.c -new file mode 100644 -index 00000000..589a748c ---- /dev/null -+++ b/qcow2/lib/nbd/common.c -@@ -0,0 +1,266 @@ -+/* -+ * Copyright (C) 2005 Anthony Liguori -+ * -+ * Network Block Device Common Code -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; under version 2 of the License. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, see . -+ */ -+ -+#include "qemu/osdep.h" -+#include "trace.h" -+#include "nbd-internal.h" -+ -+/* Discard length bytes from channel. Return -errno on failure and 0 on -+ * success */ -+int nbd_drop(QIOChannel *ioc, size_t size, Error **errp) -+{ -+ ssize_t ret = 0; -+ char small[1024]; -+ char *buffer; -+ -+ buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size)); -+ while (size > 0) { -+ ssize_t count = MIN(65536, size); -+ ret = nbd_read(ioc, buffer, MIN(65536, size), NULL, errp); -+ -+ if (ret < 0) { -+ goto cleanup; -+ } -+ size -= count; -+ } -+ -+ cleanup: -+ if (buffer != small) { -+ g_free(buffer); -+ } -+ return ret; -+} -+ -+ -+const char *nbd_opt_lookup(uint32_t opt) -+{ -+ switch (opt) { -+ case NBD_OPT_EXPORT_NAME: -+ return "export name"; -+ case NBD_OPT_ABORT: -+ return "abort"; -+ case NBD_OPT_LIST: -+ return "list"; -+ case NBD_OPT_STARTTLS: -+ return "starttls"; -+ case NBD_OPT_INFO: -+ return "info"; -+ case NBD_OPT_GO: -+ return "go"; -+ case NBD_OPT_STRUCTURED_REPLY: -+ return "structured reply"; -+ case NBD_OPT_LIST_META_CONTEXT: -+ return "list meta context"; -+ case NBD_OPT_SET_META_CONTEXT: -+ return "set meta context"; -+ case NBD_OPT_EXTENDED_HEADERS: -+ return "extended headers"; -+ default: -+ return ""; -+ } -+} -+ -+ -+const char *nbd_rep_lookup(uint32_t rep) -+{ -+ switch (rep) { -+ case NBD_REP_ACK: -+ return "ack"; -+ case NBD_REP_SERVER: -+ return "server"; -+ case NBD_REP_INFO: -+ return "info"; -+ case NBD_REP_META_CONTEXT: -+ return "meta context"; -+ case NBD_REP_ERR_UNSUP: -+ return "unsupported"; -+ case NBD_REP_ERR_POLICY: -+ return "denied by policy"; -+ case NBD_REP_ERR_INVALID: -+ return "invalid"; -+ case NBD_REP_ERR_PLATFORM: -+ return "platform lacks support"; -+ case NBD_REP_ERR_TLS_REQD: -+ return "TLS required"; -+ case NBD_REP_ERR_UNKNOWN: -+ return "export unknown"; -+ case NBD_REP_ERR_SHUTDOWN: -+ return "server shutting down"; -+ case NBD_REP_ERR_BLOCK_SIZE_REQD: -+ return "block size required"; -+ case NBD_REP_ERR_TOO_BIG: -+ return "option payload too big"; -+ case NBD_REP_ERR_EXT_HEADER_REQD: -+ return "extended headers required"; -+ default: -+ return ""; -+ } -+} -+ -+ -+const char *nbd_info_lookup(uint16_t info) -+{ -+ switch (info) { -+ case NBD_INFO_EXPORT: -+ return "export"; -+ case NBD_INFO_NAME: -+ return "name"; -+ case NBD_INFO_DESCRIPTION: -+ return "description"; -+ case NBD_INFO_BLOCK_SIZE: -+ return "block size"; -+ default: -+ return ""; -+ } -+} -+ -+ -+const char *nbd_cmd_lookup(uint16_t cmd) -+{ -+ switch (cmd) { -+ case NBD_CMD_READ: -+ return "read"; -+ case NBD_CMD_WRITE: -+ return "write"; -+ case NBD_CMD_DISC: -+ return "disconnect"; -+ case NBD_CMD_FLUSH: -+ return "flush"; -+ case NBD_CMD_TRIM: -+ return "trim"; -+ case NBD_CMD_CACHE: -+ return "cache"; -+ case NBD_CMD_WRITE_ZEROES: -+ return "write zeroes"; -+ case NBD_CMD_BLOCK_STATUS: -+ return "block status"; -+ default: -+ return ""; -+ } -+} -+ -+ -+const char *nbd_reply_type_lookup(uint16_t type) -+{ -+ switch (type) { -+ case NBD_REPLY_TYPE_NONE: -+ return "none"; -+ case NBD_REPLY_TYPE_OFFSET_DATA: -+ return "data"; -+ case NBD_REPLY_TYPE_OFFSET_HOLE: -+ return "hole"; -+ case NBD_REPLY_TYPE_BLOCK_STATUS: -+ return "block status (32-bit)"; -+ case NBD_REPLY_TYPE_BLOCK_STATUS_EXT: -+ return "block status (64-bit)"; -+ case NBD_REPLY_TYPE_ERROR: -+ return "generic error"; -+ case NBD_REPLY_TYPE_ERROR_OFFSET: -+ return "error at offset"; -+ default: -+ if (type & (1 << 15)) { -+ return ""; -+ } -+ return ""; -+ } -+} -+ -+ -+const char *nbd_err_lookup(int err) -+{ -+ switch (err) { -+ case NBD_SUCCESS: -+ return "success"; -+ case NBD_EPERM: -+ return "EPERM"; -+ case NBD_EIO: -+ return "EIO"; -+ case NBD_ENOMEM: -+ return "ENOMEM"; -+ case NBD_EINVAL: -+ return "EINVAL"; -+ case NBD_ENOSPC: -+ return "ENOSPC"; -+ case NBD_EOVERFLOW: -+ return "EOVERFLOW"; -+ case NBD_ENOTSUP: -+ return "ENOTSUP"; -+ case NBD_ESHUTDOWN: -+ return "ESHUTDOWN"; -+ default: -+ return ""; -+ } -+} -+ -+ -+int nbd_errno_to_system_errno(int err) -+{ -+ int ret; -+ switch (err) { -+ case NBD_SUCCESS: -+ ret = 0; -+ break; -+ case NBD_EPERM: -+ ret = EPERM; -+ break; -+ case NBD_EIO: -+ ret = EIO; -+ break; -+ case NBD_ENOMEM: -+ ret = ENOMEM; -+ break; -+ case NBD_ENOSPC: -+ ret = ENOSPC; -+ break; -+ case NBD_EOVERFLOW: -+ ret = EOVERFLOW; -+ break; -+ case NBD_ENOTSUP: -+ ret = ENOTSUP; -+ break; -+ case NBD_ESHUTDOWN: -+ ret = ESHUTDOWN; -+ break; -+ default: -+ trace_nbd_unknown_error(err); -+ /* fallthrough */ -+ case NBD_EINVAL: -+ ret = EINVAL; -+ break; -+ } -+ return ret; -+} -+ -+ -+const char *nbd_mode_lookup(NBDMode mode) -+{ -+ switch (mode) { -+ case NBD_MODE_OLDSTYLE: -+ return "oldstyle"; -+ case NBD_MODE_EXPORT_NAME: -+ return "export name only"; -+ case NBD_MODE_SIMPLE: -+ return "simple headers"; -+ case NBD_MODE_STRUCTURED: -+ return "structured replies"; -+ case NBD_MODE_EXTENDED: -+ return "extended headers"; -+ default: -+ return ""; -+ } -+} -diff --git a/qcow2/lib/nbd/nbd-internal.h b/qcow2/lib/nbd/nbd-internal.h -new file mode 100644 -index 00000000..91895106 ---- /dev/null -+++ b/qcow2/lib/nbd/nbd-internal.h -@@ -0,0 +1,77 @@ -+/* -+ * NBD Internal Declarations -+ * -+ * Copyright Red Hat -+ * -+ * This work is licensed under the terms of the GNU GPL, version 2 or later. -+ * See the COPYING file in the top-level directory. -+ */ -+ -+#ifndef NBD_INTERNAL_H -+#define NBD_INTERNAL_H -+#include "block/nbd.h" -+#include "sysemu/block-backend.h" -+#include "io/channel-tls.h" -+ -+#include "qemu/iov.h" -+ -+#ifndef _WIN32 -+#include -+#endif -+#ifdef HAVE_SYS_IOCCOM_H -+#include -+#endif -+ -+#ifdef __linux__ -+#include -+#endif -+ -+#include "qemu/bswap.h" -+ -+/* This is all part of the "official" NBD API. -+ * -+ * The most up-to-date documentation is available at: -+ * https://github.com/yoe/nbd/blob/master/doc/proto.md -+ */ -+ -+/* Size of all compact NBD_CMD_*, without payload */ -+#define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4) -+/* Size of all extended NBD_CMD_*, without payload */ -+#define NBD_EXTENDED_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 8) -+ -+/* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */ -+#define NBD_REPLY_SIZE (4 + 4 + 8) -+/* Size of reply to NBD_OPT_EXPORT_NAME */ -+#define NBD_REPLY_EXPORT_NAME_SIZE (8 + 2 + 124) -+/* Size of oldstyle negotiation */ -+#define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124) -+ -+#define NBD_INIT_MAGIC 0x4e42444d41474943LL /* ASCII "NBDMAGIC" */ -+#define NBD_OPTS_MAGIC 0x49484156454F5054LL /* ASCII "IHAVEOPT" */ -+#define NBD_CLIENT_MAGIC 0x0000420281861253LL -+#define NBD_REP_MAGIC 0x0003e889045565a9LL -+ -+#define NBD_SET_SOCK _IO(0xab, 0) -+#define NBD_SET_BLKSIZE _IO(0xab, 1) -+#define NBD_SET_SIZE _IO(0xab, 2) -+#define NBD_DO_IT _IO(0xab, 3) -+#define NBD_CLEAR_SOCK _IO(0xab, 4) -+#define NBD_CLEAR_QUE _IO(0xab, 5) -+#define NBD_PRINT_DEBUG _IO(0xab, 6) -+#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) -+#define NBD_DISCONNECT _IO(0xab, 8) -+#define NBD_SET_TIMEOUT _IO(0xab, 9) -+#define NBD_SET_FLAGS _IO(0xab, 10) -+ -+/* nbd_write -+ * Writes @size bytes to @ioc. Returns 0 on success. -+ */ -+static inline int nbd_write(QIOChannel *ioc, const void *buffer, size_t size, -+ Error **errp) -+{ -+ return qio_channel_write_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; -+} -+ -+int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); -+ -+#endif diff --git a/qcow2/lib/qapi/qapi-clone-visitor.c b/qcow2/lib/qapi/qapi-clone-visitor.c new file mode 100644 index 00000000..bbf95369 @@ -141790,6 +135375,75 @@ index 00000000..b2e26e21 + } + return ret; +} +diff --git a/qcow2/lib/util/qemu-timer-common.c b/qcow2/lib/util/qemu-timer-common.c +new file mode 100644 +index 00000000..cc1326f7 +--- /dev/null ++++ b/qcow2/lib/util/qemu-timer-common.c +@@ -0,0 +1,63 @@ ++/* ++ * QEMU System Emulator ++ * ++ * Copyright (c) 2003-2008 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++#include "qemu/osdep.h" ++#include "qemu/timer.h" ++ ++/***********************************************************/ ++/* real time host monotonic timer */ ++ ++int64_t clock_start; ++ ++#ifdef _WIN32 ++ ++int64_t clock_freq; ++ ++static void __attribute__((constructor)) init_get_clock(void) ++{ ++ LARGE_INTEGER freq; ++ int ret; ++ ret = QueryPerformanceFrequency(&freq); ++ if (ret == 0) { ++ fprintf(stderr, "Could not calibrate ticks\n"); ++ exit(1); ++ } ++ clock_freq = freq.QuadPart; ++ clock_start = get_clock(); ++} ++ ++#else ++ ++int use_rt_clock; ++ ++static void __attribute__((constructor)) init_get_clock(void) ++{ ++ struct timespec ts; ++ ++ use_rt_clock = 0; ++ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { ++ use_rt_clock = 1; ++ } ++ clock_start = get_clock(); ++} ++#endif diff --git a/qcow2/lib/util/qemu-timer.c b/qcow2/lib/util/qemu-timer.c new file mode 100644 index 00000000..6b1533bc diff --git a/SOURCES/0025-libqcow2-build-qcow2-library-for-tapdisk.patch b/SOURCES/0025-libqcow2-build-qcow2-library-for-tapdisk.patch index e0d289e..01073e5 100644 --- a/SOURCES/0025-libqcow2-build-qcow2-library-for-tapdisk.patch +++ b/SOURCES/0025-libqcow2-build-qcow2-library-for-tapdisk.patch @@ -1,4 +1,4 @@ -From f1c5e6517749dcf6114595b3c71faf0bd6e48aef Mon Sep 17 00:00:00 2001 +From 26429f3072a88c392ffade221422f2fc814d0bbf Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 16 Jan 2025 14:09:28 +0100 Subject: [PATCH] libqcow2: build qcow2 library for tapdisk @@ -10,68 +10,48 @@ Signed-off-by: Anthoine Bourgeois --- .gitignore | 4 + Makefile.am | 1 + - configure.ac | 8 + - include/block/aio.h | 9 +- - include/block/block-common.h | 4 +- - include/block/block-global-state.h | 2 + - include/block/block_int-common.h | 2 +- - include/block/block_int-global-state.h | 12 + + configure.ac | 7 + + include/block/aio.h | 7 +- + include/block/trace.h | 12 + include/compiler.h | 2 + include/crypto/hash.h | 20 +- include/hw/block/block.h | 2 +- - include/hw/qdev-core.h | 18 +- + include/hw/qdev-core.h | 16 +- include/list.h | 2 + include/qapi/qapi-commands-block-core.h | 6 +- include/qapi/qapi-commands-block.h | 2 +- include/qapi/qapi-events-block-core.h | 4 +- - include/qapi/qapi-types-block-core.h | 76 ++- + include/qapi/qapi-types-block-core.h | 50 +- include/qapi/qapi-visit-block-core.h | 6 +- include/qapi/qmp/dispatch.h | 2 +- - include/qcow2.h | 22 +- + include/qcow2.h | 4 +- include/qemu/atomic.h | 2 +- - include/qemu/bitops.h | 9 +- + include/qemu/bitops.h | 5 +- include/qemu/config-host.h | 495 ++++++++++++++++++++ include/qemu/hbitmap.h | 4 +- include/qemu/host-utils.h | 6 +- - include/qemu/main-loop.h | 12 +- - include/qemu/option_int.h | 2 +- + include/qemu/main-loop.h | 8 +- include/qemu/osdep.h | 2 +- - include/qemu/range.h | 2 + - include/qemu/timer.h | 6 +- include/sysemu/block-backend-common.h | 4 +- - include/sysemu/block-backend-global-state.h | 2 +- - include/sysemu/block-backend-io.h | 4 + + include/sysemu/block-backend-global-state.h | 2 + + include/trace.h | 191 ++++++++ + include/trace/trace-root.h | 11 + + mk/blktap.spec.in | 1 + qcow2/Makefile.am | 21 + - qcow2/lib/Makefile.am | 166 +++++++ - qcow2/lib/authz/base.c | 4 +- - qcow2/lib/block.c | 28 +- + qcow2/lib/Makefile.am | 159 +++++++ + qcow2/lib/block.c | 20 +- qcow2/lib/block/accounting.c | 3 +- - qcow2/lib/block/block-backend.c | 108 +++-- - qcow2/lib/block/block-gen.c | 2 + - qcow2/lib/block/commit.c | 6 +- - qcow2/lib/block/dirty-bitmap.c | 2 +- - qcow2/lib/block/file-posix.c | 46 +- - qcow2/lib/block/io.c | 60 ++- - qcow2/lib/block/mirror.c | 30 +- - qcow2/lib/block/nbd.c | 31 +- + qcow2/lib/block/block-backend.c | 98 +++- + qcow2/lib/block/block-gen.c | 5 +- + qcow2/lib/block/dirty-bitmap.c | 2 + + qcow2/lib/block/file-posix.c | 20 +- + qcow2/lib/block/io.c | 40 +- + qcow2/lib/block/mirror.c | 6 + qcow2/lib/block/qapi.c | 6 +- qcow2/lib/block/raw-format.c | 4 + - qcow2/lib/blockdev.c | 26 +- - qcow2/lib/blockjob.c | 6 +- - qcow2/lib/crypto/hash.c | 96 +++- - qcow2/lib/crypto/tlscreds.c | 10 +- - qcow2/lib/crypto/tlssession.c | 16 +- + qcow2/lib/blockdev.c | 24 +- + qcow2/lib/blockjob.c | 4 + qcow2/lib/hw/block/block.c | 8 +- - qcow2/lib/hw/block/hd-geometry.c | 6 +- - qcow2/lib/io/channel-file.c | 6 +- - qcow2/lib/io/channel-socket.c | 36 +- - qcow2/lib/io/channel-tls.c | 20 +- - qcow2/lib/io/task.c | 18 +- - qcow2/lib/job-qmp.c | 14 +- - qcow2/lib/job.c | 14 +- - qcow2/lib/nbd/client-connection.c | 4 +- - qcow2/lib/nbd/client.c | 83 ++-- - qcow2/lib/nbd/common.c | 4 +- qcow2/lib/qapi/qapi-events-block-core.c | 24 +- qcow2/lib/qapi/qapi-events-job.c | 6 +- qcow2/lib/qapi/qapi-types-block-core.c | 38 +- @@ -82,50 +62,45 @@ Signed-off-by: Anthoine Bourgeois qcow2/lib/qapi/qapi-types-yank.c | 4 +- qcow2/lib/qapi/qapi-visit-block-core.c | 80 +++- qcow2/lib/qapi/qapi-visit-common.c | 2 +- - qcow2/lib/qapi/qapi-visit-core.c | 62 +-- qcow2/lib/qapi/qapi-visit-crypto.c | 2 +- qcow2/lib/qapi/qapi-visit-job.c | 2 +- qcow2/lib/qapi/qapi-visit-sockets.c | 2 +- qcow2/lib/qapi/qapi-visit-yank.c | 2 +- qcow2/lib/qapi/qmp-dispatch.c | 2 + qcow2/lib/qapi/qobject-input-visitor.c | 4 +- - qcow2/lib/qcow2-cache.c | 26 +- - qcow2/lib/qcow2-cluster.c | 34 +- - qcow2/lib/qcow2-refcount.c | 7 +- + qcow2/lib/qcow2-cache.c | 2 +- + qcow2/lib/qcow2-cluster.c | 2 + + qcow2/lib/qcow2-refcount.c | 5 +- qcow2/lib/qcow2-threads.c | 4 +- - qcow2/lib/qcow2.c | 186 +++++--- + qcow2/lib/qcow2.c | 158 +++++-- qcow2/lib/qobject/block-qdict.c | 2 + - qcow2/lib/qom/object.c | 38 +- - qcow2/lib/system/cpus.c | 44 +- - qcow2/lib/util/aio-posix.c | 20 +- - qcow2/lib/util/async.c | 8 +- + qcow2/lib/qom/object.c | 32 +- + qcow2/lib/system/cpus.c | 42 +- + qcow2/lib/util/aio-posix.c | 6 +- + qcow2/lib/util/async.c | 2 + qcow2/lib/util/bufferiszero.c | 9 +- qcow2/lib/util/cutils.c | 6 +- qcow2/lib/util/error-report.c | 19 +- qcow2/lib/util/error.c | 6 +- - qcow2/lib/util/hbitmap.c | 12 +- + qcow2/lib/util/hbitmap.c | 2 + qcow2/lib/util/host-utils.c | 2 + qcow2/lib/util/iov.c | 6 +- - qcow2/lib/util/lockcnt.c | 22 +- qcow2/lib/util/main-loop.c | 18 +- - qcow2/lib/util/memalign.c | 9 +- - qcow2/lib/util/module.c | 6 +- + qcow2/lib/util/memalign.c | 3 +- qcow2/lib/util/osdep.c | 22 +- - qcow2/lib/util/oslib-posix.c | 34 +- - qcow2/lib/util/qemu-coroutine-lock.c | 12 +- - qcow2/lib/util/qemu-coroutine.c | 8 +- - qcow2/lib/util/qemu-option.c | 12 +- + qcow2/lib/util/oslib-posix.c | 32 +- qcow2/lib/util/qemu-print.c | 6 +- - qcow2/lib/util/qemu-sockets.c | 18 +- - qcow2/lib/util/qemu-thread-common.h | 8 +- - qcow2/lib/util/qemu-thread-posix.c | 8 +- + qcow2/lib/util/qemu-sockets.c | 14 +- + qcow2/lib/util/qemu-thread-posix.c | 4 +- qcow2/lib/util/qemu-timer.c | 25 +- qcow2/lib/util/qsp.c | 10 +- - qcow2/lib/util/thread-pool.c | 10 +- qcow2/lib/util/yank.c | 2 +- qcow2/qemu-img.c | 76 +-- - 116 files changed, 1991 insertions(+), 628 deletions(-) + 88 files changed, 1713 insertions(+), 286 deletions(-) + create mode 100644 include/block/trace.h create mode 100644 include/qemu/config-host.h + create mode 100644 include/trace.h + create mode 100644 include/trace/trace-root.h create mode 100644 qcow2/Makefile.am create mode 100644 qcow2/lib/Makefile.am @@ -171,23 +146,22 @@ index 7133a020..78c56ba3 100644 SUBDIRS += control SUBDIRS += drivers diff --git a/configure.ac b/configure.ac -index a419f8ae..43fc21a6 100644 +index a419f8ae..26d3c321 100644 --- a/configure.ac +++ b/configure.ac -@@ -29,6 +29,12 @@ AC_CHECK_LIB([uuid], [main], +@@ -29,6 +29,11 @@ AC_CHECK_LIB([uuid], [main], [:], AC_MSG_ERROR([Need uuid-dev])) +PKG_PROG_PKG_CONFIG +PKG_CHECK_MODULES([GLIB], [glib-2.0]) +PKG_CHECK_MODULES([ZLIB], [zlib]) -+PKG_CHECK_MODULES([GNUTLS], [gnutls]) +PKG_CHECK_MODULES([LIBZSTD], [libzstd]) + AS_IF([test x$with_libiconv != xno], [AC_CHECK_LIB([iconv], [main], [AC_SUBST([LIBICONV], ["-liconv"])], -@@ -64,6 +70,8 @@ cbt/Makefile +@@ -64,6 +69,8 @@ cbt/Makefile vhd/Makefile vhd/lib/Makefile vhd/lib/test/Makefile @@ -197,7 +171,7 @@ index a419f8ae..43fc21a6 100644 drivers/Makefile drivers/crypto/Makefile diff --git a/include/block/aio.h b/include/block/aio.h -index 4ee81936..3776e8bf 100644 +index 4ee81936..d9bdc698 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -23,11 +23,15 @@ @@ -225,153 +199,24 @@ index 4ee81936..3776e8bf 100644 #define aio_bh_schedule_oneshot(ctx, cb, opaque) \ aio_bh_schedule_oneshot_full((ctx), (cb), (opaque), (stringify(cb))) -@@ -691,6 +696,7 @@ void aio_context_destroy(AioContext *ctx); - /* Used internally, do not call outside AioContext code */ - void aio_context_use_g_source(AioContext *ctx); - -+#if 0 - /** - * aio_context_set_poll_params: - * @ctx: the aio context -@@ -721,3 +727,4 @@ void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch); - void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min, - int64_t max, Error **errp); - #endif -+#endif -diff --git a/include/block/block-common.h b/include/block/block-common.h -index 338fe5ff..b07b07e2 100644 ---- a/include/block/block-common.h -+++ b/include/block/block-common.h -@@ -125,6 +125,7 @@ typedef struct BlockZoneDescriptor { - BlockZoneState state; - } BlockZoneDescriptor; - -+#if 0 - /* - * Track write pointers of a zone in bytes. - */ -@@ -132,6 +133,7 @@ typedef struct BlockZoneWps { - CoMutex colock; - uint64_t wp[]; - } BlockZoneWps; -+#endif - - typedef struct BlockDriverInfo { - /* in bytes, 0 if irrelevant */ -@@ -546,7 +548,7 @@ typedef struct HDGeometry { - */ - - char *bdrv_perm_names(uint64_t perm); --uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); -+//uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); - - void bdrv_init_with_whitelist(void); - bool bdrv_uses_whitelist(void); -diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h -index bd7cecd1..2e54cf5e 100644 ---- a/include/block/block-global-state.h -+++ b/include/block/block-global-state.h -@@ -196,8 +196,10 @@ void bdrv_aio_cancel(BlockAIOCB *acb); - int bdrv_has_zero_init_1(BlockDriverState *bs); - int coroutine_mixed_fn GRAPH_RDLOCK bdrv_has_zero_init(BlockDriverState *bs); - BlockDriverState *bdrv_find_node(const char *node_name); -+#if 0 - BlockDeviceInfoList *bdrv_named_nodes_list(bool flat, Error **errp); - XDbgBlockGraph * GRAPH_RDLOCK bdrv_get_xdbg_block_graph(Error **errp); -+#endif - BlockDriverState *bdrv_lookup_bs(const char *device, - const char *node_name, - Error **errp); -diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h -index ebb4e56a..c6d8b950 100644 ---- a/include/block/block_int-common.h -+++ b/include/block/block_int-common.h -@@ -1253,7 +1253,7 @@ struct BlockDriverState { - BdrvBlockStatusCache *block_status_cache; - - /* array of write pointers' location of each zone in the zoned device. */ -- BlockZoneWps *wps; -+ //BlockZoneWps *wps; - }; - - struct BlockBackendRootState { -diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h -index eb2d92a2..2d91fb96 100644 ---- a/include/block/block_int-global-state.h -+++ b/include/block/block_int-global-state.h -@@ -37,6 +37,7 @@ - * the GS API. - */ - -+#if 0 - /** - * stream_start: - * @job_id: The id of the newly-created job, or %NULL to use the -@@ -72,6 +73,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, - BlockdevOnError on_error, - const char *filter_node_name, - Error **errp); -+#endif - - /** - * commit_start: -@@ -124,6 +126,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, - const char *filter_node_name, - BlockCompletionFunc *cb, void *opaque, - bool auto_complete, Error **errp); -+#if 0 - /* - * mirror_start: - * @job_id: The id of the newly-created job, or %NULL to use the -@@ -201,6 +204,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, - int creation_flags, - BlockCompletionFunc *cb, void *opaque, - JobTxn *txn, Error **errp); -+#endif - - BdrvChild * GRAPH_WRLOCK - bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, -@@ -247,18 +251,24 @@ void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c, - uint64_t perm, uint64_t shared, - uint64_t *nperm, uint64_t *nshared); - -+#if 0 - void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp); -+#endif - bool blk_dev_has_removable_media(BlockBackend *blk); -+#if 0 - void blk_dev_eject_request(BlockBackend *blk, bool force); -+#endif - bool blk_dev_is_medium_locked(BlockBackend *blk); - - void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup); - -+#if 0 - void bdrv_set_monitor_owned(BlockDriverState *bs); - - void blockdev_close_all_bdrv_states(void); - - BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp); -+#endif - - /** - * Simple implementation of bdrv_co_create_opts for protocol drivers -@@ -270,6 +280,7 @@ int coroutine_fn bdrv_co_create_opts_simple(BlockDriver *drv, - QemuOpts *opts, - Error **errp); - -+#if 0 - BdrvDirtyBitmap *block_dirty_bitmap_lookup(const char *node, - const char *name, - BlockDriverState **pbs, -@@ -281,6 +292,7 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, - bool release, - BlockDriverState **bitmap_bs, - Error **errp); -+#endif - - - BlockDriverState * GRAPH_RDLOCK +diff --git a/include/block/trace.h b/include/block/trace.h +new file mode 100644 +index 00000000..d55cc5eb +--- /dev/null ++++ b/include/block/trace.h +@@ -0,0 +1,12 @@ ++/* qcow2/lib/block.c */ ++#define trace_bdrv_open_common(...) do {} while(0) ++#define trace_bdrv_lock_medium(...) do {} while(0) ++/* qcow2/lib/blockdev.c */ ++#define trace_qmp_block_stream(...) do {} while(0) ++#define trace_qmp_block_job_cancel(...) do {} while(0) ++#define trace_qmp_block_job_pause(...) do {} while(0) ++#define trace_qmp_block_job_resume(...) do {} while(0) ++#define trace_qmp_block_job_complete(...) do {} while(0) ++#define trace_qmp_block_job_finalize(...) do {} while(0) ++#define trace_qmp_block_job_dismiss(...) do {} while(0) ++/* qcow2/lib/blockjob.c */ diff --git a/include/compiler.h b/include/compiler.h index 57ab2367..9ab73c76 100644 --- a/include/compiler.h @@ -435,7 +280,7 @@ index de3946a5..7d533173 100644 /* Configuration */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h -index 77bfcbdf..f54e6ade 100644 +index 77bfcbdf..524fab7b 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -7,8 +7,8 @@ @@ -462,36 +307,41 @@ index 77bfcbdf..f54e6ade 100644 /** * @unplug_blockers: list of reasons to block unplugging of device */ -@@ -376,7 +378,7 @@ struct BusState { +@@ -376,7 +378,9 @@ struct BusState { /* public: */ DeviceState *parent; char *name; -- HotplugHandler *hotplug_handler; -+ //HotplugHandler *hotplug_handler; ++#if 0 + HotplugHandler *hotplug_handler; ++#endif int max_index; bool realized; bool full; -@@ -394,7 +396,7 @@ struct BusState { +@@ -391,10 +395,12 @@ struct BusState { + * @sibling: next bus + */ + BusStateEntry sibling; ++#if 0 /** * @reset: ResettableState for the bus; handled by Resettable interface. */ -- ResettableState reset; -+ //ResettableState reset; + ResettableState reset; ++#endif }; /** -@@ -531,8 +533,8 @@ bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); +@@ -531,8 +537,10 @@ bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp); void qdev_unrealize(DeviceState *dev); void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version); --HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); --HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); -+//HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); -+//HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); ++#if 0 + HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); + HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); ++#endif bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); /** -@@ -545,10 +547,12 @@ bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); +@@ -545,10 +553,12 @@ bool qdev_hotplug_allowed(DeviceState *dev, Error **errp); * Return: pointer to object that implements TYPE_HOTPLUG_HANDLER interface * or NULL if there aren't any. */ @@ -504,7 +354,7 @@ index 77bfcbdf..f54e6ade 100644 void qdev_machine_creation_done(void); bool qdev_machine_modified(void); -@@ -1014,6 +1018,7 @@ extern bool qdev_hot_removed; +@@ -1014,6 +1024,7 @@ extern bool qdev_hot_removed; char *qdev_get_dev_path(DeviceState *dev); @@ -512,7 +362,7 @@ index 77bfcbdf..f54e6ade 100644 void qbus_set_hotplug_handler(BusState *bus, Object *handler); void qbus_set_bus_hotplug_handler(BusState *bus); -@@ -1032,6 +1037,7 @@ static inline bool qbus_is_hotpluggable(BusState *bus) +@@ -1032,6 +1043,7 @@ static inline bool qbus_is_hotpluggable(BusState *bus) } return ret; } @@ -585,7 +435,7 @@ index 7f12beff..e9830b25 100644 #include "qapi-types-block-core.h" diff --git a/include/qapi/qapi-types-block-core.h b/include/qapi/qapi-types-block-core.h -index 26585893..a2c2f2e2 100644 +index 26585893..4a3960c6 100644 --- a/include/qapi/qapi-types-block-core.h +++ b/include/qapi/qapi-types-block-core.h @@ -14,10 +14,10 @@ @@ -614,22 +464,19 @@ index 26585893..a2c2f2e2 100644 IMAGE_INFO_SPECIFIC_KIND_FILE, IMAGE_INFO_SPECIFIC_KIND__MAX, } ImageInfoSpecificKind; -@@ -1007,6 +1009,7 @@ struct SnapshotInfo { - void qapi_free_SnapshotInfo(SnapshotInfo *obj); - G_DEFINE_AUTOPTR_CLEANUP_FUNC(SnapshotInfo, qapi_free_SnapshotInfo) - -+#if 0 - struct ImageInfoSpecificQCow2EncryptionBase { +@@ -1017,10 +1019,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2EncryptionBase, qapi_free_Im + struct ImageInfoSpecificQCow2Encryption { + /* Members inherited from ImageInfoSpecificQCow2EncryptionBase: */ BlockdevQcow2EncryptionFormat format; - }; -@@ -1030,6 +1033,7 @@ static inline ImageInfoSpecificQCow2EncryptionBase *qapi_ImageInfoSpecificQCow2E - - void qapi_free_ImageInfoSpecificQCow2Encryption(ImageInfoSpecificQCow2Encryption *obj); - G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Encryption, qapi_free_ImageInfoSpecificQCow2Encryption) ++#if 0 + /* Own members: */ + union { /* union tag is @format */ + QCryptoBlockInfoLUKS luks; + } u; +#endif + }; - struct Qcow2BitmapInfoList { - Qcow2BitmapInfoList *next; + static inline ImageInfoSpecificQCow2EncryptionBase *qapi_ImageInfoSpecificQCow2Encryption_base(const ImageInfoSpecificQCow2Encryption *obj) @@ -1114,6 +1118,7 @@ struct ImageInfoSpecificQCow2Wrapper { void qapi_free_ImageInfoSpecificQCow2Wrapper(ImageInfoSpecificQCow2Wrapper *obj); G_DEFINE_AUTOPTR_CLEANUP_FUNC(ImageInfoSpecificQCow2Wrapper, qapi_free_ImageInfoSpecificQCow2Wrapper) @@ -658,38 +505,31 @@ index 26585893..a2c2f2e2 100644 ImageInfoSpecificFileWrapper file; } u; }; -@@ -2186,6 +2194,7 @@ struct BlockJobChangeOptionsMirror { - void qapi_free_BlockJobChangeOptionsMirror(BlockJobChangeOptionsMirror *obj); - G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobChangeOptionsMirror, qapi_free_BlockJobChangeOptionsMirror) +@@ -2347,9 +2355,11 @@ struct q_obj_BlockdevQcowEncryption_base { + struct BlockdevQcowEncryption { + BlockdevQcowEncryptionFormat format; +#if 0 - struct q_obj_BlockJobChangeOptions_base { - char *id; - JobType type; -@@ -2201,6 +2210,7 @@ struct BlockJobChangeOptions { - - void qapi_free_BlockJobChangeOptions(BlockJobChangeOptions *obj); - G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockJobChangeOptions, qapi_free_BlockJobChangeOptions) + union { /* union tag is @format */ + QCryptoBlockOptionsQCow aes; + } u; +#endif + }; - struct BlockdevCacheOptions { - bool has_direct; -@@ -2341,6 +2351,7 @@ struct Qcow2OverlapChecks { - void qapi_free_Qcow2OverlapChecks(Qcow2OverlapChecks *obj); - G_DEFINE_AUTOPTR_CLEANUP_FUNC(Qcow2OverlapChecks, qapi_free_Qcow2OverlapChecks) + void qapi_free_BlockdevQcowEncryption(BlockdevQcowEncryption *obj); +@@ -2377,10 +2387,12 @@ struct q_obj_BlockdevQcow2Encryption_base { + struct BlockdevQcow2Encryption { + BlockdevQcow2EncryptionFormat format; +#if 0 - struct q_obj_BlockdevQcowEncryption_base { - BlockdevQcowEncryptionFormat format; + union { /* union tag is @format */ + QCryptoBlockOptionsQCow aes; + QCryptoBlockOptionsLUKS luks; + } u; ++#endif }; -@@ -2385,6 +2396,7 @@ struct BlockdevQcow2Encryption { void qapi_free_BlockdevQcow2Encryption(BlockdevQcow2Encryption *obj); - G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevQcow2Encryption, qapi_free_BlockdevQcow2Encryption) -+#endif - - struct BlockdevOptionsPreallocate { - /* Members inherited from BlockdevOptionsGenericFormat: */ @@ -2442,6 +2454,7 @@ static inline BlockdevOptionsGenericCOWFormat *qapi_BlockdevOptionsQcow2_base(co void qapi_free_BlockdevOptionsQcow2(BlockdevOptionsQcow2 *obj); G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevOptionsQcow2, qapi_free_BlockdevOptionsQcow2) @@ -754,40 +594,24 @@ index 26585893..a2c2f2e2 100644 struct BlockdevOptionsRaw { /* Members inherited from BlockdevOptionsGenericFormat: */ -@@ -3105,26 +3125,31 @@ struct BlockdevOptions { +@@ -3105,6 +3125,7 @@ struct BlockdevOptions { bool has_detect_zeroes; BlockdevDetectZeroesOptions detect_zeroes; union { /* union tag is @driver */ -- BlockdevOptionsBlkdebug blkdebug; -- BlockdevOptionsBlklogwrites blklogwrites; -- BlockdevOptionsBlkverify blkverify; -- BlockdevOptionsBlkreplay blkreplay; -- BlockdevOptionsGenericFormat bochs; -- BlockdevOptionsGenericFormat cloop; -- BlockdevOptionsGenericFormat compress; -- BlockdevOptionsCbw copy_before_write; -- BlockdevOptionsCor copy_on_read; -- BlockdevOptionsGenericFormat dmg; -+#if 0 -+ //BlockdevOptionsBlkdebug blkdebug; -+ //BlockdevOptionsBlklogwrites blklogwrites; -+ //BlockdevOptionsBlkverify blkverify; -+ //BlockdevOptionsBlkreplay blkreplay; -+ //BlockdevOptionsGenericFormat bochs; -+ //BlockdevOptionsGenericFormat cloop; -+ //BlockdevOptionsGenericFormat compress; -+ //BlockdevOptionsCbw copy_before_write; -+ //BlockdevOptionsCor copy_on_read; -+ //BlockdevOptionsGenericFormat dmg; ++#if 0 + BlockdevOptionsBlkdebug blkdebug; + BlockdevOptionsBlklogwrites blklogwrites; + BlockdevOptionsBlkverify blkverify; +@@ -3115,16 +3136,20 @@ struct BlockdevOptions { + BlockdevOptionsCbw copy_before_write; + BlockdevOptionsCor copy_on_read; + BlockdevOptionsGenericFormat dmg; +#endif BlockdevOptionsFile file; -- BlockdevOptionsCurlFtp ftp; -- BlockdevOptionsCurlFtps ftps; -- BlockdevOptionsGluster gluster; +#if 0 -+ //BlockdevOptionsCurlFtp ftp; -+ //BlockdevOptionsCurlFtps ftps; -+ //BlockdevOptionsGluster gluster; + BlockdevOptionsCurlFtp ftp; + BlockdevOptionsCurlFtps ftps; + BlockdevOptionsGluster gluster; +#endif #if defined(HAVE_HOST_BLOCK_DEVICE) BlockdevOptionsFile host_cdrom; @@ -834,16 +658,17 @@ index 26585893..a2c2f2e2 100644 struct BlockdevCreateOptionsQcow2 { BlockdevRef *file; -@@ -3309,7 +3339,7 @@ struct BlockdevCreateOptionsQcow2 { +@@ -3309,7 +3339,9 @@ struct BlockdevCreateOptionsQcow2 { char *backing_file; bool has_backing_fmt; BlockdevDriver backing_fmt; -- QCryptoBlockCreateOptions *encrypt; -+ //QCryptoBlockCreateOptions *encrypt; ++#if 0 + QCryptoBlockCreateOptions *encrypt; ++#endif bool has_cluster_size; uint64_t cluster_size; bool has_preallocation; -@@ -3424,12 +3454,15 @@ struct BlockdevCreateOptions { +@@ -3424,12 +3456,15 @@ struct BlockdevCreateOptions { BlockdevDriver driver; union { /* union tag is @driver */ BlockdevCreateOptionsFile file; @@ -859,7 +684,7 @@ index 26585893..a2c2f2e2 100644 BlockdevCreateOptionsQed qed; BlockdevCreateOptionsRbd rbd; BlockdevCreateOptionsSsh ssh; -@@ -3437,6 +3470,7 @@ struct BlockdevCreateOptions { +@@ -3437,6 +3472,7 @@ struct BlockdevCreateOptions { BlockdevCreateOptionsVhdx vhdx; BlockdevCreateOptionsVmdk vmdk; BlockdevCreateOptionsVpc vpc; @@ -867,7 +692,7 @@ index 26585893..a2c2f2e2 100644 } u; }; -@@ -3448,6 +3482,7 @@ struct q_obj_blockdev_create_arg { +@@ -3448,6 +3484,7 @@ struct q_obj_blockdev_create_arg { BlockdevCreateOptions *options; }; @@ -875,28 +700,30 @@ index 26585893..a2c2f2e2 100644 struct BlockdevAmendOptionsLUKS { /* Members inherited from QCryptoBlockAmendOptionsLUKS: */ QCryptoBlockLUKSKeyslotState state; -@@ -3468,9 +3503,10 @@ static inline QCryptoBlockAmendOptionsLUKS *qapi_BlockdevAmendOptionsLUKS_base(c +@@ -3468,9 +3505,12 @@ static inline QCryptoBlockAmendOptionsLUKS *qapi_BlockdevAmendOptionsLUKS_base(c void qapi_free_BlockdevAmendOptionsLUKS(BlockdevAmendOptionsLUKS *obj); G_DEFINE_AUTOPTR_CLEANUP_FUNC(BlockdevAmendOptionsLUKS, qapi_free_BlockdevAmendOptionsLUKS) +#endif struct BlockdevAmendOptionsQcow2 { -- QCryptoBlockAmendOptions *encrypt; -+ //QCryptoBlockAmendOptions *encrypt; ++#if 0 + QCryptoBlockAmendOptions *encrypt; ++#endif }; void qapi_free_BlockdevAmendOptionsQcow2(BlockdevAmendOptionsQcow2 *obj); -@@ -3483,7 +3519,7 @@ struct q_obj_BlockdevAmendOptions_base { +@@ -3483,7 +3523,9 @@ struct q_obj_BlockdevAmendOptions_base { struct BlockdevAmendOptions { BlockdevDriver driver; union { /* union tag is @driver */ -- BlockdevAmendOptionsLUKS luks; -+ //BlockdevAmendOptionsLUKS luks; ++#if 0 + BlockdevAmendOptionsLUKS luks; ++#endif BlockdevAmendOptionsQcow2 qcow2; } u; }; -@@ -3573,12 +3609,14 @@ struct q_obj_x_blockdev_change_arg { +@@ -3573,12 +3615,14 @@ struct q_obj_x_blockdev_change_arg { char *node; }; @@ -942,7 +769,7 @@ index f2e95681..0a0c8361 100644 typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); diff --git a/include/qcow2.h b/include/qcow2.h -index a9e3481c..5d606fc9 100644 +index a9e3481c..12b3315b 100644 --- a/include/qcow2.h +++ b/include/qcow2.h @@ -25,7 +25,7 @@ @@ -954,76 +781,17 @@ index a9e3481c..5d606fc9 100644 #include "qemu/coroutine.h" #include "qemu/units.h" #include "block/block_int.h" -@@ -231,10 +231,12 @@ typedef struct QCowSnapshot { - struct Qcow2Cache; - typedef struct Qcow2Cache Qcow2Cache; - -+#if 0 - typedef struct Qcow2CryptoHeaderExtension { - uint64_t offset; - uint64_t length; - } QEMU_PACKED Qcow2CryptoHeaderExtension; -+#endif - - typedef struct Qcow2UnknownHeaderExtension { - uint32_t magic; -@@ -359,12 +361,12 @@ typedef struct BDRVQcow2State { - +@@ -360,8 +360,10 @@ typedef struct BDRVQcow2State { CoMutex lock; -- Qcow2CryptoHeaderExtension crypto_header; /* QCow2 header extension */ -- QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ -- QCryptoBlock *crypto; /* Disk encryption format driver */ -- bool crypt_physical_offset; /* Whether to use virtual or physical offset -- for encryption initialization vector tweak */ -- uint32_t crypt_method_header; -+ //Qcow2CryptoHeaderExtension crypto_header; /* QCow2 header extension */ -+ //QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ -+ //QCryptoBlock *crypto; /* Disk encryption format driver */ -+ //bool crypt_physical_offset; /* Whether to use virtual or physical offset -+ // for encryption initialization vector tweak */ -+ //uint32_t crypt_method_header; - uint64_t snapshots_offset; - int snapshots_size; - unsigned int nb_snapshots; -@@ -892,9 +894,11 @@ int coroutine_fn qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *re - - void GRAPH_RDLOCK qcow2_process_discards(BlockDriverState *bs, int ret); - -+#if 0 - int GRAPH_RDLOCK - qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, - int64_t size); -+#endif - int GRAPH_RDLOCK - qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, - int64_t size, bool data_file); -@@ -924,9 +928,11 @@ int coroutine_fn GRAPH_RDLOCK - qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); - - int GRAPH_RDLOCK qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); + Qcow2CryptoHeaderExtension crypto_header; /* QCow2 header extension */ +#if 0 - int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, - uint8_t *buf, int nb_sectors, bool enc, Error **errp); - -+#endif - int GRAPH_RDLOCK - qcow2_get_host_offset(BlockDriverState *bs, uint64_t offset, - unsigned int *bytes, uint64_t *host_offset, -@@ -1064,11 +1070,13 @@ qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size, - ssize_t coroutine_fn - qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, - const void *src, size_t src_size); -+#if 0 - int coroutine_fn - qcow2_co_encrypt(BlockDriverState *bs, uint64_t host_offset, - uint64_t guest_offset, void *buf, size_t len); - int coroutine_fn - qcow2_co_decrypt(BlockDriverState *bs, uint64_t host_offset, - uint64_t guest_offset, void *buf, size_t len); + QCryptoBlockOpenOptions *crypto_opts; /* Disk encryption runtime options */ + QCryptoBlock *crypto; /* Disk encryption format driver */ +#endif - - #endif + bool crypt_physical_offset; /* Whether to use virtual or physical offset + for encryption initialization vector tweak */ + uint32_t crypt_method_header; diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 7a3f2e65..9e392dcd 100644 --- a/include/qemu/atomic.h @@ -1038,7 +806,7 @@ index 7a3f2e65..9e392dcd 100644 /* Compiler barrier */ #define barrier() ({ asm volatile("" ::: "memory"); (void)0; }) diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h -index 2c0a2fe7..df735fa7 100644 +index 2c0a2fe7..69c31b5b 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -12,9 +12,8 @@ @@ -1053,38 +821,6 @@ index 2c0a2fe7..df735fa7 100644 #define BITS_PER_BYTE CHAR_BIT #define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) -@@ -41,6 +40,7 @@ static inline void set_bit(long nr, unsigned long *addr) - *p |= mask; - } - -+#if 0 - /** - * set_bit_atomic - Set a bit in memory atomically - * @nr: the bit to set -@@ -53,6 +53,7 @@ static inline void set_bit_atomic(long nr, unsigned long *addr) - - qatomic_or(p, mask); - } -+#endif - - /** - * clear_bit - Clears a bit in memory -@@ -67,6 +68,7 @@ static inline void clear_bit(long nr, unsigned long *addr) - *p &= ~mask; - } - -+#if 0 - /** - * clear_bit_atomic - Clears a bit in memory atomically - * @nr: Bit to clear -@@ -79,6 +81,7 @@ static inline void clear_bit_atomic(long nr, unsigned long *addr) - - return qatomic_and(p, ~mask); - } -+#endif - - /** - * change_bit - Toggle a bit in memory diff --git a/include/qemu/config-host.h b/include/qemu/config-host.h new file mode 100644 index 00000000..c0b00bf6 @@ -1624,7 +1360,7 @@ index ead97d35..12ae37af 100644 +//Int128 divs256(Int128 *plow, Int128 *phigh, Int128 divisor); #endif diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h -index 5764db15..e0a0be44 100644 +index 5764db15..feab3b96 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -26,11 +26,12 @@ @@ -1652,51 +1388,6 @@ index 5764db15..e0a0be44 100644 /** * qemu_init_main_loop: Set up the process so that it can run the main loop. -@@ -110,6 +114,7 @@ AioContext *qemu_get_aio_context(void); - */ - void qemu_notify_event(void); - -+#if 0 - #ifdef _WIN32 - /* return TRUE if no sleep should be done afterwards */ - typedef int PollingFunc(void *opaque); -@@ -195,6 +200,7 @@ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); - * the event loop. - */ - typedef int IOCanReadHandler(void *opaque); -+#endif - - /** - * qemu_set_fd_handler: Register a file descriptor with the main loop -@@ -342,6 +348,7 @@ void bql_lock_impl(const char *file, int line); - */ - void bql_unlock(void); - -+#if 0 - /** - * BQL_LOCK_GUARD - * -@@ -369,6 +376,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(BQLLockAuto, bql_auto_unlock) - #define BQL_LOCK_GUARD() \ - g_autoptr(BQLLockAuto) _bql_lock_auto __attribute__((unused)) \ - = bql_auto_lock(__FILE__, __LINE__) -+#endif - - /* - * qemu_cond_wait_bql: Wait on condition for the Big QEMU Lock (BQL) -diff --git a/include/qemu/option_int.h b/include/qemu/option_int.h -index 5dd9a516..f88dd33b 100644 ---- a/include/qemu/option_int.h -+++ b/include/qemu/option_int.h -@@ -46,7 +46,7 @@ struct QemuOpt { - struct QemuOpts { - char *id; - QemuOptsList *list; -- Location loc; -+ //Location loc; - QTAILQ_HEAD(, QemuOpt) head; - QTAILQ_ENTRY(QemuOpts) next; - }; diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index fe7c3c5f..bdc90246 100644 --- a/include/qemu/osdep.h @@ -1710,55 +1401,8 @@ index fe7c3c5f..bdc90246 100644 #endif /* -diff --git a/include/qemu/range.h b/include/qemu/range.h -index d446ad88..8afd93e0 100644 ---- a/include/qemu/range.h -+++ b/include/qemu/range.h -@@ -236,6 +236,7 @@ int range_compare(Range *a, Range *b); - - GList *range_list_insert(GList *list, Range *data); - -+#if 0 - /* - * Inverse an array of sorted ranges over the [low, high] span, ie. - * original ranges becomes holes in the newly allocated inv_ranges -@@ -243,5 +244,6 @@ GList *range_list_insert(GList *list, Range *data); - void range_inverse_array(GList *in_ranges, - GList **out_ranges, - uint64_t low, uint64_t high); -+#endif - - #endif -diff --git a/include/qemu/timer.h b/include/qemu/timer.h -index fa56ec94..f877974c 100644 ---- a/include/qemu/timer.h -+++ b/include/qemu/timer.h -@@ -842,19 +842,21 @@ static inline int64_t get_clock(void) - - #else - --extern int use_rt_clock; -+//extern int use_rt_clock; - - static inline int64_t get_clock(void) - { -+#if 0 - if (use_rt_clock) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec * 1000000000LL + ts.tv_nsec; - } else { -+#endif - /* XXX: using gettimeofday leads to problems if the date - changes, so it should be avoided. */ - return get_clock_realtime(); -- } -+ //} - } - #endif - diff --git a/include/sysemu/block-backend-common.h b/include/sysemu/block-backend-common.h -index 780cea73..6b77d20d 100644 +index 780cea73..a8af5e5c 100644 --- a/include/sysemu/block-backend-common.h +++ b/include/sysemu/block-backend-common.h @@ -14,7 +14,7 @@ @@ -1770,62 +1414,256 @@ index 780cea73..6b77d20d 100644 /* * TODO Have to include block/block.h for a bunch of block layer -@@ -97,7 +97,7 @@ typedef struct BlockDevOps { +@@ -97,7 +97,9 @@ typedef struct BlockDevOps { * friends so that BlockBackends can be kept in lists outside block-backend.c */ typedef struct BlockBackendPublic { -- ThrottleGroupMember throttle_group_member; -+ //ThrottleGroupMember throttle_group_member; ++#if 0 + ThrottleGroupMember throttle_group_member; ++#endif } BlockBackendPublic; #endif /* BLOCK_BACKEND_COMMON_H */ diff --git a/include/sysemu/block-backend-global-state.h b/include/sysemu/block-backend-global-state.h -index 49c12b0f..8c557f0d 100644 +index 49c12b0f..c4d5e87b 100644 --- a/include/sysemu/block-backend-global-state.h +++ b/include/sysemu/block-backend-global-state.h -@@ -117,7 +117,7 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); +@@ -117,7 +117,9 @@ int blk_load_vmstate(BlockBackend *blk, uint8_t *buf, int64_t pos, int size); int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); --void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); -+//void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); ++#if 0 + void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); ++#endif void blk_io_limits_disable(BlockBackend *blk); void blk_io_limits_enable(BlockBackend *blk, const char *group); void blk_io_limits_update_group(BlockBackend *blk, const char *group); -diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h -index d174275a..040efc7c 100644 ---- a/include/sysemu/block-backend-io.h -+++ b/include/sysemu/block-backend-io.h -@@ -46,6 +46,7 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset, - BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *blk_aio_flush(BlockBackend *blk, - BlockCompletionFunc *cb, void *opaque); -+#if 0 - BlockAIOCB *blk_aio_zone_report(BlockBackend *blk, int64_t offset, - unsigned int *nr_zones, - BlockZoneDescriptor *zones, -@@ -56,6 +57,7 @@ BlockAIOCB *blk_aio_zone_mgmt(BlockBackend *blk, BlockZoneOp op, - BlockAIOCB *blk_aio_zone_append(BlockBackend *blk, int64_t *offset, - QEMUIOVector *qiov, BdrvRequestFlags flags, - BlockCompletionFunc *cb, void *opaque); -+#endif - BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk, int64_t offset, int64_t bytes, - BlockCompletionFunc *cb, void *opaque); - void blk_aio_cancel_async(BlockAIOCB *acb); -@@ -92,11 +94,13 @@ void *blk_blockalign(BlockBackend *blk, size_t size); - bool blk_is_writable(BlockBackend *blk); - bool blk_enable_write_cache(BlockBackend *blk); - BlockdevOnError blk_get_on_error(BlockBackend *blk, bool is_read); -+#if 0 - BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, - int error); - void blk_error_action(BlockBackend *blk, BlockErrorAction action, - bool is_read, int error); - void blk_iostatus_set_err(BlockBackend *blk, int error); -+#endif - int blk_get_max_iov(BlockBackend *blk); - int blk_get_max_hw_iov(BlockBackend *blk); - +diff --git a/include/trace.h b/include/trace.h +new file mode 100644 +index 00000000..d1b57619 +--- /dev/null ++++ b/include/trace.h +@@ -0,0 +1,191 @@ ++/* qcow2/lib/system/cpus.c */ ++#define trace_vm_stop_flush_all(...) do {} while(0) ++/* qcow2/lib/hw/block/hd-geometry.c */ ++#define trace_hd_geometry_lchs_guess(...) do {} while(0) ++#define trace_hd_geometry_guess(...) do {} while(0) ++/* qcow2/lib/qapi/qapi-visit-core.c */ ++#define trace_visit_complete(...) do {} while(0) ++#define trace_visit_free(...) do {} while(0) ++#define trace_visit_start_struct(...) do {} while(0) ++#define trace_visit_check_struct(...) do {} while(0) ++#define trace_visit_end_struct(...) do {} while(0) ++#define trace_visit_start_list(...) do {} while(0) ++#define trace_visit_next_list(...) do {} while(0) ++#define trace_visit_check_list(...) do {} while(0) ++#define trace_visit_end_list(...) do {} while(0) ++#define trace_visit_start_alternate(...) do {} while(0) ++#define trace_visit_end_alternate(...) do {} while(0) ++#define trace_visit_optional(...) do {} while(0) ++#define trace_visit_policy_reject(...) do {} while(0) ++#define trace_visit_policy_skip(...) do {} while(0) ++#define trace_visit_type_int(...) do {} while(0) ++#define trace_visit_type_uint8(...) do {} while(0) ++#define trace_visit_type_uint16(...) do {} while(0) ++#define trace_visit_type_uint32(...) do {} while(0) ++#define trace_visit_type_uint64(...) do {} while(0) ++#define trace_visit_type_int8(...) do {} while(0) ++#define trace_visit_type_int16(...) do {} while(0) ++#define trace_visit_type_int32(...) do {} while(0) ++#define trace_visit_type_int64(...) do {} while(0) ++#define trace_visit_type_size(...) do {} while(0) ++#define trace_visit_type_bool(...) do {} while(0) ++#define trace_visit_type_str(...) do {} while(0) ++#define trace_visit_type_number(...) do {} while(0) ++#define trace_visit_type_any(...) do {} while(0) ++#define trace_visit_type_null(...) do {} while(0) ++#define trace_visit_type_enum(...) do {} while(0) ++/* qcow2/lib/io/channel-file.c */ ++#define trace_qio_channel_file_new_fd(...) do {} while(0) ++#define trace_qio_channel_file_new_path(...) do {} while(0) ++/* qcow2/lib/io/channel-socket.c */ ++#define trace_qio_channel_socket_new(...) do {} while(0) ++#define trace_qio_channel_socket_new_fd(...) do {} while(0) ++#define trace_qio_channel_socket_connect_sync(...) do {} while(0) ++#define trace_qio_channel_socket_connect_fail(...) do {} while(0) ++#define trace_qio_channel_socket_connect_complete(...) do {} while(0) ++#define trace_qio_channel_socket_connect_async(...) do {} while(0) ++#define trace_qio_channel_socket_listen_sync(...) do {} while(0) ++#define trace_qio_channel_socket_listen_fail(...) do {} while(0) ++#define trace_qio_channel_socket_listen_complete(...) do {} while(0) ++#define trace_qio_channel_socket_listen_async(...) do {} while(0) ++#define trace_qio_channel_socket_dgram_sync(...) do {} while(0) ++#define trace_qio_channel_socket_dgram_fail(...) do {} while(0) ++#define trace_qio_channel_socket_dgram_complete(...) do {} while(0) ++#define trace_qio_channel_socket_dgram_async(...) do {} while(0) ++#define trace_qio_channel_socket_accept(...) do {} while(0) ++#define trace_qio_channel_socket_accept_fail(...) do {} while(0) ++#define trace_qio_channel_socket_accept_complete(...) do {} while(0) ++/* qcow2/lib/io/task.c */ ++#define trace_qio_task_new(...) do {} while(0) ++#define trace_qio_task_thread_result(...) do {} while(0) ++#define trace_qio_task_thread_run(...) do {} while(0) ++#define trace_qio_task_thread_exit(...) do {} while(0) ++#define trace_qio_task_thread_source_attach(...) do {} while(0) ++#define trace_qio_task_thread_start(...) do {} while(0) ++#define trace_qio_task_thread_source_cancel(...) do {} while(0) ++#define trace_qio_task_complete(...) do {} while(0) ++/* qcow2/lib/util/aio-posix.c */ ++#define trace_poll_add(...) do {} while(0) ++#define trace_poll_remove(...) do {} while(0) ++#define trace_run_poll_handlers_begin(...) do {} while(0) ++#define trace_run_poll_handlers_end(...) do {} while(0) ++#define trace_poll_shrink(...) do {} while(0) ++#define trace_poll_grow(...) do {} while(0) ++/* qcow2/lib/util/async.c */ ++#define trace_reentrant_aio(...) do {} while(0) ++#define trace_aio_co_schedule_bh_cb(...) do {} while(0) ++#define trace_aio_co_schedule(...) do {} while(0) ++/* qcow2/lib/util/hbitmap.c */ ++#define trace_hbitmap_iter_skip_words(...) do {} while(0) ++#define trace_hbitmap_set(...) do {} while(0) ++#define trace_hbitmap_reset(...) do {} while(0) ++/* qcow2/lib/util/lockcnt.c */ ++#define trace_lockcnt_fast_path_attempt(...) do {} while(0) ++#define trace_lockcnt_fast_path_success(...) do {} while(0) ++#define trace_lockcnt_futex_wait_prepare(...) do {} while(0) ++#define trace_lockcnt_futex_wait(...) do {} while(0) ++#define trace_lockcnt_futex_wait_resume(...) do {} while(0) ++#define trace_lockcnt_futex_wake(...) do {} while(0) ++#define trace_lockcnt_unlock_attempt(...) do {} while(0) ++#define trace_lockcnt_unlock_success(...) do {} while(0) ++/* qcow2/lib/util/memalign.c */ ++#define trace_qemu_memalign(...) do {} while(0) ++#define trace_qemu_vfree(...) do {} while(0) ++/* qcow2/lib/util/module.c */ ++#define trace_module_load_module(...) do {} while(0) ++#define trace_module_lookup_object_type(...) do {} while(0) ++/* qcow2/lib/util/oslib-posix.c */ ++#define trace_qemu_anon_ram_alloc(...) do {} while(0) ++#define trace_qemu_anon_ram_free(...) do {} while(0) ++/* qcow2/lib/util/qemu-coroutine-lock.c */ ++#define trace_qemu_co_mutex_lock_entry(...) do {} while(0) ++#define trace_qemu_co_mutex_lock_return(...) do {} while(0) ++#define trace_qemu_co_mutex_lock_uncontended(...) do {} while(0) ++#define trace_qemu_co_mutex_unlock_entry(...) do {} while(0) ++#define trace_qemu_co_mutex_unlock_return(...) do {} while(0) ++/* qcow2/lib/util/qemu-coroutine.c */ ++#define trace_qemu_aio_coroutine_enter(...) do {} while(0) ++#define trace_qemu_coroutine_terminate(...) do {} while(0) ++#define trace_qemu_coroutine_yield(...) do {} while(0) ++/* qcow2/lib/util/qemu-sockets.c */ ++#define trace_socket_listen(...) do {} while(0) ++/* qcow2/lib/util/qemu-thread-common.h */ ++#define trace_qemu_mutex_lock(...) do {} while(0) ++#define trace_qemu_mutex_locked(...) do {} while(0) ++#define trace_qemu_mutex_unlock(...) do {} while(0) ++/* qcow2/lib/util/thread-pool.c */ ++#define trace_thread_pool_complete(...) do {} while(0) ++#define trace_thread_pool_cancel(...) do {} while(0) ++#define trace_thread_pool_submit(...) do {} while(0) ++/* qcow2/lib/authz/base.c */ ++#define trace_qauthz_is_allowed(...) do {} while(0) ++/* qcow2/lib/block/block-backend.c */ ++#define trace_blk_root_attach(...) do {} while(0) ++#define trace_blk_root_detach(...) do {} while(0) ++#define trace_blk_co_preadv(...) do {} while(0) ++#define trace_blk_co_pwritev(...) do {} while(0) ++/* qcow2/lib/block/commit.c */ ++#define trace_commit_one_iteration(...) do {} while(0) ++#define trace_commit_start(...) do {} while(0) ++/* qcow2/lib/block/dirty-bitmap.c */ ++/* qcow2/lib/block/file-posix.c */ ++#define trace_file_flush_fdatasync_failed(...) do {} while(0) ++#define trace_file_copy_file_range(...) do {} while(0) ++#define trace_zbd_zone_append_complete(...) do {} while(0) ++#define trace_zbd_zone_report(...) do {} while(0) ++#define trace_zbd_zone_mgmt(...) do {} while(0) ++#define trace_zbd_zone_append(...) do {} while(0) ++#define trace_file_FindEjectableOpticalMedia(...) do {} while(0) ++#define trace_file_setup_cdrom(...) do {} while(0) ++#define trace_file_hdev_is_sg(...) do {} while(0) ++/* qcow2/lib/block/io.c */ ++#define trace_bdrv_co_do_copy_on_readv(...) do {} while(0) ++#define trace_bdrv_co_preadv_part(...) do {} while(0) ++#define trace_bdrv_co_pwritev_part(...) do {} while(0) ++#define trace_bdrv_co_pwrite_zeroes(...) do {} while(0) ++#define trace_bdrv_co_copy_range_from(...) do {} while(0) ++#define trace_bdrv_co_copy_range_to(...) do {} while(0) ++/* qcow2/lib/block/mirror.c */ ++#define trace_mirror_iteration_done(...) do {} while(0) ++#define trace_mirror_yield_in_flight(...) do {} while(0) ++#define trace_mirror_one_iteration(...) do {} while(0) ++#define trace_mirror_restart_iter(...) do {} while(0) ++#define trace_mirror_yield(...) do {} while(0) ++#define trace_mirror_before_flush(...) do {} while(0) ++#define trace_mirror_before_drain(...) do {} while(0) ++#define trace_mirror_before_sleep(...) do {} while(0) ++#define trace_mirror_start(...) do {} while(0) ++/* qcow2/lib/qom/object.c */ ++#define trace_object_dynamic_cast_assert(...) do {} while(0) ++#define trace_object_class_dynamic_cast_assert(...) do {} while(0) ++/* qcow2/lib/qcow2-cache.c */ ++#define trace_qcow2_cache_entry_flush(...) do {} while(0) ++#define trace_qcow2_cache_flush(...) do {} while(0) ++#define trace_qcow2_cache_get(...) do {} while(0) ++#define trace_qcow2_cache_get_done(...) do {} while(0) ++#define trace_qcow2_cache_get_read(...) do {} while(0) ++#define trace_qcow2_cache_get_replace_entry(...) do {} while(0) ++/* qcow2/lib/qcow2-cluster.c */ ++#define trace_qcow2_l2_allocate(...) do {} while(0) ++#define trace_qcow2_l2_allocate_get_empty(...) do {} while(0) ++#define trace_qcow2_l2_allocate_write_l2(...) do {} while(0) ++#define trace_qcow2_l2_allocate_write_l1(...) do {} while(0) ++#define trace_qcow2_l2_allocate_done(...) do {} while(0) ++#define trace_qcow2_cluster_link_l2(...) do {} while(0) ++#define trace_qcow2_handle_copied(...) do {} while(0) ++#define trace_qcow2_do_alloc_clusters_offset(...) do {} while(0) ++#define trace_qcow2_cluster_alloc_phys(...) do {} while(0) ++#define trace_qcow2_handle_alloc(...) do {} while(0) ++#define trace_qcow2_alloc_clusters_offset(...) do {} while(0) ++/* qcow2/lib/qcow2-refcount.c */ ++#define trace_qcow2_process_discards_failed_region(...) do {} while(0) ++/* qcow2/lib/qcow2.c */ ++#define trace_qcow2_add_task(...) do {} while(0) ++#define trace_qcow2_skip_cow(...) do {} while(0) ++#define trace_qcow2_writev_data(...) do {} while(0) ++#define trace_qcow2_writev_start_req(...) do {} while(0) ++#define trace_qcow2_writev_start_part(...) do {} while(0) ++#define trace_qcow2_writev_done_part(...) do {} while(0) ++#define trace_qcow2_writev_done_req(...) do {} while(0) ++#define trace_qcow2_pwrite_zeroes_start_req(...) do {} while(0) ++#define trace_qcow2_pwrite_zeroes(...) do {} while(0) +diff --git a/include/trace/trace-root.h b/include/trace/trace-root.h +new file mode 100644 +index 00000000..68037e50 +--- /dev/null ++++ b/include/trace/trace-root.h +@@ -0,0 +1,11 @@ ++/* qcow2/lib/job-qmp.c */ ++#define trace_qmp_job_cancel(...) do {} while(0) ++#define trace_qmp_job_pause(...) do {} while(0) ++#define trace_qmp_job_resume(...) do {} while(0) ++#define trace_qmp_job_complete(...) do {} while(0) ++#define trace_qmp_job_finalize(...) do {} while(0) ++#define trace_qmp_job_dismiss(...) do {} while(0) ++/* qcow2/lib/job.c */ ++#define trace_job_state_transition(...) do {} while(0) ++#define trace_job_apply_verb(...) do {} while(0) ++#define trace_job_completed(...) do {} while(0) +diff --git a/mk/blktap.spec.in b/mk/blktap.spec.in +index 2da6fe2c..fd1a4714 100644 +--- a/mk/blktap.spec.in ++++ b/mk/blktap.spec.in +@@ -11,6 +11,7 @@ BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot + Obsoletes: xen-blktap + BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autogen, autoconf, automake, libtool, libuuid-devel + BuildRequires: xen-devel, kernel-headers, xen-dom0-libs-devel, zlib-devel, xen-libs-devel, libcmocka-devel, lcov, git ++BuildRequires: glib2, glib2-devel, libzstd, libzstd-devel + BuildRequires: openssl-devel >= 1.1.1 + Requires(post): systemd + Requires(preun): systemd diff --git a/qcow2/Makefile.am b/qcow2/Makefile.am new file mode 100644 index 00000000..69460e08 @@ -1855,10 +1693,10 @@ index 00000000..69460e08 + -rm -rf *.gc?? diff --git a/qcow2/lib/Makefile.am b/qcow2/lib/Makefile.am new file mode 100644 -index 00000000..fb125aa7 +index 00000000..55a007d9 --- /dev/null +++ b/qcow2/lib/Makefile.am -@@ -0,0 +1,166 @@ +@@ -0,0 +1,159 @@ +AUTOMAKE_OPTIONS = subdir-objects + +SUBDIRS = . @@ -1902,28 +1740,20 @@ index 00000000..fb125aa7 +libqcow2_la_SOURCES += block/linux-aio.c +libqcow2_la_SOURCES += block/mirror.c +libqcow2_la_SOURCES += block/monitor/bitmap-qmp-cmds.c -+libqcow2_la_SOURCES += block/nbd.c +libqcow2_la_SOURCES += block/progress_meter.c +libqcow2_la_SOURCES += block/qapi.c +libqcow2_la_SOURCES += block/raw-format.c +libqcow2_la_SOURCES += block/snapshot.c -+libqcow2_la_SOURCES += crypto/hash.c -+libqcow2_la_SOURCES += crypto/tlscreds.c -+libqcow2_la_SOURCES += crypto/tlssession.c +libqcow2_la_SOURCES += hw/block/block.c +libqcow2_la_SOURCES += hw/block/hd-geometry.c +libqcow2_la_SOURCES += io/channel.c +libqcow2_la_SOURCES += io/channel-file.c +libqcow2_la_SOURCES += io/channel-socket.c -+libqcow2_la_SOURCES += io/channel-tls.c +libqcow2_la_SOURCES += io/channel-util.c +libqcow2_la_SOURCES += io/channel-watch.c +libqcow2_la_SOURCES += io/task.c +libqcow2_la_SOURCES += job.c +libqcow2_la_SOURCES += job-qmp.c -+libqcow2_la_SOURCES += nbd/client.c -+libqcow2_la_SOURCES += nbd/client-connection.c -+libqcow2_la_SOURCES += nbd/common.c +libqcow2_la_SOURCES += qapi/qapi-clone-visitor.c +libqcow2_la_SOURCES += qapi/qapi-dealloc-visitor.c +libqcow2_la_SOURCES += qapi/qapi-events-block-core.c @@ -1998,6 +1828,7 @@ index 00000000..fb125aa7 +libqcow2_la_SOURCES += util/qemu-print.c +libqcow2_la_SOURCES += util/qemu-sockets.c +libqcow2_la_SOURCES += util/qemu-thread-posix.c ++libqcow2_la_SOURCES += util/qemu-timer-common.c +libqcow2_la_SOURCES += util/qemu-timer.c +libqcow2_la_SOURCES += util/qsp.c +libqcow2_la_SOURCES += util/range.c @@ -2025,38 +1856,11 @@ index 00000000..fb125aa7 + +clean-local: + -rm -rf *.gc?? -diff --git a/qcow2/lib/authz/base.c b/qcow2/lib/authz/base.c -index f2b7fbe9..bd155bea 100644 ---- a/qcow2/lib/authz/base.c -+++ b/qcow2/lib/authz/base.c -@@ -21,7 +21,7 @@ - #include "qemu/osdep.h" - #include "authz/base.h" - #include "qemu/module.h" --#include "trace.h" -+//#include "trace.h" - - bool qauthz_is_allowed(QAuthZ *authz, - const char *identity, -@@ -31,7 +31,7 @@ bool qauthz_is_allowed(QAuthZ *authz, - bool allowed; - - allowed = cls->is_allowed(authz, identity, errp); -- trace_qauthz_is_allowed(authz, identity, allowed); -+ //trace_qauthz_is_allowed(authz, identity, allowed); - - return allowed; - } diff --git a/qcow2/lib/block.c b/qcow2/lib/block.c -index c317de9e..e823b462 100644 +index c317de9e..de666676 100644 --- a/qcow2/lib/block.c +++ b/qcow2/lib/block.c -@@ -24,12 +24,12 @@ - */ - - #include "qemu/osdep.h" --#include "block/trace.h" -+//#include "block/trace.h" +@@ -28,8 +28,8 @@ #include "block/block_int.h" #include "block/blockjob.h" #include "block/dirty-bitmap.h" @@ -2067,17 +1871,6 @@ index c317de9e..e823b462 100644 #include "block/qdict.h" #include "qemu/error-report.h" #include "block/module_block.h" -@@ -1917,8 +1917,8 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, - goto fail_opts; - } - -- trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, -- drv->format_name); -+ //trace_bdrv_open_common(bs, filename ?: "", bs->open_flags, -+ // drv->format_name); - - ro = bdrv_is_read_only(bs); - @@ -5114,7 +5114,6 @@ static void GRAPH_UNLOCKED bdrv_reopen_abort(BDRVReopenState *reopen_state) } } @@ -2183,15 +1976,6 @@ index c317de9e..e823b462 100644 BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, -@@ -7095,7 +7105,7 @@ void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked) - BlockDriver *drv = bs->drv; - IO_CODE(); - assert_bdrv_graph_readable(); -- trace_bdrv_lock_medium(bs, locked); -+ //trace_bdrv_lock_medium(bs, locked); - - if (drv && drv->bdrv_co_lock_medium) { - drv->bdrv_co_lock_medium(bs, locked); @@ -7425,6 +7435,7 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs) return bs ? bs->aio_context : qemu_get_aio_context(); } @@ -2223,7 +2007,7 @@ index 28297453..dea4f667 100644 static QEMUClockType clock_type = QEMU_CLOCK_REALTIME; static const int qtest_latency_ns = NANOSECONDS_PER_SECOND / 1000; diff --git a/qcow2/lib/block/block-backend.c b/qcow2/lib/block/block-backend.c -index db6f9b92..d15da172 100644 +index db6f9b92..0d08a07b 100644 --- a/qcow2/lib/block/block-backend.c +++ b/qcow2/lib/block/block-backend.c @@ -15,18 +15,18 @@ @@ -2246,9 +2030,8 @@ index db6f9b92..d15da172 100644 #include "qemu/id.h" #include "qemu/main-loop.h" #include "qemu/option.h" --#include "trace.h" + #include "trace.h" -#include "migration/misc.h" -+//#include "trace.h" +//#include "migration/misc.h" /* Number of coroutines to reserve per attached device model */ @@ -2294,24 +2077,6 @@ index db6f9b92..d15da172 100644 blk_set_perm_locked(blk, blk->perm, blk->shared_perm, &local_err); if (local_err) { -@@ -285,7 +289,7 @@ static void blk_root_attach(BdrvChild *child) - BlockBackend *blk = child->opaque; - BlockBackendAioNotifier *notifier; - -- trace_blk_root_attach(child, blk, child->bs); -+ //trace_blk_root_attach(child, blk, child->bs); - - QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { - bdrv_add_aio_context_notifier(child->bs, -@@ -300,7 +304,7 @@ static void blk_root_detach(BdrvChild *child) - BlockBackend *blk = child->opaque; - BlockBackendAioNotifier *notifier; - -- trace_blk_root_detach(child, blk, child->bs); -+ //trace_blk_root_detach(child, blk, child->bs); - - QLIST_FOREACH(notifier, &blk->aio_notifiers, list) { - bdrv_remove_aio_context_notifier(child->bs, @@ -480,16 +484,20 @@ static void blk_delete(BlockBackend *blk) assert(!blk->refcnt); assert(!blk->name); @@ -2530,15 +2295,6 @@ index db6f9b92..d15da172 100644 void blk_set_allow_write_beyond_eof(BlockBackend *blk, bool allow) { -@@ -1329,7 +1355,7 @@ blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, - - /* Call blk_bs() only after waiting, the graph may have changed */ - bs = blk_bs(blk); -- trace_blk_co_preadv(blk, bs, offset, bytes, flags); -+ //trace_blk_co_preadv(blk, bs, offset, bytes, flags); - - ret = blk_check_byte_request(blk, offset, bytes); - if (ret < 0) { @@ -1338,11 +1364,13 @@ blk_co_do_preadv_part(BlockBackend *blk, int64_t offset, int64_t bytes, bdrv_inc_in_flight(bs); @@ -2553,15 +2309,6 @@ index db6f9b92..d15da172 100644 ret = bdrv_co_preadv_part(blk->root, offset, bytes, qiov, qiov_offset, flags); -@@ -1404,7 +1432,7 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, - - /* Call blk_bs() only after waiting, the graph may have changed */ - bs = blk_bs(blk); -- trace_blk_co_pwritev(blk, bs, offset, bytes, flags); -+ //trace_blk_co_pwritev(blk, bs, offset, bytes, flags); - - ret = blk_check_byte_request(blk, offset, bytes); - if (ret < 0) { @@ -1412,11 +1440,13 @@ blk_co_do_pwritev_part(BlockBackend *blk, int64_t offset, int64_t bytes, } @@ -2752,7 +2499,7 @@ index db6f9b92..d15da172 100644 if (qatomic_fetch_dec(&blk->quiesce_counter) == 1) { if (blk->dev_ops && blk->dev_ops->drained_end) { diff --git a/qcow2/lib/block/block-gen.c b/qcow2/lib/block/block-gen.c -index ff2832fa..8d4f3294 100644 +index ff2832fa..6265dd52 100644 --- a/qcow2/lib/block/block-gen.c +++ b/qcow2/lib/block/block-gen.c @@ -2190,6 +2190,7 @@ int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset, int64_t bytes, BdrvRequ @@ -2771,63 +2518,42 @@ index ff2832fa..8d4f3294 100644 /* -diff --git a/qcow2/lib/block/commit.c b/qcow2/lib/block/commit.c -index 7c3fdcb0..6b96a4c6 100644 ---- a/qcow2/lib/block/commit.c -+++ b/qcow2/lib/block/commit.c -@@ -14,7 +14,7 @@ - - #include "qemu/osdep.h" - #include "qemu/cutils.h" --#include "trace.h" -+//#include "trace.h" - #include "block/block_int.h" - #include "block/blockjob_int.h" - #include "qapi/error.h" -@@ -170,7 +170,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp) - ret = blk_co_is_allocated_above(s->top, s->base_overlay, true, - offset, COMMIT_BUFFER_SIZE, &n); - copy = (ret > 0); -- trace_commit_one_iteration(s, offset, n, ret); -+ //trace_commit_one_iteration(s, offset, n, ret); - if (copy) { - assert(n < SIZE_MAX); - -@@ -414,7 +414,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, - s->backing_mask_protocol = backing_mask_protocol; - s->on_error = on_error; - -- trace_commit_start(bs, base, top, s); -+ //trace_commit_start(bs, base, top, s); - job_start(&s->common.job); - return; - +@@ -2592,7 +2594,7 @@ int bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, + /* + * Wrappers for nbd_co_do_establish_connection + */ +- ++#if 0 + typedef struct NbdDoEstablishConnection { + BdrvPollCo poll_state; + int ret; +@@ -2634,3 +2636,4 @@ int nbd_do_establish_connection(BlockDriverState *bs, bool blocking, Error **err + return s.ret; + } + } ++#endif diff --git a/qcow2/lib/block/dirty-bitmap.c b/qcow2/lib/block/dirty-bitmap.c -index 13a19797..df6a3c30 100644 +index 13a19797..557059de 100644 --- a/qcow2/lib/block/dirty-bitmap.c +++ b/qcow2/lib/block/dirty-bitmap.c -@@ -23,7 +23,7 @@ - */ - #include "qemu/osdep.h" - #include "qapi/error.h" --#include "trace.h" -+//#include "trace.h" - #include "block/block-io.h" - #include "block/block_int.h" - #include "block/blockjob.h" +@@ -770,10 +770,12 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BdrvDirtyBitmap *bitmap) + return QLIST_NEXT(bitmap, list); + } + ++#if 0 + char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) + { + return hbitmap_sha256(bitmap->bitmap, errp); + } ++#endif + + int64_t bdrv_dirty_bitmap_next_dirty(BdrvDirtyBitmap *bitmap, int64_t offset, + int64_t bytes) diff --git a/qcow2/lib/block/file-posix.c b/qcow2/lib/block/file-posix.c -index ff928b5e..852f0222 100644 +index ff928b5e..3223c75e 100644 --- a/qcow2/lib/block/file-posix.c +++ b/qcow2/lib/block/file-posix.c -@@ -32,15 +32,15 @@ - #include "qemu/option.h" - #include "qemu/units.h" - #include "qemu/memalign.h" --#include "trace.h" -+//#include "trace.h" - #include "block/thread-pool.h" - #include "qemu/iov.h" - #include "block/raw-aio.h" +@@ -39,8 +39,8 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -2907,95 +2633,15 @@ index ff928b5e..852f0222 100644 static int handle_aiocb_ioctl(void *opaque) { RawPosixAIOData *aiocb = opaque; -@@ -1627,7 +1633,7 @@ static int handle_aiocb_flush(void *opaque) - - ret = qemu_fdatasync(aiocb->aio_fildes); - if (ret == -1) { -- trace_file_flush_fdatasync_failed(errno); -+ //trace_file_flush_fdatasync_failed(errno); - - /* There is no clear definition of the semantics of a failing fsync(), - * so we may have to assume the worst. The sad truth is that this -@@ -2168,9 +2174,9 @@ static int handle_aiocb_copy_range(void *opaque) - ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, - aiocb->copy_range.aio_fd2, &out_off, - bytes, 0); -- trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, -- aiocb->copy_range.aio_fd2, out_off, bytes, -- 0, ret); -+ //trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, -+ // aiocb->copy_range.aio_fd2, out_off, bytes, -+ // 0, ret); - if (ret == 0) { - /* No progress (e.g. when beyond EOF), let the caller fall back to - * buffer I/O. */ -@@ -2546,8 +2552,8 @@ out: - if (!BDRV_ZT_IS_CONV(*wp)) { - if (type & QEMU_AIO_ZONE_APPEND) { - *offset_ptr = *wp; -- trace_zbd_zone_append_complete(bs, *offset_ptr -- >> BDRV_SECTOR_BITS); -+ //trace_zbd_zone_append_complete(bs, *offset_ptr -+ // >> BDRV_SECTOR_BITS); - } - /* Advance the wp if needed */ - if (offset + bytes > *wp) { -@@ -3404,7 +3410,7 @@ static int coroutine_fn raw_co_zone_report(BlockDriverState *bs, int64_t offset, - }, - }; - -- trace_zbd_zone_report(bs, *nr_zones, offset >> BDRV_SECTOR_BITS); -+ //trace_zbd_zone_report(bs, *nr_zones, offset >> BDRV_SECTOR_BITS); - return raw_thread_pool_submit(handle_aiocb_zone_report, &acb); - } - #endif -@@ -3480,8 +3486,8 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, - }, +@@ -3481,7 +3487,7 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, }; -- trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, + trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, - len >> BDRV_SECTOR_BITS); -+ //trace_zbd_zone_mgmt(bs, op_name, offset >> BDRV_SECTOR_BITS, + // len >> BDRV_SECTOR_BITS); ret = raw_thread_pool_submit(handle_aiocb_zone_mgmt, &acb); if (ret != 0) { update_zones_wp(bs, s->fd, offset, nrz); -@@ -3539,7 +3545,7 @@ static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, - len += iov_len; - } - -- trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); -+ //trace_zbd_zone_append(bs, *offset >> BDRV_SECTOR_BITS); - return raw_co_prw(bs, offset, len, qiov, QEMU_AIO_ZONE_APPEND); - } - #endif -@@ -3962,7 +3968,7 @@ static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) - - /* If a match was found, leave the loop */ - if (*mediaIterator != 0) { -- trace_file_FindEjectableOpticalMedia(matching_array[index]); -+ //trace_file_FindEjectableOpticalMedia(matching_array[index]); - mediaType = g_strdup(matching_array[index]); - break; - } -@@ -4022,7 +4028,7 @@ static bool setup_cdrom(char *bsd_path, Error **errp) - if (partition_found == false) { - error_setg(errp, "Failed to find a working partition on disc"); - } else { -- trace_file_setup_cdrom(test_partition); -+ //trace_file_setup_cdrom(test_partition); - pstrcpy(bsd_path, MAXPATHLEN, test_partition); - } - return partition_found; -@@ -4084,7 +4090,7 @@ static bool hdev_is_sg(BlockDriverState *bs) - - ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); - if (ret >= 0) { -- trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); -+ //trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); - return true; - } - @@ -4194,6 +4200,7 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) return ret; } @@ -3013,15 +2659,10 @@ index ff928b5e..852f0222 100644 acb = (RawPosixAIOData) { .bs = bs, diff --git a/qcow2/lib/block/io.c b/qcow2/lib/block/io.c -index 301514c8..ae2adc51 100644 +index 301514c8..f5aaade3 100644 --- a/qcow2/lib/block/io.c +++ b/qcow2/lib/block/io.c -@@ -23,21 +23,23 @@ - */ - - #include "qemu/osdep.h" --#include "trace.h" -+//#include "trace.h" +@@ -27,17 +27,19 @@ #include "sysemu/block-backend.h" #include "block/aio-wait.h" #include "block/blockjob.h" @@ -3044,17 +2685,6 @@ index 301514c8..ae2adc51 100644 /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) -@@ -1209,8 +1211,8 @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, - bdrv_round_to_subclusters(bs, offset, bytes, &align_offset, &align_bytes); - skip_bytes = offset - align_offset; - -- trace_bdrv_co_do_copy_on_readv(bs, offset, bytes, -- align_offset, align_bytes); -+ //trace_bdrv_co_do_copy_on_readv(bs, offset, bytes, -+ // align_offset, align_bytes); - - while (align_bytes) { - int64_t pnum; @@ -1263,7 +1265,7 @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes, goto err; } @@ -3108,15 +2738,6 @@ index 301514c8..ae2adc51 100644 } zero_mem: -@@ -1779,7 +1781,7 @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child, - int ret; - IO_CODE(); - -- trace_bdrv_co_preadv_part(bs, offset, bytes, flags); -+ //trace_bdrv_co_preadv_part(bs, offset, bytes, flags); - - if (!bdrv_co_is_inserted(bs)) { - return -ENOMEDIUM; @@ -2001,7 +2003,7 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, int64_t bytes, } else { assert(child->perm & BLK_PERM_WRITE); @@ -3155,24 +2776,7 @@ index 301514c8..ae2adc51 100644 if (ret >= 0) { ret = 0; -@@ -2233,7 +2235,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child, - bool padded = false; - IO_CODE(); - -- trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); -+ //trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags); - - if (!bdrv_co_is_inserted(bs)) { - return -ENOMEDIUM; -@@ -2317,13 +2319,14 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, - int64_t bytes, BdrvRequestFlags flags) - { - IO_CODE(); -- trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); -+ //trace_bdrv_co_pwrite_zeroes(child->bs, offset, bytes, flags); - assert_bdrv_graph_readable(); - - return bdrv_co_pwritev(child, offset, bytes, NULL, +@@ -2324,6 +2326,7 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, BDRV_REQ_ZERO_WRITE | flags); } @@ -3220,28 +2824,6 @@ index 301514c8..ae2adc51 100644 void *qemu_blockalign(BlockDriverState *bs, size_t size) { -@@ -3478,8 +3486,8 @@ int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, int64_t src_offset, - { - IO_CODE(); - assert_bdrv_graph_readable(); -- trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, -- read_flags, write_flags); -+ //trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, -+ // read_flags, write_flags); - return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, - bytes, read_flags, write_flags, true); - } -@@ -3496,8 +3504,8 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset, - { - IO_CODE(); - assert_bdrv_graph_readable(); -- trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, -- read_flags, write_flags); -+ //trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, -+ // read_flags, write_flags); - return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, - bytes, read_flags, write_flags, false); - } @@ -3679,6 +3687,7 @@ void bdrv_cancel_in_flight(BlockDriverState *bs) } } @@ -3256,121 +2838,10 @@ index 301514c8..ae2adc51 100644 } +#endif diff --git a/qcow2/lib/block/mirror.c b/qcow2/lib/block/mirror.c -index 61f0a717..4b75f105 100644 +index 61f0a717..1b34bb9e 100644 --- a/qcow2/lib/block/mirror.c +++ b/qcow2/lib/block/mirror.c -@@ -15,7 +15,7 @@ - #include "qemu/cutils.h" - #include "qemu/coroutine.h" - #include "qemu/range.h" --#include "trace.h" -+//#include "trace.h" - #include "block/blockjob_int.h" - #include "block/block_int.h" - #include "block/dirty-bitmap.h" -@@ -201,7 +201,9 @@ static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) - int64_t chunk_num; - int i, nb_chunks; - -+#if 0 - trace_mirror_iteration_done(s, op->offset, op->bytes, ret); -+#endif - - s->in_flight--; - s->bytes_in_flight -= op->bytes; -@@ -375,7 +377,9 @@ static void coroutine_fn mirror_co_read(void *opaque) - nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); - - while (s->buf_free_count < nb_chunks) { -+#if 0 - trace_mirror_yield_in_flight(s, op->offset, s->in_flight); -+#endif - mirror_wait_for_free_in_flight_slot(s); - } - -@@ -396,7 +400,9 @@ static void coroutine_fn mirror_co_read(void *opaque) - s->in_flight++; - s->bytes_in_flight += op->bytes; - op->is_in_flight = true; -+#if 0 - trace_mirror_one_iteration(s, op->offset, op->bytes); -+#endif - - WITH_GRAPH_RDLOCK_GUARD() { - ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes, -@@ -499,7 +505,9 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) - if (offset < 0) { - bdrv_set_dirty_iter(s->dbi, 0); - offset = bdrv_dirty_iter_next(s->dbi); -+#if 0 - trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); -+#endif - assert(offset >= 0); - } - bdrv_dirty_bitmap_unlock(s->dirty_bitmap); -@@ -601,7 +609,9 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) - } - - while (s->in_flight >= MAX_IN_FLIGHT) { -+#if 0 - trace_mirror_yield_in_flight(s, offset, s->in_flight); -+#endif - mirror_wait_for_free_in_flight_slot(s); - } - -@@ -867,8 +877,10 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) - } - - if (s->in_flight >= MAX_IN_FLIGHT) { -+#if 0 - trace_mirror_yield(s, UINT64_MAX, s->buf_free_count, - s->in_flight); -+#endif - mirror_wait_for_free_in_flight_slot(s); - continue; - } -@@ -1071,7 +1083,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) - iostatus == BLOCK_DEVICE_IO_STATUS_OK) { - if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || - (cnt == 0 && s->in_flight > 0)) { -+#if 0 - trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); -+#endif - mirror_wait_for_free_in_flight_slot(s); - continue; - } else if (cnt != 0) { -@@ -1081,7 +1095,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) - - should_complete = false; - if (s->in_flight == 0 && cnt == 0) { -+#if 0 - trace_mirror_before_flush(s); -+#endif - if (!job_is_ready(&s->common.job)) { - if (mirror_flush(s) < 0) { - /* Go check s->ret. */ -@@ -1114,7 +1130,9 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) - * whether to switch to target check one last time if I/O has - * come in the meanwhile, and if not flush the data to disk. - */ -+#if 0 - trace_mirror_before_drain(s, cnt); -+#endif - - s->in_drain = true; - bdrv_drained_begin(bs); -@@ -1139,8 +1157,10 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) - - if (job_is_ready(&s->common.job) && !should_complete) { - if (s->in_flight == 0 && cnt == 0) { -+#if 0 - trace_mirror_before_sleep(s, cnt, job_is_ready(&s->common.job), - BLOCK_JOB_SLICE_TIME); -+#endif - job_sleep_ns(&s->common.job, BLOCK_JOB_SLICE_TIME); - } - } else { -@@ -1235,6 +1255,7 @@ static bool mirror_drained_poll(BlockJob *job) +@@ -1235,6 +1235,7 @@ static bool mirror_drained_poll(BlockJob *job) return !!s->in_flight; } @@ -3378,7 +2849,7 @@ index 61f0a717..4b75f105 100644 static bool mirror_cancel(Job *job, bool force) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); -@@ -1251,6 +1272,7 @@ static bool mirror_cancel(Job *job, bool force) +@@ -1251,6 +1252,7 @@ static bool mirror_cancel(Job *job, bool force) } return force; } @@ -3386,7 +2857,7 @@ index 61f0a717..4b75f105 100644 static bool commit_active_cancel(Job *job, bool force) { -@@ -1258,6 +1280,7 @@ static bool commit_active_cancel(Job *job, bool force) +@@ -1258,6 +1260,7 @@ static bool commit_active_cancel(Job *job, bool force) return force || !job_is_ready(job); } @@ -3394,7 +2865,7 @@ index 61f0a717..4b75f105 100644 static void mirror_change(BlockJob *job, BlockJobChangeOptions *opts, Error **errp) { -@@ -1317,6 +1340,7 @@ static const BlockJobDriver mirror_job_driver = { +@@ -1317,6 +1320,7 @@ static const BlockJobDriver mirror_job_driver = { .change = mirror_change, .query = mirror_query, }; @@ -3402,16 +2873,7 @@ index 61f0a717..4b75f105 100644 static const BlockJobDriver commit_active_job_driver = { .job_driver = { -@@ -1969,7 +1993,7 @@ static BlockJob *mirror_start_job( - - QTAILQ_INIT(&s->ops_in_flight); - -- trace_mirror_start(bs, s, opaque); -+ //trace_mirror_start(bs, s, opaque); - job_start(&s->common.job); - - return &s->common; -@@ -2004,6 +2028,7 @@ fail: +@@ -2004,6 +2008,7 @@ fail: return NULL; } @@ -3419,7 +2881,7 @@ index 61f0a717..4b75f105 100644 void mirror_start(const char *job_id, BlockDriverState *bs, BlockDriverState *target, const char *replaces, int creation_flags, int64_t speed, -@@ -2038,6 +2063,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, +@@ -2038,6 +2043,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, &mirror_job_driver, is_none_mode, base, false, filter_node_name, true, copy_mode, false, errp); } @@ -3427,148 +2889,6 @@ index 61f0a717..4b75f105 100644 BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, int creation_flags, -diff --git a/qcow2/lib/block/nbd.c b/qcow2/lib/block/nbd.c -index 2c65cdcf..2710edce 100644 ---- a/qcow2/lib/block/nbd.c -+++ b/qcow2/lib/block/nbd.c -@@ -30,7 +30,7 @@ - - #include "qemu/osdep.h" - --#include "trace.h" -+//#include "trace.h" - #include "qemu/uri.h" - #include "qemu/option.h" - #include "qemu/cutils.h" -@@ -311,7 +311,7 @@ nbd_handle_updated_info(BlockDriverState *bs, Error **errp) - } - } - -- trace_nbd_client_handshake_success(s->export); -+ //trace_nbd_client_handshake_success(s->export); - - return 0; - } -@@ -373,7 +373,7 @@ static bool nbd_client_connecting(BDRVNBDState *s) - /* Called with s->requests_lock taken. */ - static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) - { -- int ret; -+ //int ret; - bool blocking = s->state == NBD_CLIENT_CONNECTING_WAIT; - - /* -@@ -383,7 +383,7 @@ static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) - assert(nbd_client_connecting(s)); - assert(s->in_flight == 1); - -- trace_nbd_reconnect_attempt(s->bs->in_flight); -+ //trace_nbd_reconnect_attempt(s->bs->in_flight); - - if (blocking && !s->reconnect_delay_timer) { - /* -@@ -405,8 +405,9 @@ static void coroutine_fn GRAPH_RDLOCK nbd_reconnect_attempt(BDRVNBDState *s) - } - - qemu_mutex_unlock(&s->requests_lock); -- ret = nbd_co_do_establish_connection(s->bs, blocking, NULL); -- trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight); -+ nbd_co_do_establish_connection(s->bs, blocking, NULL); -+ //ret = nbd_co_do_establish_connection(s->bs, blocking, NULL); -+ //trace_nbd_reconnect_attempt_result(ret, s->bs->in_flight); - qemu_mutex_lock(&s->requests_lock); - - /* -@@ -601,7 +602,7 @@ static int nbd_parse_offset_hole_payload(BDRVNBDState *s, - } - if (s->info.min_block && - !QEMU_IS_ALIGNED(hole_size, s->info.min_block)) { -- trace_nbd_structured_read_compliance("hole"); -+ //trace_nbd_structured_read_compliance("hole"); - } - - qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size); -@@ -671,7 +672,7 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, - */ - if (s->info.min_block && !QEMU_IS_ALIGNED(extent->length, - s->info.min_block)) { -- trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); -+ //trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); - if (extent->length > s->info.min_block) { - extent->length = QEMU_ALIGN_DOWN(extent->length, - s->info.min_block); -@@ -692,11 +693,11 @@ static int nbd_parse_blockstatus_payload(BDRVNBDState *s, - * the length of our request. - */ - if (count != wide || chunk->length > pay_len) { -- trace_nbd_parse_blockstatus_compliance("unexpected extent count"); -+ //trace_nbd_parse_blockstatus_compliance("unexpected extent count"); - } - if (extent->length > orig_length) { - extent->length = orig_length; -- trace_nbd_parse_blockstatus_compliance("extent length too large"); -+ //trace_nbd_parse_blockstatus_compliance("extent length too large"); - } - - /* -@@ -786,7 +787,7 @@ nbd_co_receive_offset_data_payload(BDRVNBDState *s, uint64_t orig_offset, - return -EINVAL; - } - if (s->info.min_block && !QEMU_IS_ALIGNED(data_size, s->info.min_block)) { -- trace_nbd_structured_read_compliance("data"); -+ //trace_nbd_structured_read_compliance("data"); - } - - qemu_iovec_init(&sub_qiov, qiov->niov); -@@ -1161,7 +1162,7 @@ nbd_co_receive_blockstatus_reply(BDRVNBDState *s, uint64_t cookie, - case NBD_REPLY_TYPE_BLOCK_STATUS: - wide = chunk->type == NBD_REPLY_TYPE_BLOCK_STATUS_EXT; - if ((s->info.mode >= NBD_MODE_EXTENDED) != wide) { -- trace_nbd_extended_headers_compliance("block_status"); -+ //trace_nbd_extended_headers_compliance("block_status"); - } - if (received) { - nbd_channel_error(s, -EINVAL); -@@ -1228,11 +1229,13 @@ nbd_co_request(BlockDriverState *bs, NBDRequest *request, - ret = nbd_co_receive_return_code(s, request->cookie, - &request_ret, &local_err); - if (local_err) { -+#if 0 - trace_nbd_co_request_fail(request->from, request->len, - request->cookie, request->flags, - request->type, - nbd_cmd_lookup(request->type), - ret, error_get_pretty(local_err)); -+#endif - error_free(local_err); - local_err = NULL; - } -@@ -1287,10 +1290,12 @@ nbd_client_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, - ret = nbd_co_receive_cmdread_reply(s, request.cookie, offset, qiov, - &request_ret, &local_err); - if (local_err) { -+#if 0 - trace_nbd_co_request_fail(request.from, request.len, request.cookie, - request.flags, request.type, - nbd_cmd_lookup(request.type), - ret, error_get_pretty(local_err)); -+#endif - error_free(local_err); - local_err = NULL; - } -@@ -1451,10 +1456,12 @@ static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( - &extent, &request_ret, - &local_err); - if (local_err) { -+#if 0 - trace_nbd_co_request_fail(request.from, request.len, request.cookie, - request.flags, request.type, - nbd_cmd_lookup(request.type), - ret, error_get_pretty(local_err)); -+#endif - error_free(local_err); - local_err = NULL; - } diff --git a/qcow2/lib/block/qapi.c b/qcow2/lib/block/qapi.c index 2b5793f1..82e12895 100644 --- a/qcow2/lib/block/qapi.c @@ -3633,7 +2953,7 @@ index ac7e8495..f3343e98 100644 .bdrv_co_copy_range_from = &raw_co_copy_range_from, .bdrv_co_copy_range_to = &raw_co_copy_range_to, diff --git a/qcow2/lib/blockdev.c b/qcow2/lib/blockdev.c -index 835064ed..53279249 100644 +index 835064ed..95cba97c 100644 --- a/qcow2/lib/blockdev.c +++ b/qcow2/lib/blockdev.c @@ -37,14 +37,14 @@ @@ -3663,10 +2983,9 @@ index 835064ed..53279249 100644 +//#include "sysemu/sysemu.h" +//#include "sysemu/iothread.h" #include "block/block_int.h" --#include "block/trace.h" + #include "block/trace.h" -#include "sysemu/runstate.h" -#include "sysemu/replay.h" -+//#include "block/trace.h" +//#include "sysemu/runstate.h" +//#include "sysemu/replay.h" #include "qemu/cutils.h" @@ -3733,18 +3052,9 @@ index 835064ed..53279249 100644 }; +#endif diff --git a/qcow2/lib/blockjob.c b/qcow2/lib/blockjob.c -index d5f29e14..b739a1ca 100644 +index d5f29e14..071b511c 100644 --- a/qcow2/lib/blockjob.c +++ b/qcow2/lib/blockjob.c -@@ -28,7 +28,7 @@ - #include "block/block.h" - #include "block/blockjob_int.h" - #include "block/block_int.h" --#include "block/trace.h" -+//#include "block/trace.h" - #include "sysemu/block-backend.h" - #include "qapi/error.h" - #include "qapi/qapi-events-block-core.h" @@ -213,6 +213,7 @@ void block_job_remove_all_bdrv(BlockJob *job) bdrv_graph_wrunlock(); } @@ -3777,232 +3087,6 @@ index d5f29e14..b739a1ca 100644 void block_job_ratelimit_processed_bytes(BlockJob *job, uint64_t n) { -diff --git a/qcow2/lib/crypto/hash.c b/qcow2/lib/crypto/hash.c -index b0f8228b..a282cf74 100644 ---- a/qcow2/lib/crypto/hash.c -+++ b/qcow2/lib/crypto/hash.c -@@ -18,20 +18,92 @@ - * - */ - -+#include -+ - #include "qemu/osdep.h" - #include "crypto/hash.h" --#include "hashpriv.h" -+//#include "hashpriv.h" - - static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { -- [QCRYPTO_HASH_ALG_MD5] = 16, -- [QCRYPTO_HASH_ALG_SHA1] = 20, -- [QCRYPTO_HASH_ALG_SHA224] = 28, -- [QCRYPTO_HASH_ALG_SHA256] = 32, -- [QCRYPTO_HASH_ALG_SHA384] = 48, -- [QCRYPTO_HASH_ALG_SHA512] = 64, -- [QCRYPTO_HASH_ALG_RIPEMD160] = 20, -+ [QCRYPTO_HASH_ALG_MD5] = QCRYPTO_HASH_DIGEST_LEN_MD5, -+ [QCRYPTO_HASH_ALG_SHA1] = QCRYPTO_HASH_DIGEST_LEN_SHA1, -+ [QCRYPTO_HASH_ALG_SHA224] = QCRYPTO_HASH_DIGEST_LEN_SHA224, -+ [QCRYPTO_HASH_ALG_SHA256] = QCRYPTO_HASH_DIGEST_LEN_SHA256, -+ [QCRYPTO_HASH_ALG_SHA384] = QCRYPTO_HASH_DIGEST_LEN_SHA384, -+ [QCRYPTO_HASH_ALG_SHA512] = QCRYPTO_HASH_DIGEST_LEN_SHA512, -+ [QCRYPTO_HASH_ALG_RIPEMD160] = QCRYPTO_HASH_DIGEST_LEN_RIPEMD160, -+}; -+ -+static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { -+ [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, -+ [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, -+ [QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224, -+ [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, -+ [QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384, -+ [QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512, -+ [QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160, - }; - -+gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) -+{ -+ size_t i; -+ const gnutls_digest_algorithm_t *algs; -+ if (alg >= QCRYPTO_HASH_ALG__MAX || -+ qcrypto_hash_alg_map[alg] == GNUTLS_DIG_UNKNOWN) { -+ return false; -+ } -+ algs = gnutls_digest_list(); -+ for (i = 0; algs[i] != GNUTLS_DIG_UNKNOWN; i++) { -+ if (algs[i] == qcrypto_hash_alg_map[alg]) { -+ return true; -+ } -+ } -+ return false; -+} -+ -+static int -+qcrypto_gnutls_hash_bytesv(QCryptoHashAlgorithm alg, -+ const struct iovec *iov, -+ size_t niov, -+ uint8_t **result, -+ size_t *resultlen, -+ Error **errp) -+{ -+ int i, ret; -+ gnutls_hash_hd_t hash; -+ -+ if (!qcrypto_hash_supports(alg)) { -+ fprintf(stderr, "Unknown hash algorithm %d.\n", alg); -+ return -1; -+ } -+ -+ ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]); -+ if (*resultlen == 0) { -+ *resultlen = ret; -+ *result = calloc(sizeof(uint8_t), *resultlen); -+ } else if (*resultlen != ret) { -+ fprintf(stderr, -+ "Result buffer size %zu is smaller than hash %d.\n", -+ *resultlen, ret); -+ return -1; -+ } -+ -+ ret = gnutls_hash_init(&hash, qcrypto_hash_alg_map[alg]); -+ if (ret < 0) { -+ fprintf(stderr, -+ "Unable to initialize hash algorithm: %s.\n", -+ gnutls_strerror(ret)); -+ return -1; -+ } -+ -+ for (i = 0; i < niov; i++) { -+ gnutls_hash(hash, iov[i].iov_base, iov[i].iov_len); -+ } -+ -+ gnutls_hash_deinit(hash, *result); -+ return 0; -+} -+ - size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) - { - assert(alg < G_N_ELEMENTS(qcrypto_hash_alg_size)); -@@ -59,9 +131,9 @@ int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, - } - #endif - -- return qcrypto_hash_lib_driver.hash_bytesv(alg, iov, niov, -- result, resultlen, -- errp); -+ return qcrypto_gnutls_hash_bytesv(alg, iov, niov, -+ result, resultlen, -+ errp); - } - - -@@ -114,6 +186,7 @@ int qcrypto_hash_digest(QCryptoHashAlgorithm alg, - return qcrypto_hash_digestv(alg, &iov, 1, digest, errp); - } - -+#if 0 - int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, - const struct iovec *iov, - size_t niov, -@@ -142,3 +215,4 @@ int qcrypto_hash_base64(QCryptoHashAlgorithm alg, - - return qcrypto_hash_base64v(alg, &iov, 1, base64, errp); - } -+#endif -diff --git a/qcow2/lib/crypto/tlscreds.c b/qcow2/lib/crypto/tlscreds.c -index 084ce0d5..eb4a1684 100644 ---- a/qcow2/lib/crypto/tlscreds.c -+++ b/qcow2/lib/crypto/tlscreds.c -@@ -20,10 +20,10 @@ - - #include "qemu/osdep.h" - #include "qapi/error.h" --#include "qapi-types-crypto.h" -+#include "qapi/qapi-types-crypto.h" - #include "qemu/module.h" - #include "tlscredspriv.h" --#include "trace.h" -+//#include "trace.h" - - #define DH_BITS 2048 - -@@ -36,7 +36,7 @@ qcrypto_tls_creds_get_dh_params_file(QCryptoTLSCreds *creds, - { - int ret; - -- trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : ""); -+ //trace_qcrypto_tls_creds_load_dh(creds, filename ? filename : ""); - - if (filename == NULL) { - ret = gnutls_dh_params_init(dh_params); -@@ -129,8 +129,8 @@ qcrypto_tls_creds_get_path(QCryptoTLSCreds *creds, - - ret = 0; - cleanup: -- trace_qcrypto_tls_creds_get_path(creds, filename, -- *cred ? *cred : ""); -+ //trace_qcrypto_tls_creds_get_path(creds, filename, -+ // *cred ? *cred : ""); - return ret; - } - -diff --git a/qcow2/lib/crypto/tlssession.c b/qcow2/lib/crypto/tlssession.c -index 77286e23..83aa84dc 100644 ---- a/qcow2/lib/crypto/tlssession.c -+++ b/qcow2/lib/crypto/tlssession.c -@@ -26,7 +26,7 @@ - #include "qapi/error.h" - #include "authz/base.h" - #include "tlscredspriv.h" --#include "trace.h" -+//#include "trace.h" - - #ifdef CONFIG_GNUTLS - -@@ -140,9 +140,11 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, - int ret; - - session = g_new0(QCryptoTLSSession, 1); -+#if 0 - trace_qcrypto_tls_session_new( - session, creds, hostname ? hostname : "", - authzid ? authzid : "", endpoint); -+#endif - - if (hostname) { - session->hostname = g_strdup(hostname); -@@ -435,26 +437,26 @@ qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, - { - if (object_dynamic_cast(OBJECT(session->creds), - TYPE_QCRYPTO_TLS_CREDS_ANON)) { -- trace_qcrypto_tls_session_check_creds(session, "nop"); -+ //trace_qcrypto_tls_session_check_creds(session, "nop"); - return 0; - } else if (object_dynamic_cast(OBJECT(session->creds), - TYPE_QCRYPTO_TLS_CREDS_PSK)) { -- trace_qcrypto_tls_session_check_creds(session, "nop"); -+ //trace_qcrypto_tls_session_check_creds(session, "nop"); - return 0; - } else if (object_dynamic_cast(OBJECT(session->creds), - TYPE_QCRYPTO_TLS_CREDS_X509)) { - if (session->creds->verifyPeer) { - int ret = qcrypto_tls_session_check_certificate(session, - errp); -- trace_qcrypto_tls_session_check_creds(session, -- ret == 0 ? "pass" : "fail"); -+ //trace_qcrypto_tls_session_check_creds(session, -+ // ret == 0 ? "pass" : "fail"); - return ret; - } else { -- trace_qcrypto_tls_session_check_creds(session, "skip"); -+ //trace_qcrypto_tls_session_check_creds(session, "skip"); - return 0; - } - } else { -- trace_qcrypto_tls_session_check_creds(session, "error"); -+ //trace_qcrypto_tls_session_check_creds(session, "error"); - error_setg(errp, "Unexpected credential type %s", - object_get_typename(OBJECT(session->creds))); - return -1; diff --git a/qcow2/lib/hw/block/block.c b/qcow2/lib/hw/block/block.c index 3ceca7dc..9a0da1e6 100644 --- a/qcow2/lib/hw/block/block.c @@ -4041,890 +3125,6 @@ index 3ceca7dc..9a0da1e6 100644 } if (conf->cyls || conf->heads || conf->secs) { if (conf->cyls < 1 || conf->cyls > cyls_max) { -diff --git a/qcow2/lib/hw/block/hd-geometry.c b/qcow2/lib/hw/block/hd-geometry.c -index 2b0af443..498f7a81 100644 ---- a/qcow2/lib/hw/block/hd-geometry.c -+++ b/qcow2/lib/hw/block/hd-geometry.c -@@ -35,7 +35,7 @@ - #include "qapi/qapi-types-block.h" - #include "qemu/bswap.h" - #include "hw/block/block.h" --#include "trace.h" -+//#include "trace.h" - - struct partition { - uint8_t boot_ind; /* 0x80 - active */ -@@ -88,7 +88,7 @@ static int guess_disk_lchs(BlockBackend *blk, - *pheads = heads; - *psectors = sectors; - *pcylinders = cylinders; -- trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors); -+ //trace_hd_geometry_lchs_guess(blk, cylinders, heads, sectors); - return 0; - } - } -@@ -157,7 +157,7 @@ void hd_geometry_guess(BlockBackend *blk, - translation = *ptrans; - } - } -- trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); -+ //trace_hd_geometry_guess(blk, *pcyls, *pheads, *psecs, translation); - } - - int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs) -diff --git a/qcow2/lib/io/channel-file.c b/qcow2/lib/io/channel-file.c -index 2ea8d083..dcd512dd 100644 ---- a/qcow2/lib/io/channel-file.c -+++ b/qcow2/lib/io/channel-file.c -@@ -25,7 +25,7 @@ - #include "qapi/error.h" - #include "qemu/module.h" - #include "qemu/sockets.h" --#include "trace.h" -+//#include "trace.h" - - QIOChannelFile * - qio_channel_file_new_fd(int fd) -@@ -40,7 +40,7 @@ qio_channel_file_new_fd(int fd) - qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); - } - -- trace_qio_channel_file_new_fd(ioc, fd); -+ //trace_qio_channel_file_new_fd(ioc, fd); - - return ioc; - } -@@ -82,7 +82,7 @@ qio_channel_file_new_path(const char *path, - qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SEEKABLE); - } - -- trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd); -+ //trace_qio_channel_file_new_path(ioc, path, flags, mode, ioc->fd); - - return ioc; - } -diff --git a/qcow2/lib/io/channel-socket.c b/qcow2/lib/io/channel-socket.c -index 608bcf06..7081d0d3 100644 ---- a/qcow2/lib/io/channel-socket.c -+++ b/qcow2/lib/io/channel-socket.c -@@ -24,7 +24,7 @@ - #include "io/channel-socket.h" - #include "io/channel-util.h" - #include "io/channel-watch.h" --#include "trace.h" -+//#include "trace.h" - #include "qapi/clone-visitor.h" - #ifdef CONFIG_LINUX - #include -@@ -73,7 +73,7 @@ qio_channel_socket_new(void) - ioc->event = CreateEvent(NULL, FALSE, FALSE, NULL); - #endif - -- trace_qio_channel_socket_new(sioc); -+ //trace_qio_channel_socket_new(sioc); - - return sioc; - } -@@ -139,7 +139,7 @@ qio_channel_socket_new_fd(int fd, - return NULL; - } - -- trace_qio_channel_socket_new_fd(ioc, fd); -+ //trace_qio_channel_socket_new_fd(ioc, fd); - - return ioc; - } -@@ -151,14 +151,14 @@ int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, - { - int fd; - -- trace_qio_channel_socket_connect_sync(ioc, addr); -+ //trace_qio_channel_socket_connect_sync(ioc, addr); - fd = socket_connect(addr, errp); - if (fd < 0) { -- trace_qio_channel_socket_connect_fail(ioc); -+ //trace_qio_channel_socket_connect_fail(ioc); - return -1; - } - -- trace_qio_channel_socket_connect_complete(ioc, fd); -+ //trace_qio_channel_socket_connect_complete(ioc, fd); - if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { - close(fd); - return -1; -@@ -209,7 +209,7 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc, - - /* socket_connect() does a non-blocking connect(), but it - * still blocks in DNS lookups, so we must use a thread */ -- trace_qio_channel_socket_connect_async(ioc, addr); -+ //trace_qio_channel_socket_connect_async(ioc, addr); - qio_task_run_in_thread(task, - qio_channel_socket_connect_worker, - addrCopy, -@@ -225,14 +225,14 @@ int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, - { - int fd; - -- trace_qio_channel_socket_listen_sync(ioc, addr, num); -+ //trace_qio_channel_socket_listen_sync(ioc, addr, num); - fd = socket_listen(addr, num, errp); - if (fd < 0) { -- trace_qio_channel_socket_listen_fail(ioc); -+ //trace_qio_channel_socket_listen_fail(ioc); - return -1; - } - -- trace_qio_channel_socket_listen_complete(ioc, fd); -+ //trace_qio_channel_socket_listen_complete(ioc, fd); - if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { - close(fd); - return -1; -@@ -286,7 +286,7 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc, - data->num = num; - - /* socket_listen() blocks in DNS lookups, so we must use a thread */ -- trace_qio_channel_socket_listen_async(ioc, addr, num); -+ //trace_qio_channel_socket_listen_async(ioc, addr, num); - qio_task_run_in_thread(task, - qio_channel_socket_listen_worker, - data, -@@ -302,14 +302,14 @@ int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, - { - int fd; - -- trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr); -+ //trace_qio_channel_socket_dgram_sync(ioc, localAddr, remoteAddr); - fd = socket_dgram(remoteAddr, localAddr, errp); - if (fd < 0) { -- trace_qio_channel_socket_dgram_fail(ioc); -+ //trace_qio_channel_socket_dgram_fail(ioc); - return -1; - } - -- trace_qio_channel_socket_dgram_complete(ioc, fd); -+ //trace_qio_channel_socket_dgram_complete(ioc, fd); - if (qio_channel_socket_set_fd(ioc, fd, errp) < 0) { - close(fd); - return -1; -@@ -364,7 +364,7 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, - data->localAddr = QAPI_CLONE(SocketAddress, localAddr); - data->remoteAddr = QAPI_CLONE(SocketAddress, remoteAddr); - -- trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); -+ //trace_qio_channel_socket_dgram_async(ioc, localAddr, remoteAddr); - qio_task_run_in_thread(task, - qio_channel_socket_dgram_worker, - data, -@@ -384,7 +384,7 @@ qio_channel_socket_accept(QIOChannelSocket *ioc, - cioc->localAddrLen = sizeof(ioc->localAddr); - - retry: -- trace_qio_channel_socket_accept(ioc); -+ //trace_qio_channel_socket_accept(ioc); - cioc->fd = qemu_accept(ioc->fd, (struct sockaddr *)&cioc->remoteAddr, - &cioc->remoteAddrLen); - if (cioc->fd < 0) { -@@ -392,7 +392,7 @@ qio_channel_socket_accept(QIOChannelSocket *ioc, - goto retry; - } - error_setg_errno(errp, errno, "Unable to accept connection"); -- trace_qio_channel_socket_accept_fail(ioc); -+ //trace_qio_channel_socket_accept_fail(ioc); - goto error; - } - -@@ -413,7 +413,7 @@ qio_channel_socket_accept(QIOChannelSocket *ioc, - qio_channel_set_feature(QIO_CHANNEL(cioc), - QIO_CHANNEL_FEATURE_READ_MSG_PEEK); - -- trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); -+ //trace_qio_channel_socket_accept_complete(ioc, cioc, cioc->fd); - return cioc; - - error: -diff --git a/qcow2/lib/io/channel-tls.c b/qcow2/lib/io/channel-tls.c -index aab630e5..dd347704 100644 ---- a/qcow2/lib/io/channel-tls.c -+++ b/qcow2/lib/io/channel-tls.c -@@ -22,7 +22,7 @@ - #include "qapi/error.h" - #include "qemu/module.h" - #include "io/channel-tls.h" --#include "trace.h" -+//#include "trace.h" - #include "qemu/atomic.h" - - -@@ -96,7 +96,7 @@ qio_channel_tls_new_server(QIOChannel *master, - qio_channel_tls_read_handler, - tioc); - -- trace_qio_channel_tls_new_server(tioc, master, creds, aclname); -+ //trace_qio_channel_tls_new_server(tioc, master, creds, aclname); - return tioc; - - error: -@@ -139,7 +139,7 @@ qio_channel_tls_new_client(QIOChannel *master, - qio_channel_tls_read_handler, - tioc); - -- trace_qio_channel_tls_new_client(tioc, master, creds, hostname); -+ //trace_qio_channel_tls_new_client(tioc, master, creds, hostname); - return tioc; - - error: -@@ -165,7 +165,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, - QCryptoTLSSessionHandshakeStatus status; - - if (qcrypto_tls_session_handshake(ioc->session, &err) < 0) { -- trace_qio_channel_tls_handshake_fail(ioc); -+ //trace_qio_channel_tls_handshake_fail(ioc); - qio_task_set_error(task, err); - qio_task_complete(task); - return; -@@ -173,13 +173,13 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, - - status = qcrypto_tls_session_get_handshake_status(ioc->session); - if (status == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { -- trace_qio_channel_tls_handshake_complete(ioc); -+ //trace_qio_channel_tls_handshake_complete(ioc); - if (qcrypto_tls_session_check_credentials(ioc->session, - &err) < 0) { -- trace_qio_channel_tls_credentials_deny(ioc); -+ //trace_qio_channel_tls_credentials_deny(ioc); - qio_task_set_error(task, err); - } else { -- trace_qio_channel_tls_credentials_allow(ioc); -+ //trace_qio_channel_tls_credentials_allow(ioc); - } - qio_task_complete(task); - } else { -@@ -199,7 +199,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, - condition = G_IO_IN; - } - -- trace_qio_channel_tls_handshake_pending(ioc, status); -+ //trace_qio_channel_tls_handshake_pending(ioc, status); - ioc->hs_ioc_tag = - qio_channel_add_watch_full(ioc->master, - condition, -@@ -243,7 +243,7 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, - task = qio_task_new(OBJECT(ioc), - func, opaque, destroy); - -- trace_qio_channel_tls_handshake_start(ioc); -+ //trace_qio_channel_tls_handshake_start(ioc); - qio_channel_tls_handshake_task(ioc, task, context); - } - -@@ -375,7 +375,7 @@ static int qio_channel_tls_close(QIOChannel *ioc, - QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); - - if (tioc->hs_ioc_tag) { -- trace_qio_channel_tls_handshake_cancel(ioc); -+ //trace_qio_channel_tls_handshake_cancel(ioc); - g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); - } - -diff --git a/qcow2/lib/io/task.c b/qcow2/lib/io/task.c -index 451f26f8..f48832e9 100644 ---- a/qcow2/lib/io/task.c -+++ b/qcow2/lib/io/task.c -@@ -23,7 +23,7 @@ - #include "qapi/error.h" - #include "qemu/thread.h" - #include "qom/object.h" --#include "trace.h" -+//#include "trace.h" - - struct QIOTaskThreadData { - QIOTaskWorker worker; -@@ -65,7 +65,7 @@ QIOTask *qio_task_new(Object *source, - qemu_mutex_init(&task->thread_lock); - qemu_cond_init(&task->thread_cond); - -- trace_qio_task_new(task, source, func, opaque); -+ //trace_qio_task_new(task, source, func, opaque); - - return task; - } -@@ -108,7 +108,7 @@ static gboolean qio_task_thread_result(gpointer opaque) - { - QIOTask *task = opaque; - -- trace_qio_task_thread_result(task); -+ //trace_qio_task_thread_result(task); - qio_task_complete(task); - - return FALSE; -@@ -119,7 +119,7 @@ static gpointer qio_task_thread_worker(gpointer opaque) - { - QIOTask *task = opaque; - -- trace_qio_task_thread_run(task); -+ //trace_qio_task_thread_run(task); - - task->thread->worker(task, task->thread->opaque); - -@@ -128,7 +128,7 @@ static gpointer qio_task_thread_worker(gpointer opaque) - * thread. So we schedule an idle callback to report - * the worker results - */ -- trace_qio_task_thread_exit(task); -+ //trace_qio_task_thread_exit(task); - - qemu_mutex_lock(&task->thread_lock); - -@@ -138,7 +138,7 @@ static gpointer qio_task_thread_worker(gpointer opaque) - g_source_attach(task->thread->completion, - task->thread->context); - g_source_unref(task->thread->completion); -- trace_qio_task_thread_source_attach(task, task->thread->completion); -+ //trace_qio_task_thread_source_attach(task, task->thread->completion); - - qemu_cond_signal(&task->thread_cond); - qemu_mutex_unlock(&task->thread_lock); -@@ -167,7 +167,7 @@ void qio_task_run_in_thread(QIOTask *task, - - task->thread = data; - -- trace_qio_task_thread_start(task, worker, opaque); -+ //trace_qio_task_thread_start(task, worker, opaque); - qemu_thread_create(&thread, - "io-task-worker", - qio_task_thread_worker, -@@ -184,7 +184,7 @@ void qio_task_wait_thread(QIOTask *task) - qemu_cond_wait(&task->thread_cond, &task->thread_lock); - } - -- trace_qio_task_thread_source_cancel(task, task->thread->completion); -+ //trace_qio_task_thread_source_cancel(task, task->thread->completion); - g_source_destroy(task->thread->completion); - qemu_mutex_unlock(&task->thread_lock); - -@@ -195,7 +195,7 @@ void qio_task_wait_thread(QIOTask *task) - void qio_task_complete(QIOTask *task) - { - task->func(task, task->opaque); -- trace_qio_task_complete(task); -+ //trace_qio_task_complete(task); - qio_task_free(task); - } - -diff --git a/qcow2/lib/job-qmp.c b/qcow2/lib/job-qmp.c -index 9e26fa89..3b9cb0aa 100644 ---- a/qcow2/lib/job-qmp.c -+++ b/qcow2/lib/job-qmp.c -@@ -27,7 +27,7 @@ - #include "qemu/job.h" - #include "qapi/qapi-commands-job.h" - #include "qapi/error.h" --#include "trace/trace-root.h" -+//#include "trace/trace-root.h" - - /* - * Get a job using its ID. Called with job_mutex held. -@@ -56,7 +56,7 @@ void qmp_job_cancel(const char *id, Error **errp) - return; - } - -- trace_qmp_job_cancel(job); -+ //trace_qmp_job_cancel(job); - job_user_cancel_locked(job, true, errp); - } - -@@ -71,7 +71,7 @@ void qmp_job_pause(const char *id, Error **errp) - return; - } - -- trace_qmp_job_pause(job); -+ //trace_qmp_job_pause(job); - job_user_pause_locked(job, errp); - } - -@@ -86,7 +86,7 @@ void qmp_job_resume(const char *id, Error **errp) - return; - } - -- trace_qmp_job_resume(job); -+ //trace_qmp_job_resume(job); - job_user_resume_locked(job, errp); - } - -@@ -101,7 +101,7 @@ void qmp_job_complete(const char *id, Error **errp) - return; - } - -- trace_qmp_job_complete(job); -+ //trace_qmp_job_complete(job); - job_complete_locked(job, errp); - } - -@@ -116,7 +116,7 @@ void qmp_job_finalize(const char *id, Error **errp) - return; - } - -- trace_qmp_job_finalize(job); -+ //trace_qmp_job_finalize(job); - job_ref_locked(job); - job_finalize_locked(job, errp); - -@@ -134,7 +134,7 @@ void qmp_job_dismiss(const char *id, Error **errp) - return; - } - -- trace_qmp_job_dismiss(job); -+ //trace_qmp_job_dismiss(job); - job_dismiss_locked(&job, errp); - } - -diff --git a/qcow2/lib/job.c b/qcow2/lib/job.c -index 660ce22c..63223a10 100644 ---- a/qcow2/lib/job.c -+++ b/qcow2/lib/job.c -@@ -29,7 +29,7 @@ - #include "qemu/id.h" - #include "qemu/main-loop.h" - #include "block/aio-wait.h" --#include "trace/trace-root.h" -+//#include "trace/trace-root.h" - #include "qapi/qapi-events-job.h" - - /* -@@ -209,9 +209,9 @@ static void job_state_transition_locked(Job *job, JobStatus s1) - { - JobStatus s0 = job->status; - assert(s1 >= 0 && s1 < JOB_STATUS__MAX); -- trace_job_state_transition(job, job->ret, -- JobSTT[s0][s1] ? "allowed" : "disallowed", -- JobStatus_str(s0), JobStatus_str(s1)); -+ //trace_job_state_transition(job, job->ret, -+ // JobSTT[s0][s1] ? "allowed" : "disallowed", -+ // JobStatus_str(s0), JobStatus_str(s1)); - assert(JobSTT[s0][s1]); - job->status = s1; - -@@ -224,8 +224,8 @@ int job_apply_verb_locked(Job *job, JobVerb verb, Error **errp) - { - JobStatus s0 = job->status; - assert(verb >= 0 && verb < JOB_VERB__MAX); -- trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), -- JobVerbTable[verb][s0] ? "allowed" : "prohibited"); -+ //trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), -+ // JobVerbTable[verb][s0] ? "allowed" : "prohibited"); - if (JobVerbTable[verb][s0]) { - return 0; - } -@@ -1060,7 +1060,7 @@ static void job_completed_locked(Job *job) - assert(job && job->txn && !job_is_completed_locked(job)); - - job_update_rc_locked(job); -- trace_job_completed(job, job->ret); -+ //trace_job_completed(job, job->ret); - if (job->ret) { - job_completed_txn_abort_locked(job); - } else { -diff --git a/qcow2/lib/nbd/client-connection.c b/qcow2/lib/nbd/client-connection.c -index f9da67c8..c834ae9c 100644 ---- a/qcow2/lib/nbd/client-connection.c -+++ b/qcow2/lib/nbd/client-connection.c -@@ -23,7 +23,7 @@ - */ - - #include "qemu/osdep.h" --#include "trace.h" -+//#include "trace.h" - - #include "block/nbd.h" - -@@ -211,7 +211,7 @@ static void *connect_thread_func(void *opaque) - object_unref(OBJECT(conn->sioc)); - conn->sioc = NULL; - if (conn->do_retry && !conn->detached) { -- trace_nbd_connect_thread_sleep(timeout); -+ //trace_nbd_connect_thread_sleep(timeout); - qemu_mutex_unlock(&conn->mutex); - - sleep(timeout); -diff --git a/qcow2/lib/nbd/client.c b/qcow2/lib/nbd/client.c -index c89c7504..3e5c5828 100644 ---- a/qcow2/lib/nbd/client.c -+++ b/qcow2/lib/nbd/client.c -@@ -20,7 +20,7 @@ - #include "qemu/osdep.h" - #include "qapi/error.h" - #include "qemu/queue.h" --#include "trace.h" -+//#include "trace.h" - #include "nbd-internal.h" - #include "qemu/cutils.h" - -@@ -75,7 +75,7 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, - if (len == -1) { - req.length = len = strlen(data); - } -- trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); -+ //trace_nbd_send_option_request(opt, nbd_opt_lookup(opt), len); - - stq_be_p(&req.magic, NBD_OPTS_MAGIC); - stl_be_p(&req.option, opt); -@@ -124,9 +124,11 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, - reply->type = be32_to_cpu(reply->type); - reply->length = be32_to_cpu(reply->length); - -+#if 0 - trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), - reply->type, nbd_rep_lookup(reply->type), - reply->length); -+#endif - - if (reply->magic != NBD_REP_MAGIC) { - error_setg(errp, "Unexpected option reply magic"); -@@ -176,14 +178,18 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, - goto err; - } - msg[reply->length] = '\0'; -+#if 0 - trace_nbd_server_error_msg(reply->type, - nbd_reply_type_lookup(reply->type), msg); -+#endif - } - - if (reply->type == NBD_REP_ERR_UNSUP || !strict) { -+#if 0 - trace_nbd_reply_err_ignored(reply->option, - nbd_opt_lookup(reply->option), - reply->type, nbd_rep_lookup(reply->type)); -+#endif - return 0; - } - -@@ -319,7 +325,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, - local_desc[len] = '\0'; - } - -- trace_nbd_receive_list(local_name, local_desc ?: ""); -+ //trace_nbd_receive_list(local_name, local_desc ?: ""); - *name = g_steal_pointer(&local_name); - if (description) { - *description = g_steal_pointer(&local_desc); -@@ -352,7 +358,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, - info->flags = 0; - - assert(opt == NBD_OPT_GO || opt == NBD_OPT_INFO); -- trace_nbd_opt_info_go_start(nbd_opt_lookup(opt), info->name); -+ //trace_nbd_opt_info_go_start(nbd_opt_lookup(opt), info->name); - buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); - stl_be_p(buf, len); - memcpy(buf + 4, info->name, len); -@@ -392,7 +398,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, - error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); - return -1; - } -- trace_nbd_opt_info_go_success(nbd_opt_lookup(opt)); -+ //trace_nbd_opt_info_go_success(nbd_opt_lookup(opt)); - return 1; - } - if (reply.type != NBD_REP_INFO) { -@@ -437,7 +443,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, - nbd_send_opt_abort(ioc); - return -1; - } -- trace_nbd_receive_negotiate_size_flags(info->size, info->flags); -+ //trace_nbd_receive_negotiate_size_flags(info->size, info->flags); - break; - - case NBD_INFO_BLOCK_SIZE: -@@ -483,8 +489,8 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, - nbd_send_opt_abort(ioc); - return -1; - } -- trace_nbd_opt_info_block_size(info->min_block, info->opt_block, -- info->max_block); -+ //trace_nbd_opt_info_block_size(info->min_block, info->opt_block, -+ // info->max_block); - break; - - default: -@@ -492,7 +498,7 @@ static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, - * Not worth the bother to check if NBD_INFO_NAME or - * NBD_INFO_DESCRIPTION exceed NBD_MAX_STRING_SIZE. - */ -- trace_nbd_opt_info_unknown(type, nbd_info_lookup(type)); -+ //trace_nbd_opt_info_unknown(type, nbd_info_lookup(type)); - if (nbd_drop(ioc, len, errp) < 0) { - error_prepend(errp, "Failed to read info payload: "); - nbd_send_opt_abort(ioc); -@@ -511,7 +517,7 @@ static int nbd_receive_query_exports(QIOChannel *ioc, - bool list_empty = true; - bool found_export = false; - -- trace_nbd_receive_query_exports_start(wantname); -+ //trace_nbd_receive_query_exports_start(wantname); - if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) { - return -1; - } -@@ -541,7 +547,7 @@ static int nbd_receive_query_exports(QIOChannel *ioc, - nbd_send_opt_abort(ioc); - return -1; - } -- trace_nbd_receive_query_exports_success(wantname); -+ //trace_nbd_receive_query_exports_success(wantname); - return 0; - } - list_empty = false; -@@ -631,13 +637,13 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, - return NULL; - } - -- trace_nbd_receive_starttls_new_client(); -+ //trace_nbd_receive_starttls_new_client(); - tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp); - if (!tioc) { - return NULL; - } - qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls"); -- trace_nbd_receive_starttls_tls_handshake(); -+ //trace_nbd_receive_starttls_tls_handshake(); - qio_channel_tls_handshake(tioc, - nbd_client_tls_handshake, - &data, -@@ -689,7 +695,7 @@ static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt, - } - p = data = g_malloc(data_len); - -- trace_nbd_opt_meta_request(nbd_opt_lookup(opt), query ?: "(all)", export); -+ //trace_nbd_opt_meta_request(nbd_opt_lookup(opt), query ?: "(all)", export); - stl_be_p(p, export_len); - memcpy(p += sizeof(export_len), export, export_len); - stl_be_p(p += export_len, queries); -@@ -767,7 +773,7 @@ static int nbd_receive_one_meta_context(QIOChannel *ioc, - return -1; - } - local_name[reply.length] = '\0'; -- trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id); -+ //trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id); - - if (name) { - *name = local_name; -@@ -905,7 +911,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, - ERRP_GUARD(); - uint64_t magic; - -- trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); -+ //trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); - - if (zeroes) { - *zeroes = true; -@@ -921,7 +927,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, - if (nbd_read64(ioc, &magic, "initial magic", errp) < 0) { - return -EINVAL; - } -- trace_nbd_receive_negotiate_magic(magic); -+ //trace_nbd_receive_negotiate_magic(magic); - - if (magic != NBD_INIT_MAGIC) { - error_setg(errp, "Bad initial magic received: 0x%" PRIx64, magic); -@@ -931,7 +937,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, - if (nbd_read64(ioc, &magic, "server magic", errp) < 0) { - return -EINVAL; - } -- trace_nbd_receive_negotiate_magic(magic); -+ //trace_nbd_receive_negotiate_magic(magic); - - if (magic == NBD_OPTS_MAGIC) { - uint32_t clientflags = 0; -@@ -941,7 +947,7 @@ static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, - if (nbd_read16(ioc, &globalflags, "server flags", errp) < 0) { - return -EINVAL; - } -- trace_nbd_receive_negotiate_server_flags(globalflags); -+ //trace_nbd_receive_negotiate_server_flags(globalflags); - if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { - fixedNewStyle = true; - clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; -@@ -1047,7 +1053,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, - bool base_allocation = info->base_allocation; - - assert(info->name && strlen(info->name) <= NBD_MAX_STRING_SIZE); -- trace_nbd_receive_negotiate_name(info->name); -+ //trace_nbd_receive_negotiate_name(info->name); - - result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, - info->mode, &zeroes, errp); -@@ -1124,7 +1130,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, - g_assert_not_reached(); - } - -- trace_nbd_receive_negotiate_size_flags(info->size, info->flags); -+ //trace_nbd_receive_negotiate_size_flags(info->size, info->flags); - if (zeroes && nbd_drop(ioc, 124, errp) < 0) { - error_prepend(errp, "Failed to read reserved block: "); - return -EINVAL; -@@ -1286,7 +1292,7 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, - return -E2BIG; - } - -- trace_nbd_init_set_socket(); -+ //trace_nbd_init_set_socket(); - - if (ioctl(fd, NBD_SET_SOCK, (unsigned long) sioc->fd) < 0) { - int serrno = errno; -@@ -1294,7 +1300,7 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, - return -serrno; - } - -- trace_nbd_init_set_block_size(sector_size); -+ //trace_nbd_init_set_block_size(sector_size); - - if (ioctl(fd, NBD_SET_BLKSIZE, sector_size) < 0) { - int serrno = errno; -@@ -1302,9 +1308,9 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, - return -serrno; - } - -- trace_nbd_init_set_size(sectors); -+ //trace_nbd_init_set_size(sectors); - if (info->size % sector_size) { -- trace_nbd_init_trailing_bytes(info->size % sector_size); -+ //trace_nbd_init_trailing_bytes(info->size % sector_size); - } - - if (ioctl(fd, NBD_SET_SIZE_BLOCKS, sectors) < 0) { -@@ -1316,7 +1322,7 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, - if (ioctl(fd, NBD_SET_FLAGS, (unsigned long) info->flags) < 0) { - if (errno == ENOTTY) { - int read_only = (info->flags & NBD_FLAG_READ_ONLY) != 0; -- trace_nbd_init_set_readonly(); -+ //trace_nbd_init_set_readonly(); - - if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) { - int serrno = errno; -@@ -1330,7 +1336,7 @@ int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, - } - } - -- trace_nbd_init_finish(); -+ //trace_nbd_init_finish(); - - return 0; - } -@@ -1340,7 +1346,7 @@ int nbd_client(int fd) - int ret; - int serrno; - -- trace_nbd_client_loop(); -+ //trace_nbd_client_loop(); - - ret = ioctl(fd, NBD_DO_IT); - if (ret < 0 && errno == EPIPE) { -@@ -1352,12 +1358,12 @@ int nbd_client(int fd) - } - serrno = errno; - -- trace_nbd_client_loop_ret(ret, strerror(serrno)); -+ //trace_nbd_client_loop_ret(ret, strerror(serrno)); - -- trace_nbd_client_clear_queue(); -+ //trace_nbd_client_clear_queue(); - ioctl(fd, NBD_CLEAR_QUE); - -- trace_nbd_client_clear_socket(); -+ //trace_nbd_client_clear_socket(); - ioctl(fd, NBD_CLEAR_SOCK); - - errno = serrno; -@@ -1379,9 +1385,11 @@ int nbd_send_request(QIOChannel *ioc, NBDRequest *request) - uint8_t buf[NBD_EXTENDED_REQUEST_SIZE]; - size_t len; - -+#if 0 - trace_nbd_send_request(request->from, request->len, request->cookie, - request->flags, request->type, - nbd_cmd_lookup(request->type)); -+#endif - - stw_be_p(buf + 4, request->flags); - stw_be_p(buf + 6, request->type); -@@ -1535,7 +1543,7 @@ int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, - NBDReply *reply, NBDMode mode, Error **errp) - { - int ret; -- const char *type; -+ //const char *type; - uint32_t expected; - - ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp); -@@ -1549,37 +1557,46 @@ int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, - switch (reply->magic) { - case NBD_SIMPLE_REPLY_MAGIC: - if (mode >= NBD_MODE_EXTENDED) { -+#if 0 - trace_nbd_receive_wrong_header(reply->magic, - nbd_mode_lookup(mode)); -+#endif - } - ret = nbd_receive_simple_reply(ioc, &reply->simple, errp); - if (ret < 0) { - return ret; - } -+#if 0 - trace_nbd_receive_simple_reply(reply->simple.error, - nbd_err_lookup(reply->simple.error), - reply->cookie); -+#endif - break; - case NBD_STRUCTURED_REPLY_MAGIC: - case NBD_EXTENDED_REPLY_MAGIC: - expected = mode >= NBD_MODE_EXTENDED ? NBD_EXTENDED_REPLY_MAGIC - : NBD_STRUCTURED_REPLY_MAGIC; - if (reply->magic != expected) { -+#if 0 - trace_nbd_receive_wrong_header(reply->magic, - nbd_mode_lookup(mode)); -+#endif - } - ret = nbd_receive_reply_chunk_header(ioc, reply, errp); - if (ret < 0) { - return ret; - } -+ nbd_reply_type_lookup(reply->structured.type); -+#if 0 - type = nbd_reply_type_lookup(reply->structured.type); - trace_nbd_receive_reply_chunk_header(reply->structured.flags, - reply->structured.type, type, - reply->structured.cookie, - reply->structured.length); -+#endif - break; - default: -- trace_nbd_receive_wrong_header(reply->magic, nbd_mode_lookup(mode)); -+ //trace_nbd_receive_wrong_header(reply->magic, nbd_mode_lookup(mode)); - error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", reply->magic); - return -EINVAL; - } -diff --git a/qcow2/lib/nbd/common.c b/qcow2/lib/nbd/common.c -index 589a748c..891b7f6b 100644 ---- a/qcow2/lib/nbd/common.c -+++ b/qcow2/lib/nbd/common.c -@@ -17,7 +17,7 @@ - */ - - #include "qemu/osdep.h" --#include "trace.h" -+//#include "trace.h" - #include "nbd-internal.h" - - /* Discard length bytes from channel. Return -errno on failure and 0 on -@@ -237,7 +237,7 @@ int nbd_errno_to_system_errno(int err) - ret = ESHUTDOWN; - break; - default: -- trace_nbd_unknown_error(err); -+ //trace_nbd_unknown_error(err); - /* fallthrough */ - case NBD_EINVAL: - ret = EINVAL; diff --git a/qcow2/lib/qapi/qapi-events-block-core.c b/qcow2/lib/qapi/qapi-events-block-core.c index a08a710c..2c8a538c 100644 --- a/qcow2/lib/qapi/qapi-events-block-core.c @@ -5995,283 +4195,6 @@ index cacb730a..96e8d05f 100644 bool visit_type_IoOperationType(Visitor *v, const char *name, IoOperationType *obj, Error **errp) -diff --git a/qcow2/lib/qapi/qapi-visit-core.c b/qcow2/lib/qapi/qapi-visit-core.c -index 6c13510a..895f89fd 100644 ---- a/qcow2/lib/qapi/qapi-visit-core.c -+++ b/qcow2/lib/qapi/qapi-visit-core.c -@@ -18,7 +18,7 @@ - #include "qapi/qmp/qerror.h" - #include "qapi/visitor.h" - #include "qapi/visitor-impl.h" --#include "trace.h" -+//#include "trace.h" - - /* Zero-initialization must result in default policy */ - QEMU_BUILD_BUG_ON(COMPAT_POLICY_INPUT_ACCEPT || COMPAT_POLICY_OUTPUT_ACCEPT); -@@ -27,7 +27,7 @@ QEMU_BUILD_BUG_ON(COMPAT_POLICY_INPUT_ACCEPT || COMPAT_POLICY_OUTPUT_ACCEPT); - void visit_complete(Visitor *v, void *opaque) - { - assert(v->type != VISITOR_OUTPUT || v->complete); -- trace_visit_complete(v, opaque); -+ //trace_visit_complete(v, opaque); - if (v->complete) { - v->complete(v, opaque); - } -@@ -35,7 +35,7 @@ void visit_complete(Visitor *v, void *opaque) - - void visit_free(Visitor *v) - { -- trace_visit_free(v); -+ //trace_visit_free(v); - if (v) { - v->free(v); - } -@@ -46,7 +46,7 @@ bool visit_start_struct(Visitor *v, const char *name, void **obj, - { - bool ok; - -- trace_visit_start_struct(v, name, obj, size); -+ //trace_visit_start_struct(v, name, obj, size); - if (obj) { - assert(size); - assert(!(v->type & VISITOR_OUTPUT) || *obj); -@@ -60,13 +60,13 @@ bool visit_start_struct(Visitor *v, const char *name, void **obj, - - bool visit_check_struct(Visitor *v, Error **errp) - { -- trace_visit_check_struct(v); -+ //trace_visit_check_struct(v); - return v->check_struct ? v->check_struct(v, errp) : true; - } - - void visit_end_struct(Visitor *v, void **obj) - { -- trace_visit_end_struct(v, obj); -+ //trace_visit_end_struct(v, obj); - v->end_struct(v, obj); - } - -@@ -76,7 +76,7 @@ bool visit_start_list(Visitor *v, const char *name, GenericList **list, - bool ok; - - assert(!list || size >= sizeof(GenericList)); -- trace_visit_start_list(v, name, list, size); -+ //trace_visit_start_list(v, name, list, size); - ok = v->start_list(v, name, list, size, errp); - if (list && (v->type & VISITOR_INPUT)) { - assert(ok || !*list); -@@ -87,19 +87,19 @@ bool visit_start_list(Visitor *v, const char *name, GenericList **list, - GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size) - { - assert(tail && size >= sizeof(GenericList)); -- trace_visit_next_list(v, tail, size); -+ //trace_visit_next_list(v, tail, size); - return v->next_list(v, tail, size); - } - - bool visit_check_list(Visitor *v, Error **errp) - { -- trace_visit_check_list(v); -+ //trace_visit_check_list(v); - return v->check_list ? v->check_list(v, errp) : true; - } - - void visit_end_list(Visitor *v, void **obj) - { -- trace_visit_end_list(v, obj); -+ //trace_visit_end_list(v, obj); - v->end_list(v, obj); - } - -@@ -111,7 +111,7 @@ bool visit_start_alternate(Visitor *v, const char *name, - - assert(obj && size >= sizeof(GenericAlternate)); - assert(!(v->type & VISITOR_OUTPUT) || *obj); -- trace_visit_start_alternate(v, name, obj, size); -+ //trace_visit_start_alternate(v, name, obj, size); - if (!v->start_alternate) { - assert(!(v->type & VISITOR_INPUT)); - return true; -@@ -125,7 +125,7 @@ bool visit_start_alternate(Visitor *v, const char *name, - - void visit_end_alternate(Visitor *v, void **obj) - { -- trace_visit_end_alternate(v, obj); -+ //trace_visit_end_alternate(v, obj); - if (v->end_alternate) { - v->end_alternate(v, obj); - } -@@ -133,7 +133,7 @@ void visit_end_alternate(Visitor *v, void **obj) - - bool visit_optional(Visitor *v, const char *name, bool *present) - { -- trace_visit_optional(v, name, present); -+ //trace_visit_optional(v, name, present); - if (v->optional) { - v->optional(v, name, present); - } -@@ -143,7 +143,7 @@ bool visit_optional(Visitor *v, const char *name, bool *present) - bool visit_policy_reject(Visitor *v, const char *name, - unsigned special_features, Error **errp) - { -- trace_visit_policy_reject(v, name); -+ //trace_visit_policy_reject(v, name); - if (v->policy_reject) { - return v->policy_reject(v, name, special_features, errp); - } -@@ -153,7 +153,7 @@ bool visit_policy_reject(Visitor *v, const char *name, - bool visit_policy_skip(Visitor *v, const char *name, - unsigned special_features) - { -- trace_visit_policy_skip(v, name); -+ //trace_visit_policy_skip(v, name); - if (v->policy_skip) { - return v->policy_skip(v, name, special_features); - } -@@ -178,7 +178,7 @@ bool visit_is_dealloc(Visitor *v) - bool visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp) - { - assert(obj); -- trace_visit_type_int(v, name, obj); -+ //trace_visit_type_int(v, name, obj); - return v->type_int64(v, name, obj, errp); - } - -@@ -208,7 +208,7 @@ bool visit_type_uint8(Visitor *v, const char *name, uint8_t *obj, - uint64_t value; - bool ok; - -- trace_visit_type_uint8(v, name, obj); -+ //trace_visit_type_uint8(v, name, obj); - value = *obj; - ok = visit_type_uintN(v, &value, name, UINT8_MAX, "uint8_t", errp); - *obj = value; -@@ -221,7 +221,7 @@ bool visit_type_uint16(Visitor *v, const char *name, uint16_t *obj, - uint64_t value; - bool ok; - -- trace_visit_type_uint16(v, name, obj); -+ //trace_visit_type_uint16(v, name, obj); - value = *obj; - ok = visit_type_uintN(v, &value, name, UINT16_MAX, "uint16_t", errp); - *obj = value; -@@ -234,7 +234,7 @@ bool visit_type_uint32(Visitor *v, const char *name, uint32_t *obj, - uint64_t value; - bool ok; - -- trace_visit_type_uint32(v, name, obj); -+ //trace_visit_type_uint32(v, name, obj); - value = *obj; - ok = visit_type_uintN(v, &value, name, UINT32_MAX, "uint32_t", errp); - *obj = value; -@@ -245,7 +245,7 @@ bool visit_type_uint64(Visitor *v, const char *name, uint64_t *obj, - Error **errp) - { - assert(obj); -- trace_visit_type_uint64(v, name, obj); -+ //trace_visit_type_uint64(v, name, obj); - return v->type_uint64(v, name, obj, errp); - } - -@@ -275,7 +275,7 @@ bool visit_type_int8(Visitor *v, const char *name, int8_t *obj, Error **errp) - int64_t value; - bool ok; - -- trace_visit_type_int8(v, name, obj); -+ //trace_visit_type_int8(v, name, obj); - value = *obj; - ok = visit_type_intN(v, &value, name, INT8_MIN, INT8_MAX, "int8_t", errp); - *obj = value; -@@ -288,7 +288,7 @@ bool visit_type_int16(Visitor *v, const char *name, int16_t *obj, - int64_t value; - bool ok; - -- trace_visit_type_int16(v, name, obj); -+ //trace_visit_type_int16(v, name, obj); - value = *obj; - ok = visit_type_intN(v, &value, name, INT16_MIN, INT16_MAX, "int16_t", - errp); -@@ -302,7 +302,7 @@ bool visit_type_int32(Visitor *v, const char *name, int32_t *obj, - int64_t value; - bool ok; - -- trace_visit_type_int32(v, name, obj); -+ //trace_visit_type_int32(v, name, obj); - value = *obj; - ok = visit_type_intN(v, &value, name, INT32_MIN, INT32_MAX, "int32_t", - errp); -@@ -314,7 +314,7 @@ bool visit_type_int64(Visitor *v, const char *name, int64_t *obj, - Error **errp) - { - assert(obj); -- trace_visit_type_int64(v, name, obj); -+ //trace_visit_type_int64(v, name, obj); - return v->type_int64(v, name, obj, errp); - } - -@@ -322,7 +322,7 @@ bool visit_type_size(Visitor *v, const char *name, uint64_t *obj, - Error **errp) - { - assert(obj); -- trace_visit_type_size(v, name, obj); -+ //trace_visit_type_size(v, name, obj); - if (v->type_size) { - return v->type_size(v, name, obj, errp); - } -@@ -332,7 +332,7 @@ bool visit_type_size(Visitor *v, const char *name, uint64_t *obj, - bool visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) - { - assert(obj); -- trace_visit_type_bool(v, name, obj); -+ //trace_visit_type_bool(v, name, obj); - return v->type_bool(v, name, obj, errp); - } - -@@ -345,7 +345,7 @@ bool visit_type_str(Visitor *v, const char *name, char **obj, Error **errp) - * can enable: - assert(!(v->type & VISITOR_OUTPUT) || *obj); - */ -- trace_visit_type_str(v, name, obj); -+ //trace_visit_type_str(v, name, obj); - ok = v->type_str(v, name, obj, errp); - if (v->type & VISITOR_INPUT) { - assert(ok != !*obj); -@@ -357,7 +357,7 @@ bool visit_type_number(Visitor *v, const char *name, double *obj, - Error **errp) - { - assert(obj); -- trace_visit_type_number(v, name, obj); -+ //trace_visit_type_number(v, name, obj); - return v->type_number(v, name, obj, errp); - } - -@@ -367,7 +367,7 @@ bool visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) - - assert(obj); - assert(v->type != VISITOR_OUTPUT || *obj); -- trace_visit_type_any(v, name, obj); -+ //trace_visit_type_any(v, name, obj); - ok = v->type_any(v, name, obj, errp); - if (v->type == VISITOR_INPUT) { - assert(ok != !*obj); -@@ -378,7 +378,7 @@ bool visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) - bool visit_type_null(Visitor *v, const char *name, QNull **obj, - Error **errp) - { -- trace_visit_type_null(v, name, obj); -+ //trace_visit_type_null(v, name, obj); - return v->type_null(v, name, obj, errp); - } - -@@ -425,7 +425,7 @@ bool visit_type_enum(Visitor *v, const char *name, int *obj, - const QEnumLookup *lookup, Error **errp) - { - assert(obj && lookup); -- trace_visit_type_enum(v, name, obj); -+ //trace_visit_type_enum(v, name, obj); - switch (v->type) { - case VISITOR_INPUT: - return input_type_enum(v, name, obj, lookup, errp); diff --git a/qcow2/lib/qapi/qapi-visit-crypto.c b/qcow2/lib/qapi/qapi-visit-crypto.c index 09eab7e3..b616ccbc 100644 --- a/qcow2/lib/qapi/qapi-visit-crypto.c @@ -6364,153 +4287,26 @@ index f110a804..408ade81 100644 Error **errp) @@ -774,3 +775,4 @@ Visitor *qobject_input_visitor_new_str(const char *str, - return v; - } -+#endif -diff --git a/qcow2/lib/qcow2-cache.c b/qcow2/lib/qcow2-cache.c -index 23d9588b..11453579 100644 ---- a/qcow2/lib/qcow2-cache.c -+++ b/qcow2/lib/qcow2-cache.c -@@ -26,7 +26,7 @@ - #include "block/block-io.h" - #include "qemu/memalign.h" - #include "qcow2.h" --#include "trace.h" -+//#include "trace.h" - - typedef struct Qcow2CachedTable { - int64_t offset; -@@ -189,8 +189,8 @@ qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) - return 0; - } - -- trace_qcow2_cache_entry_flush(qemu_coroutine_self(), -- c == s->l2_table_cache, i); -+ //trace_qcow2_cache_entry_flush(qemu_coroutine_self(), -+ // c == s->l2_table_cache, i); - - if (c->depends) { - ret = qcow2_cache_flush_dependency(bs, c); -@@ -239,12 +239,12 @@ qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) - - int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c) - { -- BDRVQcow2State *s = bs->opaque; -+ //BDRVQcow2State *s = bs->opaque; - int result = 0; - int ret; - int i; - -- trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); -+ //trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache); - - for (i = 0; i < c->size; i++) { - ret = qcow2_cache_entry_flush(bs, c, i); -@@ -333,8 +333,8 @@ qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, - - assert(offset != 0); - -- trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, -- offset, read_from_disk); -+ //trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, -+ // offset, read_from_disk); - - if (!QEMU_IS_ALIGNED(offset, c->table_size)) { - qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s " -@@ -367,16 +367,16 @@ qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, - - /* Cache miss: write a table back and replace it */ - i = min_lru_index; -- trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), -- c == s->l2_table_cache, i); -+ //trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(), -+ // c == s->l2_table_cache, i); - - ret = qcow2_cache_entry_flush(bs, c, i); - if (ret < 0) { - return ret; - } - -- trace_qcow2_cache_get_read(qemu_coroutine_self(), -- c == s->l2_table_cache, i); -+ //trace_qcow2_cache_get_read(qemu_coroutine_self(), -+ // c == s->l2_table_cache, i); - c->entries[i].offset = 0; - if (read_from_disk) { - if (c == s->l2_table_cache) { -@@ -397,8 +397,8 @@ found: - c->entries[i].ref++; - *table = qcow2_cache_get_table_addr(c, i); - -- trace_qcow2_cache_get_done(qemu_coroutine_self(), -- c == s->l2_table_cache, i); -+ //trace_qcow2_cache_get_done(qemu_coroutine_self(), -+ // c == s->l2_table_cache, i); - - return 0; + return v; } ++#endif +diff --git a/qcow2/lib/qcow2-cache.c b/qcow2/lib/qcow2-cache.c +index 23d9588b..aa269daf 100644 +--- a/qcow2/lib/qcow2-cache.c ++++ b/qcow2/lib/qcow2-cache.c +@@ -239,7 +239,7 @@ qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) + + int qcow2_cache_write(BlockDriverState *bs, Qcow2Cache *c) + { +- BDRVQcow2State *s = bs->opaque; ++ //BDRVQcow2State *s = bs->opaque; + int result = 0; + int ret; + int i; diff --git a/qcow2/lib/qcow2-cluster.c b/qcow2/lib/qcow2-cluster.c -index ce8c0076..f71ff791 100644 +index ce8c0076..ad484e0c 100644 --- a/qcow2/lib/qcow2-cluster.c +++ b/qcow2/lib/qcow2-cluster.c -@@ -30,7 +30,7 @@ - #include "qcow2.h" - #include "qemu/bswap.h" - #include "qemu/memalign.h" --#include "trace.h" -+//#include "trace.h" - - int coroutine_fn qcow2_shrink_l1_table(BlockDriverState *bs, - uint64_t exact_size) -@@ -281,7 +281,7 @@ static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) - - old_l2_offset = s->l1_table[l1_index]; - -- trace_qcow2_l2_allocate(bs, l1_index); -+ //trace_qcow2_l2_allocate(bs, l1_index); - - /* allocate a new l2 entry */ - -@@ -312,7 +312,7 @@ static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) - slice_size2 = s->l2_slice_size * l2_entry_size(s); - n_slices = s->cluster_size / slice_size2; - -- trace_qcow2_l2_allocate_get_empty(bs, l1_index); -+ //trace_qcow2_l2_allocate_get_empty(bs, l1_index); - for (slice = 0; slice < n_slices; slice++) { - ret = qcow2_cache_get_empty(bs, s->l2_table_cache, - l2_offset + slice * slice_size2, -@@ -345,7 +345,7 @@ static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) - /* write the l2 slice to the file */ - BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); - -- trace_qcow2_l2_allocate_write_l2(bs, l1_index); -+ //trace_qcow2_l2_allocate_write_l2(bs, l1_index); - qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); - qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - } -@@ -356,18 +356,18 @@ static int GRAPH_RDLOCK l2_allocate(BlockDriverState *bs, int l1_index) - } - - /* update the L1 entry */ -- trace_qcow2_l2_allocate_write_l1(bs, l1_index); -+ //trace_qcow2_l2_allocate_write_l1(bs, l1_index); - s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; - ret = qcow2_write_l1_entry(bs, l1_index); - if (ret < 0) { - goto fail; - } - -- trace_qcow2_l2_allocate_done(bs, l1_index, 0); -+ //trace_qcow2_l2_allocate_done(bs, l1_index, 0); - return 0; - - fail: -- trace_qcow2_l2_allocate_done(bs, l1_index, ret); -+ //trace_qcow2_l2_allocate_done(bs, l1_index, ret); - if (l2_slice != NULL) { - qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - } @@ -959,6 +959,7 @@ perform_cow(BlockDriverState *bs, QCowL2Meta *m) goto fail; } @@ -6527,86 +4323,17 @@ index ce8c0076..f71ff791 100644 /* And now we can write everything. If we have the guest data we * can write everything in one single operation */ -@@ -1033,7 +1035,7 @@ int coroutine_fn qcow2_alloc_cluster_link_l2(BlockDriverState *bs, - uint64_t *old_cluster, *l2_slice; - uint64_t cluster_offset = m->alloc_offset; - -- trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); -+ //trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); - assert(m->nb_clusters > 0); - - old_cluster = g_try_new(uint64_t, m->nb_clusters); -@@ -1503,8 +1505,8 @@ handle_copied(BlockDriverState *bs, uint64_t guest_offset, - unsigned int keep_clusters; - int ret; - -- trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, -- *bytes); -+ //trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, -+ // *bytes); - - assert(*host_offset == INV_OFFSET || offset_into_cluster(s, guest_offset) - == offset_into_cluster(s, *host_offset)); -@@ -1607,8 +1609,8 @@ do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, - { - BDRVQcow2State *s = bs->opaque; - -- trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, -- *host_offset, *nb_clusters); -+ //trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, -+ // *host_offset, *nb_clusters); - - if (has_data_file(bs)) { - assert(*host_offset == INV_OFFSET || -@@ -1618,7 +1620,7 @@ do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, - } - - /* Allocate new clusters */ -- trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); -+ //trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); - if (*host_offset == INV_OFFSET) { - int64_t cluster_offset = - qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size); -@@ -1670,8 +1672,8 @@ handle_alloc(BlockDriverState *bs, uint64_t guest_offset, - - uint64_t alloc_cluster_offset; - -- trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, -- *bytes); -+ //trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, -+ // *bytes); - assert(*bytes > 0); - - /* -@@ -1791,7 +1793,7 @@ int coroutine_fn qcow2_alloc_host_offset(BlockDriverState *bs, uint64_t offset, - uint64_t cur_bytes; - int ret; - -- trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *bytes); -+ //trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *bytes); - - again: - start = offset; diff --git a/qcow2/lib/qcow2-refcount.c b/qcow2/lib/qcow2-refcount.c -index 0266542c..9442522a 100644 +index 0266542c..f1895747 100644 --- a/qcow2/lib/qcow2-refcount.c +++ b/qcow2/lib/qcow2-refcount.c -@@ -30,7 +30,7 @@ - #include "qemu/bswap.h" - #include "qemu/cutils.h" - #include "qemu/memalign.h" --#include "trace.h" -+//#include "trace.h" - - static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, - uint64_t max); @@ -745,8 +745,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) if (ret >= 0) { int r2 = bdrv_pdiscard(bs->file, d->offset, d->bytes); if (r2 < 0) { - trace_qcow2_process_discards_failed_region(d->offset, d->bytes, - r2); -+ //trace_qcow2_process_discards_failed_region(d->offset, d->bytes, r2); ++ trace_qcow2_process_discards_failed_region(d->offset, d->bytes, r2); } } @@ -6653,10 +4380,10 @@ index d6071a1e..f6de6e60 100644 } +#endif diff --git a/qcow2/lib/qcow2.c b/qcow2/lib/qcow2.c -index 70b19730..801042e0 100644 +index 70b19730..47c6032b 100644 --- a/qcow2/lib/qcow2.c +++ b/qcow2/lib/qcow2.c -@@ -31,17 +31,17 @@ +@@ -31,7 +31,7 @@ #include "qcow2.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -6664,11 +4391,8 @@ index 70b19730..801042e0 100644 +//#include "qapi/qapi-events-block-core.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" --#include "trace.h" -+//#include "trace.h" - #include "qemu/option_int.h" - #include "qemu/cutils.h" - #include "qemu/bswap.h" + #include "trace.h" +@@ -41,7 +41,7 @@ #include "qemu/memalign.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" @@ -6870,21 +4594,6 @@ index 70b19730..801042e0 100644 typedef struct Qcow2AioTask { AioTask task; -@@ -2312,10 +2330,10 @@ static coroutine_fn int qcow2_add_task(BlockDriverState *bs, - .l2meta = l2meta, - }; - -- trace_qcow2_add_task(qemu_coroutine_self(), bs, pool, -- func == qcow2_co_preadv_task_entry ? "read" : "write", -- subcluster_type, host_offset, offset, bytes, -- qiov, qiov_offset); -+ //trace_qcow2_add_task(qemu_coroutine_self(), bs, pool, -+ // func == qcow2_co_preadv_task_entry ? "read" : "write", -+ // subcluster_type, host_offset, offset, bytes, -+ // qiov, qiov_offset); - - if (!pool) { - return func(&task->task); @@ -2353,8 +2371,10 @@ qcow2_co_preadv_task(BlockDriverState *bs, QCow2SubclusterType subc_type, case QCOW2_SUBCLUSTER_NORMAL: @@ -6922,15 +4631,6 @@ index 70b19730..801042e0 100644 for (m = l2meta; m != NULL; m = m->next) { int ret; -@@ -2577,7 +2601,7 @@ handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta) - continue; - } - -- trace_qcow2_skip_cow(qemu_coroutine_self(), m->offset, m->nb_clusters); -+ //trace_qcow2_skip_cow(qemu_coroutine_self(), m->offset, m->nb_clusters); - m->skip_cow = true; - } - return 0; @@ -2596,9 +2620,10 @@ int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, { int ret; @@ -6952,15 +4652,6 @@ index 70b19730..801042e0 100644 /* Try to efficiently initialize the physical space with zeroes */ ret = handle_alloc_space(bs, l2meta); -@@ -2633,7 +2659,7 @@ int qcow2_co_pwritev_task(BlockDriverState *bs, uint64_t host_offset, - */ - if (!merge_cow(offset, bytes, qiov, qiov_offset, l2meta)) { - BLKDBG_CO_EVENT(bs->file, BLKDBG_WRITE_AIO); -- trace_qcow2_writev_data(qemu_coroutine_self(), host_offset); -+ //trace_qcow2_writev_data(qemu_coroutine_self(), host_offset); - ret = bdrv_co_pwritev_part(s->data_file, host_offset, - bytes, qiov, qiov_offset, 0); - if (ret < 0) { @@ -2653,7 +2679,7 @@ out_locked: qcow2_handle_l2meta(bs, &l2meta, false); qemu_co_mutex_unlock(&s->lock); @@ -6968,40 +4659,6 @@ index 70b19730..801042e0 100644 - qemu_vfree(crypt_buf); + //qemu_vfree(crypt_buf); - return ret; - } -@@ -2686,13 +2712,13 @@ qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, - QCowL2Meta *l2meta = NULL; - AioTaskPool *aio = NULL; - -- trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes); -+ //trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes); - - while (bytes != 0 && aio_task_pool_status(aio) == 0) { - - l2meta = NULL; - -- trace_qcow2_writev_start_part(qemu_coroutine_self()); -+ //trace_qcow2_writev_start_part(qemu_coroutine_self()); - offset_in_cluster = offset_into_cluster(s, offset); - cur_bytes = MIN(bytes, INT_MAX); - if (bs->encrypted) { -@@ -2731,7 +2757,7 @@ qcow2_co_pwritev_part(BlockDriverState *bs, int64_t offset, int64_t bytes, - bytes -= cur_bytes; - offset += cur_bytes; - qiov_offset += cur_bytes; -- trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes); -+ //trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes); - } - ret = 0; - -@@ -2751,7 +2777,7 @@ fail_nometa: - g_free(aio); - } - -- trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); -+ //trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); - return ret; } @@ -2807,9 +2833,9 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file) @@ -7105,33 +4762,6 @@ index 70b19730..801042e0 100644 blk_co_unref(blk); blk = NULL; -@@ -4069,7 +4101,7 @@ qcow2_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, - uint32_t tail = ROUND_UP(offset + bytes, s->subcluster_size) - - (offset + bytes); - -- trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, bytes); -+ //trace_qcow2_pwrite_zeroes_start_req(qemu_coroutine_self(), offset, bytes); - if (offset + bytes == bs->total_sectors * BDRV_SECTOR_SIZE) { - tail = 0; - } -@@ -4105,7 +4137,7 @@ qcow2_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int64_t bytes, - qemu_co_mutex_lock(&s->lock); - } - -- trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, bytes); -+ //trace_qcow2_pwrite_zeroes(qemu_coroutine_self(), offset, bytes); - - /* Whatever is left can use real zero subclusters */ - ret = qcow2_subcluster_zeroize(bs, offset, bytes, flags); -@@ -4289,7 +4321,7 @@ fail: - - qemu_co_mutex_unlock(&s->lock); - -- trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); -+ //trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); - - return ret; - } @@ -5023,7 +5055,7 @@ static int GRAPH_RDLOCK qcow2_make_empty(BlockDriverState *bs) if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps && @@ -7382,10 +5012,10 @@ index 4a83bda2..0d2eba5b 100644 /** * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all diff --git a/qcow2/lib/qom/object.c b/qcow2/lib/qom/object.c -index 157a45c5..728b6927 100644 +index 157a45c5..61b8556a 100644 --- a/qcow2/lib/qom/object.c +++ b/qcow2/lib/qom/object.c -@@ -14,17 +14,17 @@ +@@ -14,14 +14,14 @@ #include "hw/qdev-core.h" #include "qapi/error.h" #include "qom/object.h" @@ -7401,11 +5031,7 @@ index 157a45c5..728b6927 100644 +//#include "qapi/forward-visitor.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qmp/qjson.h" --#include "trace.h" -+//#include "trace.h" - - /* TODO: replace QObject with a simpler visitor to avoid a dependency - * of the QOM core on QObject? */ + #include "trace.h" @@ -262,6 +262,7 @@ static size_t type_object_get_align(TypeImpl *ti) return 0; } @@ -7486,29 +5112,7 @@ index 157a45c5..728b6927 100644 Object *object_dynamic_cast(Object *obj, const char *typename) -@@ -912,8 +922,10 @@ Object *object_dynamic_cast(Object *obj, const char *typename) - Object *object_dynamic_cast_assert(Object *obj, const char *typename, - const char *file, int line, const char *func) - { -+#if 0 - trace_object_dynamic_cast_assert(obj ? obj->class->type->name : "(null)", - typename, file, line, func); -+#endif - - #ifdef CONFIG_QOM_CAST_DEBUG - int i; -@@ -1003,8 +1015,10 @@ ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class, - { - ObjectClass *ret; - -+#if 0 - trace_object_class_dynamic_cast_assert(class ? class->type->name : "(null)", - typename, file, line, func); -+#endif - - #ifdef CONFIG_QOM_CAST_DEBUG - int i; -@@ -1051,16 +1065,19 @@ ObjectClass *object_get_class(Object *obj) +@@ -1051,16 +1061,19 @@ ObjectClass *object_get_class(Object *obj) return obj->class; } @@ -7528,7 +5132,7 @@ index 157a45c5..728b6927 100644 ObjectClass *object_class_by_name(const char *typename) { TypeImpl *type = type_get_by_name(typename); -@@ -1092,6 +1109,7 @@ ObjectClass *module_object_class_by_name(const char *typename) +@@ -1092,6 +1105,7 @@ ObjectClass *module_object_class_by_name(const char *typename) #endif return oc; } @@ -7536,7 +5140,7 @@ index 157a45c5..728b6927 100644 ObjectClass *object_class_get_parent(ObjectClass *class) { -@@ -1106,6 +1124,7 @@ ObjectClass *object_class_get_parent(ObjectClass *class) +@@ -1106,6 +1120,7 @@ ObjectClass *object_class_get_parent(ObjectClass *class) return type->class; } @@ -7544,7 +5148,7 @@ index 157a45c5..728b6927 100644 typedef struct OCFData { void (*fn)(ObjectClass *klass, void *opaque); -@@ -1217,6 +1236,7 @@ GSList *object_class_get_list_sorted(const char *implements_type, +@@ -1217,6 +1232,7 @@ GSList *object_class_get_list_sorted(const char *implements_type, return g_slist_sort(object_class_get_list(implements_type, include_abstract), object_class_cmp); } @@ -7552,7 +5156,7 @@ index 157a45c5..728b6927 100644 Object *object_ref(void *objptr) { -@@ -1403,6 +1423,7 @@ ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name) +@@ -1403,6 +1419,7 @@ ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name) return g_hash_table_lookup(klass->properties, name); } @@ -7560,7 +5164,7 @@ index 157a45c5..728b6927 100644 ObjectProperty *object_class_property_find_err(ObjectClass *klass, const char *name, Error **errp) -@@ -1424,6 +1445,7 @@ void object_property_del(Object *obj, const char *name) +@@ -1424,6 +1441,7 @@ void object_property_del(Object *obj, const char *name) } g_hash_table_remove(obj->properties, name); } @@ -7568,7 +5172,7 @@ index 157a45c5..728b6927 100644 bool object_property_get(Object *obj, const char *name, Visitor *v, Error **errp) -@@ -1464,6 +1486,7 @@ bool object_property_set(Object *obj, const char *name, Visitor *v, +@@ -1464,6 +1482,7 @@ bool object_property_set(Object *obj, const char *name, Visitor *v, return !*errp; } @@ -7576,7 +5180,7 @@ index 157a45c5..728b6927 100644 bool object_property_set_str(Object *obj, const char *name, const char *value, Error **errp) { -@@ -1473,6 +1496,7 @@ bool object_property_set_str(Object *obj, const char *name, +@@ -1473,6 +1492,7 @@ bool object_property_set_str(Object *obj, const char *name, qobject_unref(qstr); return ok; } @@ -7584,7 +5188,7 @@ index 157a45c5..728b6927 100644 char *object_property_get_str(Object *obj, const char *name, Error **errp) -@@ -1497,6 +1521,7 @@ char *object_property_get_str(Object *obj, const char *name, +@@ -1497,6 +1517,7 @@ char *object_property_get_str(Object *obj, const char *name, return retval; } @@ -7592,7 +5196,7 @@ index 157a45c5..728b6927 100644 bool object_property_set_link(Object *obj, const char *name, Object *value, Error **errp) { -@@ -1665,6 +1690,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name, +@@ -1665,6 +1686,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name, qobject_unref(ret); return retval; } @@ -7600,7 +5204,7 @@ index 157a45c5..728b6927 100644 typedef struct EnumProperty { const QEnumLookup *lookup; -@@ -1829,6 +1855,7 @@ object_property_add_child(Object *obj, const char *name, +@@ -1829,6 +1851,7 @@ object_property_add_child(Object *obj, const char *name, return object_property_try_add_child(obj, name, child, &error_abort); } @@ -7608,7 +5212,7 @@ index 157a45c5..728b6927 100644 void object_property_allow_set_link(const Object *obj, const char *name, Object *val, Error **errp) { -@@ -2056,6 +2083,7 @@ object_property_add_const_link(Object *obj, const char *name, +@@ -2056,6 +2079,7 @@ object_property_add_const_link(Object *obj, const char *name, object_get_typename(target), target, NULL, OBJ_PROP_LINK_DIRECT); } @@ -7616,7 +5220,7 @@ index 157a45c5..728b6927 100644 const char *object_get_canonical_path_component(const Object *obj) { -@@ -2125,6 +2153,7 @@ Object *object_resolve_path_component(Object *parent, const char *part) +@@ -2125,6 +2149,7 @@ Object *object_resolve_path_component(Object *parent, const char *part) } } @@ -7624,7 +5228,7 @@ index 157a45c5..728b6927 100644 static Object *object_resolve_abs_path(Object *parent, char **parts, const char *typename) -@@ -2240,6 +2269,7 @@ Object *object_resolve_type_unambiguous(const char *typename, Error **errp) +@@ -2240,6 +2265,7 @@ Object *object_resolve_type_unambiguous(const char *typename, Error **errp) } return o; } @@ -7632,7 +5236,7 @@ index 157a45c5..728b6927 100644 typedef struct StringProperty { -@@ -2771,6 +2801,7 @@ object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, +@@ -2771,6 +2797,7 @@ object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, getter, setter, NULL, (void *)v); } @@ -7640,7 +5244,7 @@ index 157a45c5..728b6927 100644 typedef struct { Object *target_obj; char *target_name; -@@ -2849,6 +2880,7 @@ object_property_add_alias(Object *obj, const char *name, +@@ -2849,6 +2876,7 @@ object_property_add_alias(Object *obj, const char *name, target_prop->description); return op; } @@ -7649,10 +5253,10 @@ index 157a45c5..728b6927 100644 void object_property_set_description(Object *obj, const char *name, const char *description) diff --git a/qcow2/lib/system/cpus.c b/qcow2/lib/system/cpus.c -index 1c818ff6..81236835 100644 +index 1c818ff6..ed2b9ff5 100644 --- a/qcow2/lib/system/cpus.c +++ b/qcow2/lib/system/cpus.c -@@ -23,29 +23,29 @@ +@@ -23,28 +23,28 @@ */ #include "qemu/osdep.h" @@ -7691,14 +5295,12 @@ index 1c818ff6..81236835 100644 -#include "sysemu/whpx.h" -#include "hw/boards.h" -#include "hw/hw.h" --#include "trace.h" +//#include "sysemu/whpx.h" +//#include "hw/boards.h" +//#include "hw/hw.h" -+//#include "trace.h" + #include "trace.h" #ifdef CONFIG_LINUX - @@ -68,6 +68,7 @@ /* The Big QEMU Lock (BQL) */ static QemuMutex bql; @@ -7753,57 +5355,18 @@ index 1c818ff6..81236835 100644 +#endif diff --git a/qcow2/lib/util/aio-posix.c b/qcow2/lib/util/aio-posix.c -index 266c9dd3..237951d9 100644 +index 266c9dd3..fe40072f 100644 --- a/qcow2/lib/util/aio-posix.c +++ b/qcow2/lib/util/aio-posix.c -@@ -19,9 +19,9 @@ +@@ -19,7 +19,7 @@ #include "qemu/main-loop.h" #include "qemu/rcu.h" #include "qemu/rcu_queue.h" -#include "qemu/sockets.h" +//#include "qemu/sockets.h" #include "qemu/cutils.h" --#include "trace.h" -+//#include "trace.h" + #include "trace.h" #include "aio-posix.h" - - /* Stop userspace polling on a handler if it isn't active for some time */ -@@ -338,7 +338,7 @@ static bool aio_dispatch_handler(AioContext *ctx, AioHandler *node) - if (!QLIST_IS_INSERTED(node, node_deleted) && - !QLIST_IS_INSERTED(node, node_poll) && - node->io_poll) { -- trace_poll_add(ctx, node, node->pfd.fd, revents); -+ //trace_poll_add(ctx, node, node->pfd.fd, revents); - if (ctx->poll_started && node->io_poll_begin) { - node->io_poll_begin(node->opaque); - } -@@ -486,7 +486,7 @@ static bool remove_idle_poll_handlers(AioContext *ctx, - if (node->poll_idle_timeout == 0LL) { - node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS; - } else if (now >= node->poll_idle_timeout) { -- trace_poll_remove(ctx, node, node->pfd.fd); -+ //trace_poll_remove(ctx, node, node->pfd.fd); - node->poll_idle_timeout = 0LL; - QLIST_SAFE_REMOVE(node, node_poll); - if (ctx->poll_started && node->io_poll_end) { -@@ -527,7 +527,7 @@ static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list, - - assert(qemu_lockcnt_count(&ctx->list_lock) > 0); - -- trace_run_poll_handlers_begin(ctx, max_ns, *timeout); -+ //trace_run_poll_handlers_begin(ctx, max_ns, *timeout); - - /* - * Optimization: ->io_poll() handlers often contain RCU read critical -@@ -561,7 +561,7 @@ static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list, - *timeout -= MIN(*timeout, elapsed_time); - } - -- trace_run_poll_handlers_end(ctx, progress, *timeout); -+ //trace_run_poll_handlers_end(ctx, progress, *timeout); - return progress; - } - @@ -686,7 +686,7 @@ bool aio_poll(AioContext *ctx, bool blocking) /* This is the sweet spot, no adjustment needed */ } else if (block_ns > ctx->poll_max_ns) { @@ -7813,12 +5376,7 @@ index 266c9dd3..237951d9 100644 if (ctx->poll_shrink) { ctx->poll_ns /= ctx->poll_shrink; -@@ -694,11 +694,11 @@ bool aio_poll(AioContext *ctx, bool blocking) - ctx->poll_ns = 0; - } - -- trace_poll_shrink(ctx, old, ctx->poll_ns); -+ //trace_poll_shrink(ctx, old, ctx->poll_ns); +@@ -698,7 +698,7 @@ bool aio_poll(AioContext *ctx, bool blocking) } else if (ctx->poll_ns < ctx->poll_max_ns && block_ns < ctx->poll_max_ns) { /* There is room to grow, poll longer */ @@ -7827,28 +5385,10 @@ index 266c9dd3..237951d9 100644 int64_t grow = ctx->poll_grow; if (grow == 0) { -@@ -715,7 +715,7 @@ bool aio_poll(AioContext *ctx, bool blocking) - ctx->poll_ns = ctx->poll_max_ns; - } - -- trace_poll_grow(ctx, old, ctx->poll_ns); -+ //trace_poll_grow(ctx, old, ctx->poll_ns); - } - } - diff --git a/qcow2/lib/util/async.c b/qcow2/lib/util/async.c -index 3e3e4fc7..bcc6dee9 100644 +index 3e3e4fc7..ce52d0a8 100644 --- a/qcow2/lib/util/async.c +++ b/qcow2/lib/util/async.c -@@ -35,7 +35,7 @@ - #include "qemu/coroutine_int.h" - #include "qemu/coroutine-tls.h" - #include "sysemu/cpu-timers.h" --#include "trace.h" -+//#include "trace.h" - - /***********************************************************/ - /* bottom halves (can be seen as timers which expire ASAP) */ @@ -162,9 +162,11 @@ void aio_bh_call(QEMUBH *bh) MemReentrancyGuard *reentrancy_guard = bh->reentrancy_guard; if (reentrancy_guard) { @@ -7861,24 +5401,6 @@ index 3e3e4fc7..bcc6dee9 100644 reentrancy_guard->engaged_in_io = true; } -@@ -563,7 +565,7 @@ static void co_schedule_bh_cb(void *opaque) - while (!QSLIST_EMPTY(&straight)) { - Coroutine *co = QSLIST_FIRST(&straight); - QSLIST_REMOVE_HEAD(&straight, co_scheduled_next); -- trace_aio_co_schedule_bh_cb(ctx, co); -+ //trace_aio_co_schedule_bh_cb(ctx, co); - - /* Protected by write barrier in qemu_aio_coroutine_enter */ - qatomic_set(&co->scheduled, NULL); -@@ -628,7 +630,7 @@ fail: - - void aio_co_schedule(AioContext *ctx, Coroutine *co) - { -- trace_aio_co_schedule(ctx, co); -+ //trace_aio_co_schedule(ctx, co); - const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, - __func__); - diff --git a/qcow2/lib/util/bufferiszero.c b/qcow2/lib/util/bufferiszero.c index 522146da..b86b90a2 100644 --- a/qcow2/lib/util/bufferiszero.c @@ -8045,49 +5567,22 @@ index e5e24720..e84b4b16 100644 if (err->hint) { error_printf("%s", err->hint->str); diff --git a/qcow2/lib/util/hbitmap.c b/qcow2/lib/util/hbitmap.c -index 6d6e1b59..106be57b 100644 +index 6d6e1b59..37cb7bc7 100644 --- a/qcow2/lib/util/hbitmap.c +++ b/qcow2/lib/util/hbitmap.c -@@ -12,7 +12,7 @@ - #include "qemu/osdep.h" - #include "qemu/hbitmap.h" - #include "qemu/host-utils.h" --#include "trace.h" -+//#include "trace.h" - #include "crypto/hash.h" - - /* HBitmaps provides an array of bits. The bits are stored as usual in an -@@ -140,7 +140,7 @@ static unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) - } - - hbi->pos = pos; -- trace_hbitmap_iter_skip_words(hbi->hb, hbi, pos, cur); -+ //trace_hbitmap_iter_skip_words(hbi->hb, hbi, pos, cur); - - assert(cur); - return cur; -@@ -471,8 +471,8 @@ void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count) - return; - } - -- trace_hbitmap_set(hb, start, count, -- start >> hb->granularity, last >> hb->granularity); -+ //trace_hbitmap_set(hb, start, count, -+ // start >> hb->granularity, last >> hb->granularity); - - first = start >> hb->granularity; - last >>= hb->granularity; -@@ -569,8 +569,8 @@ void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count) - assert(QEMU_IS_ALIGNED(start, gran)); - assert(QEMU_IS_ALIGNED(count, gran) || (start + count == hb->orig_size)); +@@ -944,6 +944,7 @@ void hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) + result->count = hb_count_between(result, 0, result->size - 1); + } -- trace_hbitmap_reset(hb, start, count, -- start >> hb->granularity, last >> hb->granularity); -+ //trace_hbitmap_reset(hb, start, count, -+ // start >> hb->granularity, last >> hb->granularity); ++#if 0 + char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) + { + size_t size = bitmap->sizes[HBITMAP_LEVELS - 1] * sizeof(unsigned long); +@@ -953,3 +954,4 @@ char *hbitmap_sha256(const HBitmap *bitmap, Error **errp) - first = start >> hb->granularity; - last >>= hb->granularity; + return hash; + } ++#endif diff --git a/qcow2/lib/util/host-utils.c b/qcow2/lib/util/host-utils.c index fb91bcba..6d0e2825 100644 --- a/qcow2/lib/util/host-utils.c @@ -8143,91 +5638,6 @@ index 7e73948f..14eb3cd1 100644 return false; } -diff --git a/qcow2/lib/util/lockcnt.c b/qcow2/lib/util/lockcnt.c -index 5da36946..69274a4a 100644 ---- a/qcow2/lib/util/lockcnt.c -+++ b/qcow2/lib/util/lockcnt.c -@@ -9,7 +9,7 @@ - #include "qemu/osdep.h" - #include "qemu/thread.h" - #include "qemu/atomic.h" --#include "trace.h" -+//#include "trace.h" - - #ifdef CONFIG_LINUX - #include "qemu/futex.h" -@@ -60,10 +60,10 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, - if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_FREE) { - int expected = *val; - -- trace_lockcnt_fast_path_attempt(lockcnt, expected, new_if_free); -+ //trace_lockcnt_fast_path_attempt(lockcnt, expected, new_if_free); - *val = qatomic_cmpxchg(&lockcnt->count, expected, new_if_free); - if (*val == expected) { -- trace_lockcnt_fast_path_success(lockcnt, expected, new_if_free); -+ //trace_lockcnt_fast_path_success(lockcnt, expected, new_if_free); - *val = new_if_free; - return true; - } -@@ -80,7 +80,7 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, - int expected = *val; - int new = expected - QEMU_LOCKCNT_STATE_LOCKED + QEMU_LOCKCNT_STATE_WAITING; - -- trace_lockcnt_futex_wait_prepare(lockcnt, expected, new); -+ //trace_lockcnt_futex_wait_prepare(lockcnt, expected, new); - *val = qatomic_cmpxchg(&lockcnt->count, expected, new); - if (*val == expected) { - *val = new; -@@ -90,10 +90,10 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, - - if ((*val & QEMU_LOCKCNT_STATE_MASK) == QEMU_LOCKCNT_STATE_WAITING) { - *waited = true; -- trace_lockcnt_futex_wait(lockcnt, *val); -+ //trace_lockcnt_futex_wait(lockcnt, *val); - qemu_futex_wait(&lockcnt->count, *val); - *val = qatomic_read(&lockcnt->count); -- trace_lockcnt_futex_wait_resume(lockcnt, *val); -+ //trace_lockcnt_futex_wait_resume(lockcnt, *val); - continue; - } - -@@ -104,7 +104,7 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, - - static void lockcnt_wake(QemuLockCnt *lockcnt) - { -- trace_lockcnt_futex_wake(lockcnt); -+ //trace_lockcnt_futex_wake(lockcnt); - qemu_futex_wake(&lockcnt->count, 1); - } - -@@ -261,11 +261,11 @@ void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt) - do { - expected = val; - new = (val + QEMU_LOCKCNT_COUNT_STEP) & ~QEMU_LOCKCNT_STATE_MASK; -- trace_lockcnt_unlock_attempt(lockcnt, val, new); -+ //trace_lockcnt_unlock_attempt(lockcnt, val, new); - val = qatomic_cmpxchg(&lockcnt->count, val, new); - } while (val != expected); - -- trace_lockcnt_unlock_success(lockcnt, val, new); -+ //trace_lockcnt_unlock_success(lockcnt, val, new); - if (val & QEMU_LOCKCNT_STATE_WAITING) { - lockcnt_wake(lockcnt); - } -@@ -279,11 +279,11 @@ void qemu_lockcnt_unlock(QemuLockCnt *lockcnt) - do { - expected = val; - new = val & ~QEMU_LOCKCNT_STATE_MASK; -- trace_lockcnt_unlock_attempt(lockcnt, val, new); -+ //trace_lockcnt_unlock_attempt(lockcnt, val, new); - val = qatomic_cmpxchg(&lockcnt->count, val, new); - } while (val != expected); - -- trace_lockcnt_unlock_success(lockcnt, val, new); -+ //trace_lockcnt_unlock_success(lockcnt, val, new); - if (val & QEMU_LOCKCNT_STATE_WAITING) { - lockcnt_wake(lockcnt); - } diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c index a0386cfe..817e65a6 100644 --- a/qcow2/lib/util/main-loop.c @@ -8294,19 +5704,10 @@ index a0386cfe..817e65a6 100644 glib_pollfds_poll(); diff --git a/qcow2/lib/util/memalign.c b/qcow2/lib/util/memalign.c -index c199ae70..c8b526e5 100644 +index c199ae70..622e805c 100644 --- a/qcow2/lib/util/memalign.c +++ b/qcow2/lib/util/memalign.c -@@ -27,7 +27,7 @@ - #include "qemu/osdep.h" - #include "qemu/host-utils.h" - #include "qemu/memalign.h" --#include "trace.h" -+//#include "trace.h" - - void *qemu_try_memalign(size_t alignment, size_t size) - { -@@ -62,9 +62,10 @@ void *qemu_try_memalign(size_t alignment, size_t size) +@@ -62,7 +62,8 @@ void *qemu_try_memalign(size_t alignment, size_t size) #elif defined(CONFIG_MEMALIGN) ptr = memalign(alignment, size); #else @@ -8314,51 +5715,8 @@ index c199ae70..c8b526e5 100644 + ptr = aligned_alloc(alignment, size); + //#error No function to allocate aligned memory available #endif -- trace_qemu_memalign(alignment, size, ptr); -+ //trace_qemu_memalign(alignment, size, ptr); + trace_qemu_memalign(alignment, size, ptr); return ptr; - } - -@@ -82,7 +83,7 @@ void *qemu_memalign(size_t alignment, size_t size) - - void qemu_vfree(void *ptr) - { -- trace_qemu_vfree(ptr); -+ //trace_qemu_vfree(ptr); - #if !defined(CONFIG_POSIX_MEMALIGN) && defined(CONFIG_ALIGNED_MALLOC) - /* Only Windows _aligned_malloc needs a special free function */ - _aligned_free(ptr); -diff --git a/qcow2/lib/util/module.c b/qcow2/lib/util/module.c -index 3eb0f06d..f0791505 100644 ---- a/qcow2/lib/util/module.c -+++ b/qcow2/lib/util/module.c -@@ -25,7 +25,7 @@ - #ifdef CONFIG_MODULE_UPGRADES - #include "qemu-version.h" - #endif --#include "trace.h" -+//#include "trace.h" - - typedef struct ModuleEntry - { -@@ -191,7 +191,7 @@ static bool module_load_dso(const char *fname, bool export_symbols, - e->init(); - register_module_init(e->init, e->type); - } -- trace_module_load_module(fname); -+ //trace_module_load_module(fname); - QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) { - QTAILQ_REMOVE(&dso_init_list, e, node); - g_free(e); -@@ -327,7 +327,7 @@ int module_load_qom(const char *type, Error **errp) - return -1; - } - -- trace_module_lookup_object_type(type); -+ //trace_module_lookup_object_type(type); - for (modinfo = module_info; modinfo->name != NULL; modinfo++) { - if (!modinfo->objs) { - continue; diff --git a/qcow2/lib/util/osdep.c b/qcow2/lib/util/osdep.c index 77036983..f78d6cd3 100644 --- a/qcow2/lib/util/osdep.c @@ -8445,7 +5803,7 @@ index 77036983..f78d6cd3 100644 #ifdef _WIN32 static void socket_cleanup(void) diff --git a/qcow2/lib/util/oslib-posix.c b/qcow2/lib/util/oslib-posix.c -index 11b35e48..23c378e9 100644 +index 11b35e48..ee979a44 100644 --- a/qcow2/lib/util/oslib-posix.c +++ b/qcow2/lib/util/oslib-posix.c @@ -31,20 +31,20 @@ @@ -8453,9 +5811,8 @@ index 11b35e48..23c378e9 100644 #include -#include "sysemu/sysemu.h" --#include "trace.h" +//#include "sysemu/sysemu.h" -+//#include "trace.h" + #include "trace.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "qemu/madvise.h" @@ -8556,151 +5913,6 @@ index 11b35e48..23c378e9 100644 } } +#endif -diff --git a/qcow2/lib/util/qemu-coroutine-lock.c b/qcow2/lib/util/qemu-coroutine-lock.c -index 25344353..6afe27a0 100644 ---- a/qcow2/lib/util/qemu-coroutine-lock.c -+++ b/qcow2/lib/util/qemu-coroutine-lock.c -@@ -31,7 +31,7 @@ - #include "qemu/processor.h" - #include "qemu/queue.h" - #include "block/aio.h" --#include "trace.h" -+//#include "trace.h" - - void qemu_co_queue_init(CoQueue *queue) - { -@@ -198,7 +198,7 @@ static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx, - CoWaitRecord w; - unsigned old_handoff; - -- trace_qemu_co_mutex_lock_entry(mutex, self); -+ //trace_qemu_co_mutex_lock_entry(mutex, self); - push_waiter(mutex, &w); - - /* -@@ -230,7 +230,7 @@ static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx, - } - - qemu_coroutine_yield(); -- trace_qemu_co_mutex_lock_return(mutex, self); -+ //trace_qemu_co_mutex_lock_return(mutex, self); - } - - void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) -@@ -264,7 +264,7 @@ retry_fast_path: - - if (waiters == 0) { - /* Uncontended. */ -- trace_qemu_co_mutex_lock_uncontended(mutex, self); -+ //trace_qemu_co_mutex_lock_uncontended(mutex, self); - mutex->ctx = ctx; - } else { - qemu_co_mutex_lock_slowpath(ctx, mutex); -@@ -277,7 +277,7 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) - { - Coroutine *self = qemu_coroutine_self(); - -- trace_qemu_co_mutex_unlock_entry(mutex, self); -+ //trace_qemu_co_mutex_unlock_entry(mutex, self); - - assert(mutex->locked); - assert(mutex->holder == self); -@@ -326,7 +326,7 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex) - } - } - -- trace_qemu_co_mutex_unlock_return(mutex, self); -+ //trace_qemu_co_mutex_unlock_return(mutex, self); - } - - struct CoRwTicket { -diff --git a/qcow2/lib/util/qemu-coroutine.c b/qcow2/lib/util/qemu-coroutine.c -index eb4eebef..3f9b9920 100644 ---- a/qcow2/lib/util/qemu-coroutine.c -+++ b/qcow2/lib/util/qemu-coroutine.c -@@ -13,7 +13,7 @@ - */ - - #include "qemu/osdep.h" --#include "trace.h" -+//#include "trace.h" - #include "qemu/thread.h" - #include "qemu/atomic.h" - #include "qemu/coroutine_int.h" -@@ -265,7 +265,7 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) - - QSIMPLEQ_REMOVE_HEAD(&pending, co_queue_next); - -- trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg); -+ //trace_qemu_aio_coroutine_enter(ctx, from, to, to->entry_arg); - - /* if the Coroutine has already been scheduled, entering it again will - * cause us to enter it twice, potentially even after the coroutine has -@@ -302,7 +302,7 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co) - break; - case COROUTINE_TERMINATE: - assert(!to->locks_held); -- trace_qemu_coroutine_terminate(to); -+ //trace_qemu_coroutine_terminate(to); - coroutine_delete(to); - break; - default: -@@ -328,7 +328,7 @@ void coroutine_fn qemu_coroutine_yield(void) - Coroutine *self = qemu_coroutine_self(); - Coroutine *to = self->caller; - -- trace_qemu_coroutine_yield(self, to); -+ //trace_qemu_coroutine_yield(self, to); - - if (!to) { - fprintf(stderr, "Co-routine is yielding to no one\n"); -diff --git a/qcow2/lib/util/qemu-option.c b/qcow2/lib/util/qemu-option.c -index 201f7a87..4ba11fa1 100644 ---- a/qcow2/lib/util/qemu-option.c -+++ b/qcow2/lib/util/qemu-option.c -@@ -637,7 +637,7 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, - opts = g_malloc0(sizeof(*opts)); - opts->id = g_strdup(id); - opts->list = list; -- loc_save(&opts->loc); -+ //loc_save(&opts->loc); - QTAILQ_INIT(&opts->head); - QTAILQ_INSERT_TAIL(&list->head, opts, next); - return opts; -@@ -654,7 +654,7 @@ void qemu_opts_reset(QemuOptsList *list) - - void qemu_opts_loc_restore(QemuOpts *opts) - { -- loc_restore(&opts->loc); -+ //loc_restore(&opts->loc); - } - - const char *qemu_opts_id(QemuOpts *opts) -@@ -1125,20 +1125,20 @@ bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp) - int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, - void *opaque, Error **errp) - { -- Location loc; -+ //Location loc; - QemuOpts *opts, *next; - int rc = 0; - -- loc_push_none(&loc); -+ //loc_push_none(&loc); - QTAILQ_FOREACH_SAFE(opts, &list->head, next, next) { -- loc_restore(&opts->loc); -+ //loc_restore(&opts->loc); - rc = func(opaque, opts, errp); - if (rc) { - break; - } - assert(!errp || !*errp); - } -- loc_pop(&loc); -+ //loc_pop(&loc); - return rc; - } - diff --git a/qcow2/lib/util/qemu-print.c b/qcow2/lib/util/qemu-print.c index 69ba612f..98ca5474 100644 --- a/qcow2/lib/util/qemu-print.c @@ -8740,7 +5952,7 @@ index 69ba612f..98ca5474 100644 } diff --git a/qcow2/lib/util/qemu-sockets.c b/qcow2/lib/util/qemu-sockets.c -index 60c44b2b..ea955c48 100644 +index 60c44b2b..a54af944 100644 --- a/qcow2/lib/util/qemu-sockets.c +++ b/qcow2/lib/util/qemu-sockets.c @@ -21,7 +21,7 @@ @@ -8752,15 +5964,6 @@ index 60c44b2b..ea955c48 100644 #include "qapi/clone-visitor.h" #include "qapi/error.h" #include "qapi/qapi-visit-sockets.h" -@@ -30,7 +30,7 @@ - #include "qapi/qobject-input-visitor.h" - #include "qapi/qobject-output-visitor.h" - #include "qemu/cutils.h" --#include "trace.h" -+//#include "trace.h" - - #ifndef AI_ADDRCONFIG - # define AI_ADDRCONFIG 0 @@ -1137,21 +1137,23 @@ fail: static int socket_get_fd(const char *fdstr, Error **errp) @@ -8799,15 +6002,6 @@ index 60c44b2b..ea955c48 100644 case SOCKET_ADDRESS_TYPE_VSOCK: fd = vsock_connect_saddr(&addr->u.vsock, errp); -@@ -1210,7 +1214,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) - { - int fd; - -- trace_socket_listen(num); -+ //trace_socket_listen(num); - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - fd = inet_listen_saddr(&addr->u.inet, 0, num, errp); @@ -1220,6 +1224,7 @@ int socket_listen(SocketAddress *addr, int num, Error **errp) fd = unix_listen_saddr(&addr->u.q_unix, num, errp); break; @@ -8837,48 +6031,8 @@ index 60c44b2b..ea955c48 100644 return addr; } +#endif -diff --git a/qcow2/lib/util/qemu-thread-common.h b/qcow2/lib/util/qemu-thread-common.h -index 2af6b120..d09ade17 100644 ---- a/qcow2/lib/util/qemu-thread-common.h -+++ b/qcow2/lib/util/qemu-thread-common.h -@@ -14,7 +14,7 @@ - #define QEMU_THREAD_COMMON_H - - #include "qemu/thread.h" --#include "trace.h" -+//#include "trace.h" - - static inline void qemu_mutex_post_init(QemuMutex *mutex) - { -@@ -28,7 +28,7 @@ static inline void qemu_mutex_post_init(QemuMutex *mutex) - static inline void qemu_mutex_pre_lock(QemuMutex *mutex, - const char *file, int line) - { -- trace_qemu_mutex_lock(mutex, file, line); -+ //trace_qemu_mutex_lock(mutex, file, line); - } - - static inline void qemu_mutex_post_lock(QemuMutex *mutex, -@@ -38,7 +38,7 @@ static inline void qemu_mutex_post_lock(QemuMutex *mutex, - mutex->file = file; - mutex->line = line; - #endif -- trace_qemu_mutex_locked(mutex, file, line); -+ //trace_qemu_mutex_locked(mutex, file, line); - } - - static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, -@@ -48,7 +48,7 @@ static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, - mutex->file = NULL; - mutex->line = 0; - #endif -- trace_qemu_mutex_unlock(mutex, file, line); -+ //trace_qemu_mutex_unlock(mutex, file, line); - } - - #endif diff --git a/qcow2/lib/util/qemu-thread-posix.c b/qcow2/lib/util/qemu-thread-posix.c -index b2e26e21..8ea29d06 100644 +index b2e26e21..f02bf387 100644 --- a/qcow2/lib/util/qemu-thread-posix.c +++ b/qcow2/lib/util/qemu-thread-posix.c @@ -15,7 +15,7 @@ @@ -8890,18 +6044,6 @@ index b2e26e21..8ea29d06 100644 #include "qemu/bitmap.h" #ifdef CONFIG_PTHREAD_SET_NAME_NP -@@ -235,9 +235,9 @@ qemu_cond_timedwait_ts(QemuCond *cond, QemuMutex *mutex, struct timespec *ts, - int err; - - assert(cond->initialized); -- trace_qemu_mutex_unlock(mutex, file, line); -+ //trace_qemu_mutex_unlock(mutex, file, line); - err = pthread_cond_timedwait(&cond->cond, &mutex->lock, ts); -- trace_qemu_mutex_locked(mutex, file, line); -+ //trace_qemu_mutex_locked(mutex, file, line); - if (err && err != ETIMEDOUT) { - error_exit(err, __func__); - } @@ -518,7 +518,7 @@ static void *qemu_thread_start(void *args) pthread_set_name_np(pthread_self(), qemu_thread_args->name); # endif @@ -9055,48 +6197,6 @@ index 6b783e2e..5e54c4be 100644 } } +#endif -diff --git a/qcow2/lib/util/thread-pool.c b/qcow2/lib/util/thread-pool.c -index 27eb777e..c0026baa 100644 ---- a/qcow2/lib/util/thread-pool.c -+++ b/qcow2/lib/util/thread-pool.c -@@ -19,7 +19,7 @@ - #include "qemu/queue.h" - #include "qemu/thread.h" - #include "qemu/coroutine.h" --#include "trace.h" -+//#include "trace.h" - #include "block/thread-pool.h" - #include "qemu/main-loop.h" - -@@ -184,8 +184,8 @@ restart: - continue; - } - -- trace_thread_pool_complete(pool, elem, elem->common.opaque, -- elem->ret); -+ //trace_thread_pool_complete(pool, elem, elem->common.opaque, -+ // elem->ret); - QLIST_REMOVE(elem, all); - - if (elem->common.cb) { -@@ -220,7 +220,7 @@ static void thread_pool_cancel(BlockAIOCB *acb) - ThreadPoolElement *elem = (ThreadPoolElement *)acb; - ThreadPool *pool = elem->pool; - -- trace_thread_pool_cancel(elem, elem->common.opaque); -+ //trace_thread_pool_cancel(elem, elem->common.opaque); - - QEMU_LOCK_GUARD(&pool->lock); - if (elem->state == THREAD_QUEUED) { -@@ -256,7 +256,7 @@ BlockAIOCB *thread_pool_submit_aio(ThreadPoolFunc *func, void *arg, - - QLIST_INSERT_HEAD(&pool->head, req, all); - -- trace_thread_pool_submit(pool, req, arg); -+ //trace_thread_pool_submit(pool, req, arg); - - qemu_mutex_lock(&pool->lock); - if (pool->idle_threads == 0 && pool->cur_threads < pool->max_threads) { diff --git a/qcow2/lib/util/yank.c b/qcow2/lib/util/yank.c index eaac5053..be696c91 100644 --- a/qcow2/lib/util/yank.c diff --git a/SOURCES/0026-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch b/SOURCES/0026-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch index 2fb8c3d..ecfd51c 100644 --- a/SOURCES/0026-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch +++ b/SOURCES/0026-libqcow2-fix-support-for-old-components-gcc-glibc-gl.patch @@ -1,4 +1,4 @@ -From 630206452cd903deb03b916e6cf77047f4a5e48c Mon Sep 17 00:00:00 2001 +From 844e8cb3a6c61f4ad805982caf3b62470e080b95 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Tue, 14 Jan 2025 11:41:26 +0100 Subject: [PATCH] libqcow2: fix support for old components (gcc, glibc, glib, @@ -13,11 +13,10 @@ Signed-off-by: Anthoine Bourgeois include/qemu/config-host.h | 2 +- include/qemu/host-utils.h | 2 ++ qcow2/lib/block/file-posix.c | 2 ++ - qcow2/lib/crypto/hash.c | 1 + qcow2/lib/util/error-report.c | 2 +- qcow2/lib/util/qemu-thread-posix.c | 2 ++ qcow2/lib/util/thread-pool.c | 5 +++-- - 8 files changed, 14 insertions(+), 6 deletions(-) + 7 files changed, 13 insertions(+), 6 deletions(-) diff --git a/include/glib-compat.h b/include/glib-compat.h index 86be439b..e4a23999 100644 @@ -72,7 +71,7 @@ index 12ae37af..b78a2b21 100644 /* Host type specific sizes of these routines. */ diff --git a/qcow2/lib/block/file-posix.c b/qcow2/lib/block/file-posix.c -index 852f0222..efa8ec2b 100644 +index 3223c75e..0960f25f 100644 --- a/qcow2/lib/block/file-posix.c +++ b/qcow2/lib/block/file-posix.c @@ -2008,6 +2008,7 @@ static int handle_aiocb_write_zeroes_unmap(void *opaque) @@ -91,18 +90,6 @@ index 852f0222..efa8ec2b 100644 /* * parse_zone - Fill a zone descriptor -diff --git a/qcow2/lib/crypto/hash.c b/qcow2/lib/crypto/hash.c -index a282cf74..ffd74778 100644 ---- a/qcow2/lib/crypto/hash.c -+++ b/qcow2/lib/crypto/hash.c -@@ -18,6 +18,7 @@ - * - */ - -+#include - #include - - #include "qemu/osdep.h" diff --git a/qcow2/lib/util/error-report.c b/qcow2/lib/util/error-report.c index 0554f186..b3936a3c 100644 --- a/qcow2/lib/util/error-report.c @@ -117,7 +104,7 @@ index 0554f186..b3936a3c 100644 /* diff --git a/qcow2/lib/util/qemu-thread-posix.c b/qcow2/lib/util/qemu-thread-posix.c -index 8ea29d06..1bb011e3 100644 +index f02bf387..078833c7 100644 --- a/qcow2/lib/util/qemu-thread-posix.c +++ b/qcow2/lib/util/qemu-thread-posix.c @@ -534,7 +534,9 @@ static void *qemu_thread_start(void *args) @@ -131,7 +118,7 @@ index 8ea29d06..1bb011e3 100644 pthread_cleanup_push(qemu_thread_atexit_notify, NULL); diff --git a/qcow2/lib/util/thread-pool.c b/qcow2/lib/util/thread-pool.c -index c0026baa..225db985 100644 +index 27eb777e..baa5adcc 100644 --- a/qcow2/lib/util/thread-pool.c +++ b/qcow2/lib/util/thread-pool.c @@ -297,6 +297,7 @@ void thread_pool_submit(ThreadPoolFunc *func, void *arg) diff --git a/SOURCES/0027-tapdisk-protect-td_vbd_t-structure.patch b/SOURCES/0027-tapdisk-protect-td_vbd_t-structure.patch index 697b41f..81b0dfc 100644 --- a/SOURCES/0027-tapdisk-protect-td_vbd_t-structure.patch +++ b/SOURCES/0027-tapdisk-protect-td_vbd_t-structure.patch @@ -1,4 +1,4 @@ -From 1281c264539010ecd2c9cf122568855ce91cc52d Mon Sep 17 00:00:00 2001 +From 37450a2c1d3c5b520c3f38ecb709b8e89b128803 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 16 Jan 2025 14:09:33 +0100 Subject: [PATCH] tapdisk: protect td_vbd_t structure @@ -6,16 +6,20 @@ Subject: [PATCH] tapdisk: protect td_vbd_t structure libqcow2 spawns a thread to handle IO requests to qcow2 layer. Add a mutex to protect vbd structure from libqcow2 concurrency. +This commit also voids access to request after being issued. + This is the first part of tapdisk threading protection. Signed-off-by: Anthoine Bourgeois +Signed-off-by: Emmanuel Varagnat --- - drivers/tapdisk-vbd.c | 115 ++++++++++++++++++++++++++++++++++++++---- + drivers/tapdisk-vbd.c | 138 ++++++++++++++++++++++++++++++++++++------ drivers/tapdisk-vbd.h | 2 + - 2 files changed, 106 insertions(+), 11 deletions(-) + drivers/tapdisk.h | 2 +- + 3 files changed, 122 insertions(+), 20 deletions(-) diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c -index 433375d6..32131f31 100644 +index 433375d6..6e1f95d3 100644 --- a/drivers/tapdisk-vbd.c +++ b/drivers/tapdisk-vbd.c @@ -114,6 +114,8 @@ tapdisk_vbd_create(uint16_t uuid) @@ -361,7 +365,7 @@ index 433375d6..32131f31 100644 /* * if this request failed, but was not completed, * we'll back off for a while. -@@ -1662,6 +1727,7 @@ tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd) +@@ -1662,20 +1727,22 @@ tapdisk_vbd_reissue_failed_requests(td_vbd_t *vbd) if (err && !tapdisk_vbd_request_completed(vbd, vreq)) break; } @@ -369,15 +373,41 @@ index 433375d6..32131f31 100644 return 0; } -@@ -1684,17 +1750,23 @@ tapdisk_vbd_issue_new_requests(td_vbd_t *vbd) + +-static void +-tapdisk_vbd_count_new_request(td_vbd_t *vbd, td_vbd_request_t *vreq) ++static td_sector_t ++tapdisk_vbd_count_new_request(td_vbd_request_t *vreq) + { + struct td_iovec *iov; +- int write; +- +- write = vreq->op == TD_OP_WRITE; ++ td_sector_t secs = 0; + +- for (iov = &vreq->iov[0]; iov < &vreq->iov[vreq->iovcnt]; iov++) +- td_sector_count_add(&vbd->secs, iov->secs, write); ++ ASSERT(vreq->iov); ++ for (iov = &vreq->iov[0]; iov < &vreq->iov[vreq->iovcnt]; iov++) { ++ secs += iov->secs; ++ } ++ return secs; + } + + static int +@@ -1684,17 +1751,29 @@ tapdisk_vbd_issue_new_requests(td_vbd_t *vbd) int err; td_vbd_request_t *vreq, *tmp; + pthread_mutex_lock(&vbd->mutex); tapdisk_vbd_for_each_request(vreq, tmp, &vbd->new_requests) { ++ td_sector_t secs = tapdisk_vbd_count_new_request(vreq); ++ bool write = vreq->op == TD_OP_WRITE; ++ + pthread_mutex_unlock(&vbd->mutex); err = tapdisk_vbd_issue_request(vbd, vreq); + pthread_mutex_lock(&vbd->mutex); ++ /* * if this request failed, but was not completed, * we'll back off for a while. @@ -388,13 +418,16 @@ index 433375d6..32131f31 100644 return err; + } - tapdisk_vbd_count_new_request(vbd, vreq); +- tapdisk_vbd_count_new_request(vbd, vreq); ++ /* XXX: split counting and add; vreq must NOT be accessed after issuing the ++ request. */ ++ td_sector_count_add(&vbd->secs, secs, write); } + pthread_mutex_unlock(&vbd->mutex); return 0; } -@@ -1704,12 +1776,18 @@ tapdisk_vbd_recheck_state(td_vbd_t *vbd) +@@ -1704,12 +1783,18 @@ tapdisk_vbd_recheck_state(td_vbd_t *vbd) { int err = 0; @@ -415,7 +448,7 @@ index 433375d6..32131f31 100644 err = tapdisk_vbd_issue_requests(vbd); -@@ -1722,6 +1800,7 @@ tapdisk_vbd_kill_requests(td_vbd_t *vbd) +@@ -1722,6 +1807,7 @@ tapdisk_vbd_kill_requests(td_vbd_t *vbd) { td_vbd_request_t *vreq, *tmp; @@ -423,7 +456,7 @@ index 433375d6..32131f31 100644 tapdisk_vbd_for_each_request(vreq, tmp, &vbd->new_requests) { vreq->error = -ESHUTDOWN; tapdisk_vbd_move_request(vreq, &vbd->completed_requests); -@@ -1731,6 +1810,7 @@ tapdisk_vbd_kill_requests(td_vbd_t *vbd) +@@ -1731,6 +1817,7 @@ tapdisk_vbd_kill_requests(td_vbd_t *vbd) vreq->error = -ESHUTDOWN; tapdisk_vbd_move_request(vreq, &vbd->completed_requests); } @@ -431,7 +464,7 @@ index 433375d6..32131f31 100644 return 0; } -@@ -1740,17 +1820,24 @@ tapdisk_vbd_issue_requests(td_vbd_t *vbd) +@@ -1740,17 +1827,24 @@ tapdisk_vbd_issue_requests(td_vbd_t *vbd) { int err; @@ -459,7 +492,7 @@ index 433375d6..32131f31 100644 err = tapdisk_vbd_reissue_failed_requests(vbd); if (err) -@@ -1765,8 +1852,11 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) +@@ -1765,8 +1859,11 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) gettimeofday(&vreq->ts, NULL); vreq->vbd = vbd; @@ -471,7 +504,7 @@ index 433375d6..32131f31 100644 return 0; } -@@ -1774,11 +1864,13 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) +@@ -1774,11 +1871,13 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) void tapdisk_vbd_kick(td_vbd_t *vbd) { @@ -479,14 +512,14 @@ index 433375d6..32131f31 100644 + const struct list_head *list; td_vbd_request_t *vreq, *prev, *next; ++ pthread_mutex_lock(&vbd->mutex); vbd->kicked++; -+ pthread_mutex_lock(&vbd->mutex); + list = &vbd->completed_requests; while (!list_empty(list)) { /* -@@ -1808,6 +1900,7 @@ tapdisk_vbd_kick(td_vbd_t *vbd) +@@ -1808,6 +1907,7 @@ tapdisk_vbd_kick(td_vbd_t *vbd) prev->cb(prev, prev->error, prev->token, 1); vbd->returned++; } @@ -514,3 +547,16 @@ index 23ca7289..f3fa0398 100644 struct list_head next; +diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h +index 3163e190..d907d149 100644 +--- a/drivers/tapdisk.h ++++ b/drivers/tapdisk.h +@@ -243,7 +243,7 @@ struct td_sector_count { + }; + + static inline void +-td_sector_count_add(td_sector_count_t *s, td_sector_t v, int write) ++td_sector_count_add(td_sector_count_t *s, td_sector_t v, bool write) + { + if (write) + s->wr += v; diff --git a/SOURCES/0028-tapdisk-protect-td_blktap_t-structure.patch b/SOURCES/0028-tapdisk-protect-td_blktap_t-structure.patch index 8a59e8b..ef81f53 100644 --- a/SOURCES/0028-tapdisk-protect-td_blktap_t-structure.patch +++ b/SOURCES/0028-tapdisk-protect-td_blktap_t-structure.patch @@ -1,4 +1,4 @@ -From 54d1cad6f6769c28bdda54baaa1e48737882f48b Mon Sep 17 00:00:00 2001 +From fbe5e855d2b3bc859653be445883f8c73b41b230 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 16 Jan 2025 14:09:40 +0100 Subject: [PATCH] tapdisk: protect td_blktap_t structure @@ -10,15 +10,15 @@ This is the second part of the tapdisk threading protection. Signed-off-by: Anthoine Bourgeois --- - drivers/tapdisk-blktap.c | 18 ++++++++++++++---- + drivers/tapdisk-blktap.c | 34 ++++++++++++++++++++++++---------- drivers/tapdisk-blktap.h | 1 + - 2 files changed, 15 insertions(+), 4 deletions(-) + 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/tapdisk-blktap.c b/drivers/tapdisk-blktap.c -index cf72b095..8b9eeea5 100644 +index cf72b095..9c2e4855 100644 --- a/drivers/tapdisk-blktap.c +++ b/drivers/tapdisk-blktap.c -@@ -87,18 +87,20 @@ struct td_blktap_req { +@@ -87,19 +87,21 @@ struct td_blktap_req { struct timeval ts; }; @@ -37,10 +37,12 @@ index cf72b095..8b9eeea5 100644 } -void +-tapdisk_blktap_free_request(td_blktap_t *tap, td_blktap_req_t *req) +static void - tapdisk_blktap_free_request(td_blktap_t *tap, td_blktap_req_t *req) ++tapdisk_blktap_free_request_locked(td_blktap_t *tap, td_blktap_req_t *req) { BUG_ON(tap->n_reqs_free >= tap->n_reqs); + tap->reqs_free[tap->n_reqs_free++] = req; @@ -117,6 +119,8 @@ tapdisk_blktap_reqs_free(td_blktap_t *tap) free(tap->reqs_free); tap->reqs_free = NULL; @@ -59,7 +61,28 @@ index cf72b095..8b9eeea5 100644 tap->reqs = malloc(n_reqs * sizeof(td_blktap_req_t)); if (!tap->reqs) { err = -errno; -@@ -200,6 +206,7 @@ tapdisk_blktap_fail_request(td_blktap_t *tap, +@@ -139,8 +145,10 @@ tapdisk_blktap_reqs_init(td_blktap_t *tap, int n_reqs) + tap->n_reqs = n_reqs; + tap->n_reqs_free = 0; + ++ pthread_mutex_lock(&tap->mutex); + for (i = 0; i < n_reqs; i++) +- tapdisk_blktap_free_request(tap, &tap->reqs[i]); ++ tapdisk_blktap_free_request_locked(tap, &tap->reqs[i]); ++ pthread_mutex_unlock(&tap->mutex); + + return 0; + +@@ -180,7 +188,7 @@ tapdisk_blktap_error_status(td_blktap_t *tap, int error) + } + + static void +-__tapdisk_blktap_push_response(td_blktap_t *tap, int final) ++__tapdisk_blktap_push_response_locked(td_blktap_t *tap, int final) + { + tap->rsp_prod_pvt++; + +@@ -200,13 +208,15 @@ tapdisk_blktap_fail_request(td_blktap_t *tap, BUG_ON(!tap->vma); @@ -67,15 +90,16 @@ index cf72b095..8b9eeea5 100644 rsp = BLKTAP_GET_RESPONSE(tap, tap->rsp_prod_pvt); rsp->id = msg->id; -@@ -207,6 +214,7 @@ tapdisk_blktap_fail_request(td_blktap_t *tap, + rsp->operation = msg->operation; rsp->status = tapdisk_blktap_error_status(tap, error); - __tapdisk_blktap_push_response(tap, 1); +- __tapdisk_blktap_push_response(tap, 1); ++ __tapdisk_blktap_push_response_locked(tap, 1); + pthread_mutex_unlock(&tap->mutex); } static void -@@ -218,6 +226,7 @@ tapdisk_blktap_put_response(td_blktap_t *tap, +@@ -218,6 +228,7 @@ tapdisk_blktap_put_response(td_blktap_t *tap, unsigned long long interval; struct timeval now; @@ -83,18 +107,19 @@ index cf72b095..8b9eeea5 100644 BUG_ON(!tap->vma); rsp = BLKTAP_GET_RESPONSE(tap, tap->rsp_prod_pvt); -@@ -246,7 +255,10 @@ tapdisk_blktap_put_response(td_blktap_t *tap, +@@ -246,7 +257,10 @@ tapdisk_blktap_put_response(td_blktap_t *tap, rsp->operation = op; rsp->status = tapdisk_blktap_error_status(tap, error); -+ tapdisk_blktap_free_request(tap, req); +- __tapdisk_blktap_push_response(tap, final); ++ tapdisk_blktap_free_request_locked(tap, req); + - __tapdisk_blktap_push_response(tap, final); ++ __tapdisk_blktap_push_response_locked(tap, final); + pthread_mutex_unlock(&tap->mutex); } static void -@@ -256,8 +268,6 @@ tapdisk_blktap_complete_request(td_blktap_t *tap, +@@ -256,8 +270,6 @@ tapdisk_blktap_complete_request(td_blktap_t *tap, { if (likely(tap->vma)) tapdisk_blktap_put_response(tap, req, error, final); @@ -103,6 +128,17 @@ index cf72b095..8b9eeea5 100644 } static void +@@ -390,7 +402,9 @@ tapdisk_blktap_get_requests(td_blktap_t *tap) + err = tapdisk_blktap_parse_request(tap, msg, req); + if (err) { + tapdisk_blktap_fail_request(tap, msg, err); +- tapdisk_blktap_free_request(tap, req); ++ pthread_mutex_lock(&tap->mutex); ++ tapdisk_blktap_free_request_locked(tap, req); ++ pthread_mutex_unlock(&tap->mutex); + goto fail_ring; + } + diff --git a/drivers/tapdisk-blktap.h b/drivers/tapdisk-blktap.h index ca576a77..5bd4aae8 100644 --- a/drivers/tapdisk-blktap.h diff --git a/SOURCES/0029-tapdisk-protect-td_xenblkif-structure.patch b/SOURCES/0029-tapdisk-protect-td_xenblkif-structure.patch index f25259a..fb1fd29 100644 --- a/SOURCES/0029-tapdisk-protect-td_xenblkif-structure.patch +++ b/SOURCES/0029-tapdisk-protect-td_xenblkif-structure.patch @@ -1,4 +1,4 @@ -From a49cc3b09e3a3f8f074dc85f7120ae3e41f5bece Mon Sep 17 00:00:00 2001 +From 94394a12503d5ca4a30d40e13ca52c130358aeb8 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 16 Jan 2025 14:09:47 +0100 Subject: [PATCH] tapdisk: protect td_xenblkif structure @@ -10,11 +10,11 @@ This is the third part of the tapdisk threading protection. Signed-off-by: Anthoine Bourgeois --- - drivers/td-blkif.h | 5 +++++ - drivers/td-ctx.c | 12 ++++++++--- - drivers/td-req.c | 50 +++++++++++++++++++++++++++++++++++----------- - drivers/td-req.h | 15 -------------- - 4 files changed, 52 insertions(+), 30 deletions(-) + drivers/td-blkif.h | 5 ++++ + drivers/td-ctx.c | 12 ++++++-- + drivers/td-req.c | 70 +++++++++++++++++++++++++++++++++++----------- + drivers/td-req.h | 15 ---------- + 4 files changed, 68 insertions(+), 34 deletions(-) diff --git a/drivers/td-blkif.h b/drivers/td-blkif.h index d96888bd..2eaf080c 100644 @@ -80,7 +80,7 @@ index e344a62f..d54d8457 100644 tapdisk_xenblkif_queue_requests(blkif, reqs, n_reqs); diff --git a/drivers/td-req.c b/drivers/td-req.c -index 028e4bf6..1b7bd7ee 100644 +index 028e4bf6..4d901554 100644 --- a/drivers/td-req.c +++ b/drivers/td-req.c @@ -74,9 +74,11 @@ td_xenblkif_bufcache_event(event_id_t id, char mode, void *private) @@ -95,14 +95,14 @@ index 028e4bf6..1b7bd7ee 100644 } /** -@@ -439,10 +441,12 @@ out: +@@ -439,21 +441,26 @@ out: * @req the request to complete * @error completion status of the request * @final controls whether the other end should be notified + * @lock must always be true except in this function to control recursion */ -void -+static void ++static bool tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, - struct td_xenblkif_req* req, int err, const int final) + struct td_xenblkif_req* req, int err, const int final, @@ -110,7 +110,12 @@ index 028e4bf6..1b7bd7ee 100644 { int _err; long long *max = NULL, *sum = NULL, *cnt = NULL; -@@ -454,6 +458,8 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, + static int depth = 0; + bool processing_barrier_message; ++ bool blkif_destroyed = false; + uint64_t *ticks = NULL; + + ASSERT(blkif); ASSERT(req); ASSERT(depth >= 0); @@ -119,36 +124,45 @@ index 028e4bf6..1b7bd7ee 100644 depth++; processing_barrier_message = -@@ -553,9 +559,10 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, +@@ -553,9 +560,16 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, /* * If this is the last request, complete the barrier request. */ - if (tapdisk_xenblkif_barrier_should_complete(blkif)) -+ if (tapdisk_xenblkif_barrier_should_complete(blkif)) { - tapdisk_xenblkif_complete_request(blkif, +- tapdisk_xenblkif_complete_request(blkif, - msg_to_tapreq(blkif->barrier.msg), 0, 1); ++ if (tapdisk_xenblkif_barrier_should_complete(blkif)) { ++ blkif_destroyed = tapdisk_xenblkif_complete_request(blkif, + msg_to_tapreq(blkif->barrier.msg), 0, 1, false); -+ } ++ /* ++ * We assert here on "blkif_destroyed == true" because as ++ * "depth > 1" in the recursive call above, the branch to ++ * destroy the ring shouldn't be taken. ++ */ ++ ASSERT(!blkif_destroyed); ++ } } /* -@@ -566,11 +573,15 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, +@@ -566,11 +580,17 @@ tapdisk_xenblkif_complete_request(struct td_xenblkif * const blkif, && !tapdisk_xenblkif_reqs_pending(blkif))) { RING_DEBUG(blkif, "destroying dead ring\n"); + pthread_mutex_unlock(&blkif->mutex); tapdisk_xenblkif_destroy(blkif); -+ lock = 0; /* blkif with its mutex were destroyed above so don't try to unlock it */ ++ blkif_destroyed = true; ++ lock = false; /* blkif with its mutex were destroyed above so don't try to unlock it */ } out: depth--; + if (lock) + pthread_mutex_unlock(&blkif->mutex); ++ return blkif_destroyed; } /** -@@ -595,13 +606,15 @@ __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, +@@ -595,18 +615,20 @@ __tapdisk_xenblkif_request_cb(struct td_vbd_request * const vreq, req = container_of(vreq, struct td_xenblkif_req, vreq); if (error) { @@ -165,7 +179,13 @@ index 028e4bf6..1b7bd7ee 100644 } -@@ -625,6 +638,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, + static inline int +-tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, ++tapdisk_xenblkif_parse_request_locked(struct td_xenblkif * const blkif, + struct td_xenblkif_req * const req) + { + td_vbd_request_t *vreq; +@@ -625,6 +647,7 @@ tapdisk_xenblkif_parse_request(struct td_xenblkif * const blkif, req->vma = td_xenblkif_bufcache_get(blkif); if (unlikely(!req->vma)) { err = errno; @@ -173,19 +193,28 @@ index 028e4bf6..1b7bd7ee 100644 goto out; } -@@ -779,8 +793,10 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, +@@ -732,6 +755,7 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, + { + int err = 0; + td_vbd_request_t *vreq; ++ bool blkif_freed = false; + + ASSERT(req); + +@@ -779,8 +803,10 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, goto out; } - if (likely(req->msg.nr_segments)) +- err = tapdisk_xenblkif_parse_request(blkif, req); + if (likely(req->msg.nr_segments)) { + pthread_mutex_lock(&blkif->mutex); - err = tapdisk_xenblkif_parse_request(blkif, req); ++ err = tapdisk_xenblkif_parse_request_locked(blkif, req); + pthread_mutex_unlock(&blkif->mutex); /* * If we only got one request from the ring and that was a barrier one, * check whether the barrier requests completion conditions are satisfied -@@ -789,10 +805,14 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, +@@ -789,10 +815,15 @@ tapdisk_xenblkif_make_vbd_request(struct td_xenblkif * const blkif, * It could be that there are more requests in the ring after the barrier * request, tapdisk_xenblkif_complete_request() will schedule a ring check. */ @@ -196,29 +225,45 @@ index 028e4bf6..1b7bd7ee 100644 + } else { + pthread_mutex_lock(&blkif->mutex); + if (tapdisk_xenblkif_barrier_should_complete(blkif)) { -+ tapdisk_xenblkif_complete_request(blkif, ++ blkif_freed = tapdisk_xenblkif_complete_request(blkif, + msg_to_tapreq(blkif->barrier.msg), 0, 1, false); + err = 0; + } -+ pthread_mutex_unlock(&blkif->mutex); ++ if (!blkif_freed) ++ pthread_mutex_unlock(&blkif->mutex); } out: return err; -@@ -876,7 +896,7 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, +@@ -857,12 +888,13 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, + int i; + int err; + int nr_errors = 0; ++ bool blkif_freed = false; + + ASSERT(blkif); + ASSERT(reqs); + ASSERT(nr_reqs >= 0); + +- for (i = 0; i < nr_reqs; i++) { /* for each request in the ring... */ ++ for (i = 0; i < nr_reqs && !blkif_freed; i++) { /* for each request in the ring... */ + blkif_request_t *msg = reqs[i]; + struct td_xenblkif_req *req; + +@@ -876,7 +908,7 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, if (err) { /* TODO log error */ nr_errors++; - tapdisk_xenblkif_complete_request(blkif, req, err, 1); -+ tapdisk_xenblkif_complete_request(blkif, req, err, 1, true); ++ blkif_freed = tapdisk_xenblkif_complete_request(blkif, req, err, 1, true); } } -@@ -884,8 +904,11 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, +@@ -884,8 +916,11 @@ tapdisk_xenblkif_queue_requests(struct td_xenblkif * const blkif, dead and current request is the last one, hence adding this check to avoid seg fault */ - if (nr_errors && blkif) -+ if (nr_errors && blkif) { ++ if (nr_errors && !blkif_freed) { + pthread_mutex_lock(&blkif->mutex); xenio_blkif_put_response(blkif, NULL, 0, 1); + pthread_mutex_unlock(&blkif->mutex); @@ -226,7 +271,7 @@ index 028e4bf6..1b7bd7ee 100644 } void -@@ -905,6 +928,7 @@ tapdisk_xenblkif_reqs_free(struct td_xenblkif * const blkif) +@@ -905,6 +940,7 @@ tapdisk_xenblkif_reqs_free(struct td_xenblkif * const blkif) free(blkif->reqs_free); blkif->reqs_free = NULL; @@ -234,7 +279,7 @@ index 028e4bf6..1b7bd7ee 100644 } int -@@ -916,6 +940,8 @@ tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif) +@@ -916,6 +952,8 @@ tapdisk_xenblkif_reqs_init(struct td_xenblkif *td_blkif) ASSERT(td_blkif); diff --git a/SOURCES/0030-tapdisk-protect-scheduler-structure.patch b/SOURCES/0030-tapdisk-protect-scheduler-structure.patch index 5badb62..65c0a11 100644 --- a/SOURCES/0030-tapdisk-protect-scheduler-structure.patch +++ b/SOURCES/0030-tapdisk-protect-scheduler-structure.patch @@ -1,4 +1,4 @@ -From 68feaf7278cffcbe4fe9ad338a1118d63c4a23e2 Mon Sep 17 00:00:00 2001 +From 206d4e826838aa86c3df6f611f75896585a68919 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 3 Apr 2025 16:06:24 +0200 Subject: [PATCH] tapdisk: protect scheduler structure diff --git a/SOURCES/0031-mocka-fix-scheduler-tests-according-mutex-protection.patch b/SOURCES/0031-mocka-fix-scheduler-tests-according-mutex-protection.patch index 079d274..329868c 100644 --- a/SOURCES/0031-mocka-fix-scheduler-tests-according-mutex-protection.patch +++ b/SOURCES/0031-mocka-fix-scheduler-tests-according-mutex-protection.patch @@ -1,4 +1,4 @@ -From 35dc5156cf69f3f11ba9e1cc06a83e6582759b88 Mon Sep 17 00:00:00 2001 +From a509127f18e7290830efdc59b78ef5ccda06aad0 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Wed, 21 Jan 2026 17:25:29 +0100 Subject: [PATCH] mocka: fix scheduler tests according mutex protection diff --git a/SOURCES/0032-tapdisk-protect-td_nbdserver-structures.patch b/SOURCES/0032-tapdisk-protect-td_nbdserver-structures.patch index f8e8452..3c77a60 100644 --- a/SOURCES/0032-tapdisk-protect-td_nbdserver-structures.patch +++ b/SOURCES/0032-tapdisk-protect-td_nbdserver-structures.patch @@ -1,4 +1,4 @@ -From d1a9950bc7c8bec5e7095748b8ee4a3c4e93f169 Mon Sep 17 00:00:00 2001 +From 3ed6dd2c64cf0f1dfe45845e9279dd9e3b2a78b5 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Fri, 17 Oct 2025 15:14:44 +0200 Subject: [PATCH] tapdisk: protect td_nbdserver structures @@ -12,10 +12,10 @@ Signed-off-by: Anthoine Bourgeois --- drivers/tapdisk-nbdserver.c | 55 +++++++++++++++++++++++++++++++++---- drivers/tapdisk-nbdserver.h | 2 ++ - 2 files changed, 51 insertions(+), 6 deletions(-) + 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/drivers/tapdisk-nbdserver.c b/drivers/tapdisk-nbdserver.c -index 23f6e241..8d4f9269 100644 +index 23f6e241..66657c35 100644 --- a/drivers/tapdisk-nbdserver.c +++ b/drivers/tapdisk-nbdserver.c @@ -235,10 +235,13 @@ tapdisk_nbdserver_alloc_request(td_nbdserver_client_t *client) @@ -41,7 +41,7 @@ index 23f6e241..8d4f9269 100644 return req; } -@@ -318,17 +321,22 @@ void +@@ -318,17 +321,23 @@ void tapdisk_nbdserver_free_request(td_nbdserver_client_t *client, td_nbdserver_req_t *req, bool free_client_if_dead) { @@ -58,14 +58,15 @@ index 23f6e241..8d4f9269 100644 client->dead && - !tapdisk_nbdserver_reqs_pending(client))) + !tapdisk_nbdserver_reqs_pending(client))) { -+ pthread_mutex_lock(&client->server->mutex); ++ td_nbdserver_t *server = client->server; ++ pthread_mutex_lock(&server->mutex); tapdisk_nbdserver_free_client(client); -+ pthread_mutex_lock(&client->server->mutex); ++ pthread_mutex_unlock(&server->mutex); + } } static void -@@ -800,6 +808,7 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) +@@ -800,6 +809,7 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) strerror(errno)); goto fail; } @@ -73,7 +74,7 @@ index 23f6e241..8d4f9269 100644 err = tapdisk_nbdserver_reqs_init(client, NBD_SERVER_NUM_REQS); if (err < 0) { -@@ -810,8 +819,10 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) +@@ -810,8 +820,10 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) client->client_fd = -1; client->client_event_id = -1; client->server = server; @@ -84,7 +85,7 @@ index 23f6e241..8d4f9269 100644 client->paused = 0; client->dead = false; -@@ -820,8 +831,10 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) +@@ -820,8 +832,10 @@ tapdisk_nbdserver_alloc_client(td_nbdserver_t *server) return client; fail: @@ -96,16 +97,15 @@ index 23f6e241..8d4f9269 100644 return NULL; } -@@ -840,7 +853,7 @@ tapdisk_nbdserver_free_client(td_nbdserver_client_t *client) - +@@ -841,6 +855,7 @@ tapdisk_nbdserver_free_client(td_nbdserver_client_t *client) if (likely(!tapdisk_nbdserver_reqs_pending(client))) { list_del(&client->clientlist); -- tapdisk_nbdserver_reqs_free(client); + tapdisk_nbdserver_reqs_free(client); + pthread_mutex_destroy(&client->mutex); free(client); } else client->dead = true; -@@ -1054,7 +1067,9 @@ tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) +@@ -1054,7 +1069,9 @@ tapdisk_nbdserver_handshake_cb(event_id_t id, char mode, void *data) if (tapdisk_nbdserver_enable_client(client) < 0) { ERR("Error enabling client"); tmp_fd = client->client_fd; @@ -115,7 +115,7 @@ index 23f6e241..8d4f9269 100644 close(tmp_fd); } -@@ -1136,7 +1151,9 @@ tapdisk_nbdserver_newclient_fd_old(td_nbdserver_t *server, int new_fd) +@@ -1136,7 +1153,9 @@ tapdisk_nbdserver_newclient_fd_old(td_nbdserver_t *server, int new_fd) INFO("About to enable client on fd %d", client->client_fd); if (tapdisk_nbdserver_enable_client(client) < 0) { ERR("Error enabling client"); @@ -125,7 +125,7 @@ index 23f6e241..8d4f9269 100644 close(new_fd); } } -@@ -1164,7 +1181,9 @@ tapdisk_nbdserver_newclient_fd_new_fixed(td_nbdserver_t *server, int new_fd) +@@ -1164,7 +1183,9 @@ tapdisk_nbdserver_newclient_fd_new_fixed(td_nbdserver_t *server, int new_fd) if(tapdisk_nbdserver_new_protocol_handshake(client, new_fd) != 0) { ERR("Error handshaking new client connection"); @@ -135,7 +135,7 @@ index 23f6e241..8d4f9269 100644 close(new_fd); return; } -@@ -1290,7 +1309,9 @@ tapdisk_nbdserver_clientcb(event_id_t id, char mode, void *data) +@@ -1290,7 +1311,9 @@ tapdisk_nbdserver_clientcb(event_id_t id, char mode, void *data) break; case TAPDISK_NBD_CMD_DISC: INFO("Received close message. Sending reconnect header"); @@ -145,7 +145,7 @@ index 23f6e241..8d4f9269 100644 INFO("About to send initial connection message"); tapdisk_nbdserver_newclient_fd(server, fd); INFO("Sent initial connection message"); -@@ -1338,7 +1359,9 @@ fail: +@@ -1338,7 +1361,9 @@ fail: if (vreq) tapdisk_nbd_server_free_vreq(client, vreq, false); close(client->client_fd); @@ -155,7 +155,7 @@ index 23f6e241..8d4f9269 100644 return; } -@@ -1431,7 +1454,10 @@ tapdisk_nbdserver_alloc(td_vbd_t *vbd, td_disk_info_t info, nbd_protocol_style_t +@@ -1431,7 +1456,10 @@ tapdisk_nbdserver_alloc(td_vbd_t *vbd, td_disk_info_t info, nbd_protocol_style_t server->unix_listening_fd = -1; server->unix_listening_event_id = -1; server->style = style; @@ -166,7 +166,7 @@ index 23f6e241..8d4f9269 100644 switch (style) { case TAPDISK_NBD_PROTOCOL_OLD: -@@ -1479,6 +1505,7 @@ fail: +@@ -1479,6 +1507,7 @@ fail: if (server) { if (server->fdreceiver) td_fdreceiver_stop(server->fdreceiver); @@ -174,7 +174,7 @@ index 23f6e241..8d4f9269 100644 free(server); } -@@ -1494,12 +1521,14 @@ tapdisk_nbdserver_pause(td_nbdserver_t *server, bool log) +@@ -1494,12 +1523,14 @@ tapdisk_nbdserver_pause(td_nbdserver_t *server, bool log) INFO("NBD server pause(%p)", server); } @@ -189,7 +189,7 @@ index 23f6e241..8d4f9269 100644 if (server->fdrecv_listening_event_id >= 0) { tapdisk_server_unregister_event(server->fdrecv_listening_event_id); -@@ -1732,15 +1761,18 @@ tapdisk_nbdserver_unpause(td_nbdserver_t *server) +@@ -1732,15 +1763,18 @@ tapdisk_nbdserver_unpause(td_nbdserver_t *server) "unix_listening_fd=%d", server, server->fdrecv_listening_fd, server->unix_listening_fd); @@ -208,7 +208,7 @@ index 23f6e241..8d4f9269 100644 err = tapdisk_nbdserver_unpause_fdrecv(server); if (err) -@@ -1761,8 +1793,10 @@ tapdisk_nbdserver_free(td_nbdserver_t *server) +@@ -1761,8 +1795,10 @@ tapdisk_nbdserver_free(td_nbdserver_t *server) INFO("NBD server free(%p)", server); @@ -219,7 +219,7 @@ index 23f6e241..8d4f9269 100644 if (server->fdrecv_listening_event_id >= 0) { tapdisk_server_unregister_event(server->fdrecv_listening_event_id); -@@ -1796,15 +1830,20 @@ tapdisk_nbdserver_free(td_nbdserver_t *server) +@@ -1796,15 +1832,20 @@ tapdisk_nbdserver_free(td_nbdserver_t *server) if (err) ERR("failed to delete NBD metrics: %s\n", strerror(errno)); @@ -241,7 +241,7 @@ index 23f6e241..8d4f9269 100644 } bool -@@ -1815,8 +1854,12 @@ tapdisk_nbdserver_contains_client(td_nbdserver_t *server, +@@ -1815,8 +1856,12 @@ tapdisk_nbdserver_contains_client(td_nbdserver_t *server, ASSERT(server); diff --git a/SOURCES/0033-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch b/SOURCES/0033-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch index 47f2f92..30461fb 100644 --- a/SOURCES/0033-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch +++ b/SOURCES/0033-libqcow2-prepare-proper-cleanup-of-libqcow2-on-close.patch @@ -1,4 +1,4 @@ -From 2e1911694415cf75617256838024de8a96e9169d Mon Sep 17 00:00:00 2001 +From 625aab02193c7af95edc4ba99dfd12d0b0b6b8c1 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Wed, 12 Mar 2025 13:46:25 +0100 Subject: [PATCH] libqcow2: prepare proper cleanup of libqcow2 on close @@ -9,12 +9,12 @@ clean-up resources to prepare the reopen or unpause. Signed-off-by: Anthoine Bourgeois --- include/qemu/main-loop.h | 2 ++ - qcow2/lib/system/cpus.c | 5 +++++ - qcow2/lib/util/main-loop.c | 45 +++++++++++++++++++++++++++++++++++++- - 3 files changed, 51 insertions(+), 1 deletion(-) + qcow2/lib/system/cpus.c | 5 ++++ + qcow2/lib/util/main-loop.c | 49 +++++++++++++++++++++++++++++++++++++- + 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h -index e0a0be44..9a6f11a8 100644 +index feab3b96..c2983f59 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -42,6 +42,7 @@ typedef struct MainLoop MainLoop; @@ -34,7 +34,7 @@ index e0a0be44..9a6f11a8 100644 /** * main_loop_wait: Run one iteration of the main loop. diff --git a/qcow2/lib/system/cpus.c b/qcow2/lib/system/cpus.c -index 81236835..fd3f991c 100644 +index ed2b9ff5..dbf0b3dc 100644 --- a/qcow2/lib/system/cpus.c +++ b/qcow2/lib/system/cpus.c @@ -429,6 +429,11 @@ void qemu_init_cpu_loop(void) @@ -50,7 +50,7 @@ index 81236835..fd3f991c 100644 void run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data) { diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c -index 817e65a6..9b212ff4 100644 +index 817e65a6..8bde8add 100644 --- a/qcow2/lib/util/main-loop.c +++ b/qcow2/lib/util/main-loop.c @@ -39,8 +39,12 @@ @@ -84,12 +84,12 @@ index 817e65a6..9b212ff4 100644 + + src = iohandler_get_g_source(); + g_source_unref(src); -+ g_source_remove(g_source_get_id(src)); ++ g_source_destroy(src); + g_source_unref(src); + + src = aio_get_g_source(qemu_aio_context); + g_source_unref(src); -+ g_source_remove(g_source_get_id(src)); ++ g_source_destroy(src); + g_source_unref(src); + + g_array_free(gpollfds, TRUE); @@ -114,13 +114,17 @@ index 817e65a6..9b212ff4 100644 #if 0 static void main_loop_update_params(EventLoopBase *base, Error **errp) { -@@ -633,6 +669,13 @@ static void iohandler_init(void) +@@ -633,6 +669,17 @@ static void iohandler_init(void) } } +static void iohandler_deinit(void) +{ + if (iohandler_ctx) { ++ /* ++ * iohandler_ctx will be freed by the callback aio_ctx_finalize, ++ * but we need to set it to null to reallocate a new one in the future. ++ */ + iohandler_ctx = NULL; + } +} diff --git a/SOURCES/0034-libqcow2-mask-signals-used-by-tapdisk.patch b/SOURCES/0034-libqcow2-mask-signals-used-by-tapdisk.patch index 47b05a7..7de7254 100644 --- a/SOURCES/0034-libqcow2-mask-signals-used-by-tapdisk.patch +++ b/SOURCES/0034-libqcow2-mask-signals-used-by-tapdisk.patch @@ -1,4 +1,4 @@ -From 74a73e8eaa5262a189a8e85c97dccab4e95a20e8 Mon Sep 17 00:00:00 2001 +From cc2b5528d52cc78d5cba25fbd5ee9a07149fc155 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Wed, 15 Oct 2025 14:35:21 +0200 Subject: [PATCH] libqcow2: mask signals used by tapdisk @@ -12,7 +12,7 @@ Signed-off-by: Anthoine Bourgeois 1 file changed, 10 insertions(+) diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c -index 9b212ff4..82d737fe 100644 +index 8bde8add..227caf58 100644 --- a/qcow2/lib/util/main-loop.c +++ b/qcow2/lib/util/main-loop.c @@ -102,6 +102,11 @@ static int qemu_signal_init(Error **errp) diff --git a/SOURCES/0035-tapdisk-replace-signals-handling-by-signalfd.patch b/SOURCES/0035-tapdisk-replace-signals-handling-by-signalfd.patch index cc83a2b..c4df7d9 100644 --- a/SOURCES/0035-tapdisk-replace-signals-handling-by-signalfd.patch +++ b/SOURCES/0035-tapdisk-replace-signals-handling-by-signalfd.patch @@ -1,4 +1,4 @@ -From 7cd6427151a6f9afbc04f93525b6a7b8751bb9e8 Mon Sep 17 00:00:00 2001 +From 2d8405db2f9aec38c0c4db856e2dbdc601a56eee Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Fri, 17 Oct 2025 18:10:41 +0200 Subject: [PATCH] tapdisk: replace signals handling by signalfd diff --git a/SOURCES/0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch b/SOURCES/0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch index 15a58c7..22aebce 100644 --- a/SOURCES/0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch +++ b/SOURCES/0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch @@ -1,4 +1,4 @@ -From 9561c2c6b30a9eed55fe1fe24721956ede41f6fe Mon Sep 17 00:00:00 2001 +From 7cce89a9e5a1456ebaa45586c982f22c01570032 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 16 Jan 2025 14:09:50 +0100 Subject: [PATCH] qcow2: driver: support qcow2 images in tapdisk @@ -12,9 +12,9 @@ Signed-off-by: Anthoine Bourgeois Signed-off-by: Emmanuel Varagnat --- drivers/Makefile.am | 4 + - drivers/block-qcow2.c | 882 +++++++++++++++++++++++++++++++++++++ + drivers/block-qcow2.c | 869 +++++++++++++++++++++++++++++++++++++ drivers/tapdisk-disktype.c | 8 +- - 3 files changed, 888 insertions(+), 6 deletions(-) + 3 files changed, 875 insertions(+), 6 deletions(-) create mode 100644 drivers/block-qcow2.c diff --git a/drivers/Makefile.am b/drivers/Makefile.am @@ -54,10 +54,10 @@ index c03ef7ed..70bfd2d5 100644 lib_LTLIBRARIES = libblockcrypto.la diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c new file mode 100644 -index 00000000..6fc3cc6a +index 00000000..99c0e7e2 --- /dev/null +++ b/drivers/block-qcow2.c -@@ -0,0 +1,882 @@ +@@ -0,0 +1,869 @@ +/* + * Copyright (c) 2024, Vates + * @@ -586,9 +586,6 @@ index 00000000..6fc3cc6a + return 0; + } + -+ -+ /* TODO: compare sizes */ -+ + return 0; +} + @@ -633,11 +630,12 @@ index 00000000..6fc3cc6a +free_qcow2_request(struct qcow2_state *s, struct qcow2_request *req) +{ + memset(&req->treq, 0, sizeof(req->treq)); ++ ++ qemu_iovec_reset(&req->qiov); ++ + pthread_mutex_lock(&s->lock); + s->vreq_free[s->vreq_free_count++] = req; + pthread_mutex_unlock(&s->lock); -+ -+ qemu_iovec_reset(&req->qiov); +} + +static inline void @@ -794,7 +792,9 @@ index 00000000..6fc3cc6a + break; + + default: -+ ASSERT(0); ++ ERR(s, req->error, "%s: unknown op: %u", ++ req->treq.image->name, req->op); ++ ASSERT(0 && req->op); + break; + } +} @@ -865,47 +865,34 @@ index 00000000..6fc3cc6a +} + +static void -+qcow2_queue_block_status(td_driver_t *driver, td_request_t treq) -+{ -+ treq.status = TD_BLOCK_STATE_NONE; -+} -+ -+static void -+qcow2_queue_read(td_driver_t *driver, td_request_t treq) ++qcow2_queue(td_driver_t *driver, td_request_t *treq, int op) +{ + struct qcow2_state *s = (struct qcow2_state *)driver->data; + int err; + + DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", secs: 0x%04x (seg: %d)\n", -+ treq.image->name, treq.sec, treq.secs, treq.sidx); ++ treq->image->name, treq->sec, treq->secs, treq->sidx); + -+ err = schedule_request(s, &treq, QCOW2_OP_READ); ++ err = schedule_request(s, treq, op); + if (err) + goto fail; + + return; +fail: + DBG(TLOG_DBG, "request failed\n"); -+ td_complete_request(treq, err); ++ td_complete_request(*treq, err); +} + +static void -+qcow2_queue_write(td_driver_t *driver, td_request_t treq) ++qcow2_queue_read(td_driver_t *driver, td_request_t treq) +{ -+ struct qcow2_state *s = (struct qcow2_state *)driver->data; -+ int err; -+ -+ DBG(TLOG_DBG, "%s: lsec: 0x%08"PRIx64", secs: 0x%04x, (seg: %d)\n", -+ treq.image->name, treq.sec, treq.secs, treq.sidx); -+ -+ err = schedule_request(s, &treq, QCOW2_OP_WRITE); -+ if (err) -+ goto fail; ++ qcow2_queue(driver, &treq, QCOW2_OP_READ); ++} + -+ return; -+fail: -+ DBG(TLOG_DBG, "request failed\n"); -+ td_complete_request(treq, err); ++static void ++qcow2_queue_write(td_driver_t *driver, td_request_t treq) ++{ ++ qcow2_queue(driver, &treq, QCOW2_OP_WRITE); +} + +void @@ -934,14 +921,14 @@ index 00000000..6fc3cc6a + .td_open = _qcow2_open, + .td_close = _qcow2_close, + .td_queue_read = qcow2_queue_read, -+ .td_queue_block_status = qcow2_queue_block_status, ++ .td_queue_block_status = NULL, + .td_queue_write = qcow2_queue_write, + .td_get_parent_id = qcow2_get_parent_id, + .td_validate_parent = qcow2_validate_parent, + .td_debug = qcow2_debug, +}; diff --git a/drivers/tapdisk-disktype.c b/drivers/tapdisk-disktype.c -index 4a60c756..40bfcce5 100644 +index 8ab96c4f..71678045 100644 --- a/drivers/tapdisk-disktype.c +++ b/drivers/tapdisk-disktype.c @@ -77,8 +77,8 @@ static const disk_info_t ram_disk = { diff --git a/SOURCES/0037-vbd-wake-up-scheduler-to-force-check-ring.patch b/SOURCES/0037-vbd-wake-up-scheduler-to-force-check-ring.patch index 23e2dda..96d589f 100644 --- a/SOURCES/0037-vbd-wake-up-scheduler-to-force-check-ring.patch +++ b/SOURCES/0037-vbd-wake-up-scheduler-to-force-check-ring.patch @@ -1,4 +1,4 @@ -From 260a69aec99546b332596c3fce53301eba95284c Mon Sep 17 00:00:00 2001 +From 638fa45ef2e12170997f50899ce1ebe42662d5f7 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Wed, 8 Oct 2025 14:05:35 +0200 Subject: [PATCH] vbd: wake-up scheduler to force check ring @@ -13,16 +13,16 @@ Signed-off-by: Anthoine Bourgeois --- drivers/block-qcow2.c | 4 +-- drivers/tapdisk-server.c | 2 +- - drivers/tapdisk-vbd.c | 68 +++++++++++++++++++++++++++++++++++++++- + drivers/tapdisk-vbd.c | 71 +++++++++++++++++++++++++++++++++++++++- drivers/tapdisk-vbd.h | 6 +++- drivers/tapdisk.h | 2 ++ - 5 files changed, 77 insertions(+), 5 deletions(-) + 5 files changed, 80 insertions(+), 5 deletions(-) diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c -index 6fc3cc6a..b8b74a67 100644 +index 99c0e7e2..454c3bf5 100644 --- a/drivers/block-qcow2.c +++ b/drivers/block-qcow2.c -@@ -590,7 +590,7 @@ signal_completion(struct qcow2_request *r) +@@ -588,7 +588,7 @@ signal_completion(struct qcow2_request *r) DBG(TLOG_DBG, "lsec: 0x%08"PRIx64", blk: 0x%04x, " "err: %d\n", r->treq.sec, r->treq.secs, r->error); if (r->error == 0) { @@ -31,7 +31,7 @@ index 6fc3cc6a..b8b74a67 100644 s->kick++; } free_qcow2_request(s, r); -@@ -869,7 +869,7 @@ qcow2_debug(td_driver_t *driver) +@@ -856,7 +856,7 @@ qcow2_debug(td_driver_t *driver) struct tap_disk tapdisk_qcow = { .disk_type = "tapdisk_qcow2", @@ -54,7 +54,7 @@ index bf426735..812ecd90 100644 static void diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c -index 32131f31..02f0dab0 100644 +index 6e1f95d3..a71207a7 100644 --- a/drivers/tapdisk-vbd.c +++ b/drivers/tapdisk-vbd.c @@ -39,10 +39,12 @@ @@ -92,7 +92,7 @@ index 32131f31..02f0dab0 100644 pthread_mutex_unlock(&vbd->mutex); } -@@ -576,6 +586,23 @@ fail: +@@ -576,6 +586,24 @@ fail: return err; } @@ -106,17 +106,18 @@ index 32131f31..02f0dab0 100644 + + pthread_mutex_lock(&vbd->mutex); + if (vbd->efd < 0) -+ return; ++ goto unlock; + + s = read(vbd->efd, &u, sizeof(uint64_t)); + ASSERT(s == sizeof(uint64_t)); ++unlock: + pthread_mutex_unlock(&vbd->mutex); +} + int tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_devnum) { -@@ -604,6 +631,26 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d +@@ -604,6 +632,26 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d if (err) goto fail; @@ -143,7 +144,7 @@ index 32131f31..02f0dab0 100644 td_flag_clear(vbd->state, TD_VBD_CLOSED); vbd->flags = flags; -@@ -660,6 +707,14 @@ fail: +@@ -660,6 +708,14 @@ fail: free(vbd->name); vbd->name = tmp; } @@ -158,7 +159,7 @@ index 32131f31..02f0dab0 100644 if (!list_empty(&vbd->images)) tapdisk_image_close_chain(&vbd->images); -@@ -1862,10 +1917,11 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) +@@ -1869,10 +1925,11 @@ tapdisk_vbd_queue_request(td_vbd_t *vbd, td_vbd_request_t *vreq) } void @@ -169,9 +170,9 @@ index 32131f31..02f0dab0 100644 td_vbd_request_t *vreq, *prev, *next; + ssize_t s; + pthread_mutex_lock(&vbd->mutex); vbd->kicked++; - -@@ -1900,6 +1956,16 @@ tapdisk_vbd_kick(td_vbd_t *vbd) +@@ -1907,6 +1964,18 @@ tapdisk_vbd_kick(td_vbd_t *vbd) prev->cb(prev, prev->error, prev->token, 1); vbd->returned++; } @@ -179,8 +180,10 @@ index 32131f31..02f0dab0 100644 + if (scheduler_kick && td_flag_test(vbd->driver_flags, TD_DRIVER_THREADED)) { + static uint64_t token = 1; + -+ if (vbd->efd < 0) ++ if (vbd->efd < 0) { ++ pthread_mutex_unlock(&vbd->mutex); + return; ++ } + + s = write(vbd->efd, &token, sizeof(uint64_t)); + ASSERT(s == sizeof(uint64_t)); @@ -189,7 +192,7 @@ index 32131f31..02f0dab0 100644 } diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h -index f3fa0398..42239094 100644 +index f3fa0398..eb2b1425 100644 --- a/drivers/tapdisk-vbd.h +++ b/drivers/tapdisk-vbd.h @@ -167,6 +167,10 @@ struct td_vbd_handle { @@ -198,7 +201,7 @@ index f3fa0398..42239094 100644 bool watchdog_warned; + + td_flag_t driver_flags; -+ int efd; ++ int efd; + event_id_t event; }; @@ -213,7 +216,7 @@ index f3fa0398..42239094 100644 void tapdisk_vbd_free(td_vbd_t *); diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h -index 3163e190..59e9ee64 100644 +index d907d149..7e96b5d2 100644 --- a/drivers/tapdisk.h +++ b/drivers/tapdisk.h @@ -115,6 +115,8 @@ enum TD_OPS{ diff --git a/SOURCES/0038-blktap.spec-add-qcow2-dependencies.patch b/SOURCES/0038-blktap.spec-add-qcow2-dependencies.patch deleted file mode 100644 index 8623898..0000000 --- a/SOURCES/0038-blktap.spec-add-qcow2-dependencies.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 6f4a8aec3fe5b10a7446a529586a6ba5d921f7b0 Mon Sep 17 00:00:00 2001 -From: Anthoine Bourgeois -Date: Thu, 16 Jan 2025 14:09:54 +0100 -Subject: [PATCH] blktap.spec: add qcow2 dependencies - -Signed-off-by: Anthoine Bourgeois ---- - mk/blktap.spec.in | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/mk/blktap.spec.in b/mk/blktap.spec.in -index 2da6fe2c..14fea3c0 100644 ---- a/mk/blktap.spec.in -+++ b/mk/blktap.spec.in -@@ -11,6 +11,7 @@ BuildRoot: %{_tmppath}/%{name}-%{release}-buildroot - Obsoletes: xen-blktap - BuildRequires: e2fsprogs-devel, libaio-devel, systemd, autogen, autoconf, automake, libtool, libuuid-devel - BuildRequires: xen-devel, kernel-headers, xen-dom0-libs-devel, zlib-devel, xen-libs-devel, libcmocka-devel, lcov, git -+BuildRequires: glib2, glib2-devel, gnutls, gnutls-devel, libzstd, libzstd-devel - BuildRequires: openssl-devel >= 1.1.1 - Requires(post): systemd - Requires(preun): systemd diff --git a/SOURCES/0039-tapdisk-support-new-commit-command.patch b/SOURCES/0038-tapdisk-support-new-commit-command.patch similarity index 97% rename from SOURCES/0039-tapdisk-support-new-commit-command.patch rename to SOURCES/0038-tapdisk-support-new-commit-command.patch index c4f8c9a..081ffa1 100644 --- a/SOURCES/0039-tapdisk-support-new-commit-command.patch +++ b/SOURCES/0038-tapdisk-support-new-commit-command.patch @@ -1,4 +1,4 @@ -From 00e7a92af1a19b9edb80a4e23f63fda677e47c70 Mon Sep 17 00:00:00 2001 +From a6242df1cb58d476b2cd376365ec1d4a7e74062c Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Mon, 10 Feb 2025 16:45:05 +0100 Subject: [PATCH] tapdisk: support new commit command @@ -36,12 +36,12 @@ index 6ca88ab7..5f954c92 100644 diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c new file mode 100644 -index 00000000..e5d8c1f5 +index 00000000..e3b30853 --- /dev/null +++ b/control/tap-ctl-commit.c @@ -0,0 +1,76 @@ +/* -+ * Copyright (c) 2016, Citrix Systems, Inc. ++ * Copyright (c) 2025, Vates + * + * All rights reserved. + * @@ -284,10 +284,10 @@ index 437feb58..076528ea 100644 void td_queue_write(td_image_t *, td_request_t); void td_queue_read(td_image_t *, td_request_t); diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c -index 02f0dab0..3cb38bf3 100644 +index a71207a7..98a24db8 100644 --- a/drivers/tapdisk-vbd.c +++ b/drivers/tapdisk-vbd.c -@@ -1132,6 +1132,22 @@ resume_failed: +@@ -1133,6 +1133,22 @@ resume_failed: return 0; } @@ -311,7 +311,7 @@ index 02f0dab0..3cb38bf3 100644 tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, const struct timeval *now) diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h -index 42239094..aaffb073 100644 +index eb2b1425..909c2549 100644 --- a/drivers/tapdisk-vbd.h +++ b/drivers/tapdisk-vbd.h @@ -230,6 +230,7 @@ int tapdisk_vbd_resume(td_vbd_t *, const char *); @@ -323,7 +323,7 @@ index 42239094..aaffb073 100644 void tapdisk_vbd_complete_td_request(td_request_t, int); int add_extent(tapdisk_extents_t *, td_request_t *); diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h -index 59e9ee64..f43b6402 100644 +index 7e96b5d2..482c323e 100644 --- a/drivers/tapdisk.h +++ b/drivers/tapdisk.h @@ -228,6 +228,7 @@ struct tap_disk { diff --git a/SOURCES/0040-qcow2-support-commit-command.patch b/SOURCES/0039-qcow2-support-commit-command.patch similarity index 88% rename from SOURCES/0040-qcow2-support-commit-command.patch rename to SOURCES/0039-qcow2-support-commit-command.patch index cbac381..c06bf02 100644 --- a/SOURCES/0040-qcow2-support-commit-command.patch +++ b/SOURCES/0039-qcow2-support-commit-command.patch @@ -1,4 +1,4 @@ -From a971cc350e12c45949c5aaaa6caed1cb54b556ef Mon Sep 17 00:00:00 2001 +From 2c453575368fef852682fbd90047e6a5390cfb47 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Mon, 10 Feb 2025 18:54:25 +0100 Subject: [PATCH] qcow2: support commit command @@ -7,11 +7,11 @@ This command will coalesce the disk in parameter with its parent. Signed-off-by: Anthoine Bourgeois --- - drivers/block-qcow2.c | 112 +++++++++++++++++++++++++++++++++++++++++- - 1 file changed, 111 insertions(+), 1 deletion(-) + drivers/block-qcow2.c | 121 +++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 120 insertions(+), 1 deletion(-) diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c -index b8b74a67..bceaf863 100644 +index 454c3bf5..4e242237 100644 --- a/drivers/block-qcow2.c +++ b/drivers/block-qcow2.c @@ -60,6 +60,8 @@ @@ -107,8 +107,8 @@ index b8b74a67..bceaf863 100644 err = pthread_cond_destroy(&s->cond); if (err) { EPRINTF("failed to destroy thread condition %d\n", err); -@@ -848,6 +883,80 @@ fail: - td_complete_request(treq, err); +@@ -835,6 +870,89 @@ qcow2_queue_write(td_driver_t *driver, td_request_t treq) + qcow2_queue(driver, &treq, QCOW2_OP_WRITE); } +int @@ -162,8 +162,16 @@ index b8b74a67..bceaf863 100644 + } else { + top_bs = bdrv_find_backing_image(bs, req->top); + } ++ if (top_bs == NULL) { ++ DPRINTF("qcow2_commit: top '%s' doesn't exist\n", req->top); ++ goto signal_commit; ++ } + top_node = top_bs->node_name; + base_bs = bdrv_backing_chain_next(top_bs); ++ if (base_bs == NULL) { ++ DPRINTF("qcow2_commit: no base to commit in\n"); ++ goto signal_commit; ++ } + base_node = base_bs->node_name; + + DBG(TLOG_DBG, "Qcow2: block commit %s (node-name: '%s').\n", req->top, node); @@ -171,7 +179,7 @@ index b8b74a67..bceaf863 100644 + + qmp_block_commit(COMMIT_JOB_ID, node, base_node, NULL, top_node, NULL, NULL, + false, false, false, 0, false, BLOCKDEV_ON_ERROR_REPORT, -+ NULL, false, false, true, false, &local_err); ++ NULL, true, true, true, false, &local_err); + + if (local_err) { + DPRINTF("qcow2_commit: error: %s\n", error_get_pretty(local_err)); @@ -179,6 +187,7 @@ index b8b74a67..bceaf863 100644 + err = -EINVAL; + } + ++signal_commit: + pthread_mutex_lock(&s->commit_lock); + req->error = err; + pthread_cond_signal(&s->commit_cond); @@ -188,7 +197,7 @@ index b8b74a67..bceaf863 100644 void qcow2_debug(td_driver_t *driver) { -@@ -878,5 +987,6 @@ struct tap_disk tapdisk_qcow = { +@@ -865,5 +983,6 @@ struct tap_disk tapdisk_qcow = { .td_queue_write = qcow2_queue_write, .td_get_parent_id = qcow2_get_parent_id, .td_validate_parent = qcow2_validate_parent, diff --git a/SOURCES/0041-tapdisk-support-new-query-command.patch b/SOURCES/0040-tapdisk-support-new-query-command.patch similarity index 97% rename from SOURCES/0041-tapdisk-support-new-query-command.patch rename to SOURCES/0040-tapdisk-support-new-query-command.patch index f0814b7..c807378 100644 --- a/SOURCES/0041-tapdisk-support-new-query-command.patch +++ b/SOURCES/0040-tapdisk-support-new-query-command.patch @@ -1,4 +1,4 @@ -From addb2dbcf41aae1a9648c6547b56d7b50dbb555d Mon Sep 17 00:00:00 2001 +From ccbf8e59540ec8a477b309b5f0a10c3bf60c1a7a Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 13 Feb 2025 08:54:48 +0100 Subject: [PATCH] tapdisk: support new query command @@ -22,7 +22,7 @@ Signed-off-by: Anthoine Bourgeois 10 files changed, 183 insertions(+), 1 deletion(-) diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c -index e5d8c1f5..eef6ea5f 100644 +index e3b30853..04985f69 100644 --- a/control/tap-ctl-commit.c +++ b/control/tap-ctl-commit.c @@ -74,3 +74,34 @@ tap_ctl_commit(const int id, const int minor, const char *params) @@ -228,10 +228,10 @@ index 076528ea..b3ff6e13 100644 void td_queue_write(td_image_t *, td_request_t); void td_queue_read(td_image_t *, td_request_t); diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c -index 3cb38bf3..7da25b6e 100644 +index 98a24db8..9b6d4d84 100644 --- a/drivers/tapdisk-vbd.c +++ b/drivers/tapdisk-vbd.c -@@ -1148,6 +1148,22 @@ tapdisk_vbd_commit(td_vbd_t *vbd, const char *name) +@@ -1149,6 +1149,22 @@ tapdisk_vbd_commit(td_vbd_t *vbd, const char *name) return err; } @@ -255,7 +255,7 @@ index 3cb38bf3..7da25b6e 100644 tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, const struct timeval *now) diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h -index aaffb073..fb948255 100644 +index 909c2549..af333736 100644 --- a/drivers/tapdisk-vbd.h +++ b/drivers/tapdisk-vbd.h @@ -231,6 +231,7 @@ void tapdisk_vbd_kick(td_vbd_t *, bool); @@ -267,7 +267,7 @@ index aaffb073..fb948255 100644 void tapdisk_vbd_complete_td_request(td_request_t, int); int add_extent(tapdisk_extents_t *, td_request_t *); diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h -index f43b6402..06891584 100644 +index 482c323e..cf1adabc 100644 --- a/drivers/tapdisk.h +++ b/drivers/tapdisk.h @@ -136,6 +136,7 @@ typedef struct td_image_handle td_image_t; diff --git a/SOURCES/0042-qcow2-support-query-command.patch b/SOURCES/0041-qcow2-support-query-command.patch similarity index 88% rename from SOURCES/0042-qcow2-support-query-command.patch rename to SOURCES/0041-qcow2-support-query-command.patch index ce26566..70e70c1 100644 --- a/SOURCES/0042-qcow2-support-query-command.patch +++ b/SOURCES/0041-qcow2-support-query-command.patch @@ -1,4 +1,4 @@ -From 9d6d40ddb38904e8859844acb27b20b69d231c53 Mon Sep 17 00:00:00 2001 +From b2f29e6773ec00179fdfb5a2bb020b41fcdffeab Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Thu, 13 Feb 2025 08:56:22 +0100 Subject: [PATCH] qcow2: support query command @@ -7,11 +7,11 @@ This command will print the status of the coalesce in background. Signed-off-by: Anthoine Bourgeois --- - drivers/block-qcow2.c | 105 ++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 105 insertions(+) + drivers/block-qcow2.c | 101 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 101 insertions(+) diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c -index bceaf863..37e7c324 100644 +index 4e242237..5c57233e 100644 --- a/drivers/block-qcow2.c +++ b/drivers/block-qcow2.c @@ -61,6 +61,7 @@ @@ -48,7 +48,7 @@ index bceaf863..37e7c324 100644 } pthread_mutex_lock(&s->lock); } -@@ -957,6 +963,104 @@ do_commit(struct qcow2_state *s, struct qcow2_request *req) +@@ -953,6 +959,100 @@ signal_commit: pthread_mutex_unlock(&s->commit_lock); } @@ -82,8 +82,6 @@ index bceaf863..37e7c324 100644 + query->total_progress = s->job_info.total_progress; + } + -+ memset(&s->job_info, 0, sizeof(JobInfo)); -+ + err = req->error; + pthread_mutex_unlock(&s->commit_lock); + @@ -100,8 +98,8 @@ index bceaf863..37e7c324 100644 + Error *local_err = NULL; + int err = 0; + BlockJob *bjob; -+ JobStatus status; -+ uint64_t current, total; ++ JobStatus status = JOB_STATUS_UNDEFINED; ++ uint64_t current = 0, total = 0; + + job_lock(); + bjob = block_job_get_locked(COMMIT_JOB_ID); @@ -137,14 +135,12 @@ index bceaf863..37e7c324 100644 + + DPRINTF("Qcow2: commit job '%s'.\n", JobStatus_str(status)); + ++signal: + pthread_mutex_lock(&s->commit_lock); + s->job_info.status = status; + s->job_info.current_progress = current; + s->job_info.total_progress = total; -+ pthread_mutex_unlock(&s->commit_lock); + -+signal: -+ pthread_mutex_lock(&s->commit_lock); + req->error = err; + pthread_cond_signal(&s->commit_cond); + pthread_mutex_unlock(&s->commit_lock); @@ -153,7 +149,7 @@ index bceaf863..37e7c324 100644 void qcow2_debug(td_driver_t *driver) { -@@ -988,5 +1092,6 @@ struct tap_disk tapdisk_qcow = { +@@ -984,5 +1084,6 @@ struct tap_disk tapdisk_qcow = { .td_get_parent_id = qcow2_get_parent_id, .td_validate_parent = qcow2_validate_parent, .td_commit = qcow2_commit, diff --git a/SOURCES/0043-tapdisk-support-new-cancel-command.patch b/SOURCES/0042-tapdisk-support-new-cancel-command.patch similarity index 97% rename from SOURCES/0043-tapdisk-support-new-cancel-command.patch rename to SOURCES/0042-tapdisk-support-new-cancel-command.patch index ba853a4..51f73bb 100644 --- a/SOURCES/0043-tapdisk-support-new-cancel-command.patch +++ b/SOURCES/0042-tapdisk-support-new-cancel-command.patch @@ -1,4 +1,4 @@ -From bab444b5fd7f67fdcf3cdc4b5a91f506dd97f336 Mon Sep 17 00:00:00 2001 +From ae49a1d06ca20863062f21e3dd1cf2729e4a2432 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Wed, 5 Mar 2025 18:09:16 +0100 Subject: [PATCH] tapdisk: support new cancel command @@ -21,7 +21,7 @@ Signed-off-by: Anthoine Bourgeois 10 files changed, 162 insertions(+) diff --git a/control/tap-ctl-commit.c b/control/tap-ctl-commit.c -index eef6ea5f..1bc111a1 100644 +index 04985f69..56549deb 100644 --- a/control/tap-ctl-commit.c +++ b/control/tap-ctl-commit.c @@ -105,3 +105,33 @@ tap_ctl_query_commit_job(const int id, const int minor) @@ -222,10 +222,10 @@ index b3ff6e13..d4ddf4d6 100644 void td_queue_write(td_image_t *, td_request_t); void td_queue_read(td_image_t *, td_request_t); diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c -index 7da25b6e..d082cc8d 100644 +index 9b6d4d84..345fba36 100644 --- a/drivers/tapdisk-vbd.c +++ b/drivers/tapdisk-vbd.c -@@ -1164,6 +1164,22 @@ tapdisk_vbd_query_commit_job(td_vbd_t *vbd, td_query_t *query) +@@ -1165,6 +1165,22 @@ tapdisk_vbd_query_commit_job(td_vbd_t *vbd, td_query_t *query) return err; } @@ -249,7 +249,7 @@ index 7da25b6e..d082cc8d 100644 tapdisk_vbd_request_ttl(td_vbd_request_t *vreq, const struct timeval *now) diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h -index fb948255..415e0d1f 100644 +index af333736..bfac0c66 100644 --- a/drivers/tapdisk-vbd.h +++ b/drivers/tapdisk-vbd.h @@ -232,6 +232,7 @@ void tapdisk_vbd_check_state(td_vbd_t *); @@ -261,7 +261,7 @@ index fb948255..415e0d1f 100644 void tapdisk_vbd_complete_td_request(td_request_t, int); int add_extent(tapdisk_extents_t *, td_request_t *); diff --git a/drivers/tapdisk.h b/drivers/tapdisk.h -index 06891584..d32e36b5 100644 +index cf1adabc..2f74c957 100644 --- a/drivers/tapdisk.h +++ b/drivers/tapdisk.h @@ -237,6 +237,7 @@ struct tap_disk { diff --git a/SOURCES/0044-qcow2-support-cancel-command.patch b/SOURCES/0043-qcow2-support-cancel-command.patch similarity index 89% rename from SOURCES/0044-qcow2-support-cancel-command.patch rename to SOURCES/0043-qcow2-support-cancel-command.patch index 65cc327..58ad396 100644 --- a/SOURCES/0044-qcow2-support-cancel-command.patch +++ b/SOURCES/0043-qcow2-support-cancel-command.patch @@ -1,4 +1,4 @@ -From 106e420cdca9ce4f310129ca8ad32c75d6003d0e Mon Sep 17 00:00:00 2001 +From 0a8a3097f67baa716a76ff773b3988ebf6d62586 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Wed, 5 Mar 2025 18:22:56 +0100 Subject: [PATCH] qcow2: support cancel command @@ -7,11 +7,11 @@ This command will cancel the coalesce job in background. Signed-off-by: Anthoine Bourgeois --- - drivers/block-qcow2.c | 92 ++++++++++++++++++++++++++++++++++++++++++- - 1 file changed, 91 insertions(+), 1 deletion(-) + drivers/block-qcow2.c | 97 ++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c -index 37e7c324..eb9f77b1 100644 +index 5c57233e..1dca89dd 100644 --- a/drivers/block-qcow2.c +++ b/drivers/block-qcow2.c @@ -100,6 +100,7 @@ enum qcow2_ops { @@ -78,7 +78,7 @@ index 37e7c324..eb9f77b1 100644 DBG(TLOG_WARN, "qcow2_close\n"); pthread_mutex_lock(&s->lock); -@@ -1061,9 +1083,77 @@ signal: +@@ -1053,9 +1075,82 @@ signal: pthread_mutex_unlock(&s->commit_lock); } @@ -114,6 +114,10 @@ index 37e7c324..eb9f77b1 100644 + + free_qcow2_request(s, req); + ++ /* Already canceled, so this is considered a success */ ++ if (err == -ECANCELED) ++ err = 0; ++ + return err; +} + @@ -131,7 +135,8 @@ index 37e7c324..eb9f77b1 100644 + goto signal; + } + -+ if (bjob->job.status == JOB_STATUS_RUNNING) { ++ if (bjob->job.status == JOB_STATUS_RUNNING || ++ bjob->job.status == JOB_STATUS_READY) { + if (req->sync == false) { + job_cancel_locked(&bjob->job, false); + } else { @@ -156,7 +161,7 @@ index 37e7c324..eb9f77b1 100644 struct qcow2_state *s = (struct qcow2_state *)driver->data; DBG(TLOG_WARN, "Qcow2: %s: queued %lu, completed %lu, returned %lu, " -@@ -1075,7 +1165,6 @@ qcow2_debug(td_driver_t *driver) +@@ -1067,7 +1162,6 @@ qcow2_debug(td_driver_t *driver) s->writes, (s->writes ? ((float)s->write_size / s->writes) : 0.0), s->schedule, s->kick); @@ -164,7 +169,7 @@ index 37e7c324..eb9f77b1 100644 print_latencies(s); #endif } -@@ -1093,5 +1182,6 @@ struct tap_disk tapdisk_qcow = { +@@ -1085,5 +1179,6 @@ struct tap_disk tapdisk_qcow = { .td_validate_parent = qcow2_validate_parent, .td_commit = qcow2_commit, .td_query_commit_job = qcow2_query_commit_job, diff --git a/SOURCES/0045-libqcow2-fix-abort-commit-without-crash.patch b/SOURCES/0044-libqcow2-fix-abort-commit-without-crash.patch similarity index 90% rename from SOURCES/0045-libqcow2-fix-abort-commit-without-crash.patch rename to SOURCES/0044-libqcow2-fix-abort-commit-without-crash.patch index b2f487d..17a519a 100644 --- a/SOURCES/0045-libqcow2-fix-abort-commit-without-crash.patch +++ b/SOURCES/0044-libqcow2-fix-abort-commit-without-crash.patch @@ -1,4 +1,4 @@ -From 0ed27d616b9cb3fe3ab7fc9c5b27b24c30234839 Mon Sep 17 00:00:00 2001 +From 09a1a2609d0e4388bdd2e71948401459d502a8b7 Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Wed, 12 Mar 2025 10:28:42 +0100 Subject: [PATCH] libqcow2: fix abort commit without crash @@ -9,7 +9,7 @@ Signed-off-by: Anthoine Bourgeois 1 file changed, 4 insertions(+) diff --git a/qcow2/lib/block/commit.c b/qcow2/lib/block/commit.c -index 6b96a4c6..2e3705ba 100644 +index 7c3fdcb0..e7a201f8 100644 --- a/qcow2/lib/block/commit.c +++ b/qcow2/lib/block/commit.c @@ -98,6 +98,10 @@ static void commit_abort(Job *job) diff --git a/SOURCES/0046-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch b/SOURCES/0046-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch deleted file mode 100644 index 6efa55f..0000000 --- a/SOURCES/0046-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 2118e999404c8bfadf7a04ae5a529dd2d8419130 Mon Sep 17 00:00:00 2001 -From: Anthoine Bourgeois -Date: Tue, 14 Apr 2026 09:40:11 +0200 -Subject: [PATCH] qcow2: Auto finalize commit job to avoid never ending job - -With this patch the qemu code is responsible for the finalize operation -that make the state transition from ready to pending. The query command -is still responsible of dismiss the job to conclue it. - -Signed-off-by: Anthoine Bourgeois ---- - drivers/block-qcow2.c | 5 +++-- - qcow2/lib/util/main-loop.c | 4 ++-- - 2 files changed, 5 insertions(+), 4 deletions(-) - -diff --git a/drivers/block-qcow2.c b/drivers/block-qcow2.c -index eb9f77b1..437c69f4 100644 ---- a/drivers/block-qcow2.c -+++ b/drivers/block-qcow2.c -@@ -971,7 +971,7 @@ do_commit(struct qcow2_state *s, struct qcow2_request *req) - - qmp_block_commit(COMMIT_JOB_ID, node, base_node, NULL, top_node, NULL, NULL, - false, false, false, 0, false, BLOCKDEV_ON_ERROR_REPORT, -- NULL, false, false, true, false, &local_err); -+ NULL, true, true, true, false, &local_err); - - if (local_err) { - DPRINTF("qcow2_commit: error: %s\n", error_get_pretty(local_err)); -@@ -1132,7 +1132,8 @@ do_cancel_commit_job(struct qcow2_state *s, struct qcow2_request *req) - goto signal; - } - -- if (bjob->job.status == JOB_STATUS_RUNNING) { -+ if (bjob->job.status == JOB_STATUS_RUNNING || -+ bjob->job.status == JOB_STATUS_READY) { - if (req->sync == false) { - job_cancel_locked(&bjob->job, false); - } else { -diff --git a/qcow2/lib/util/main-loop.c b/qcow2/lib/util/main-loop.c -index 82d737fe..740a8164 100644 ---- a/qcow2/lib/util/main-loop.c -+++ b/qcow2/lib/util/main-loop.c -@@ -210,12 +210,12 @@ int qemu_deinit_main_loop(void) - - src = iohandler_get_g_source(); - g_source_unref(src); -- g_source_remove(g_source_get_id(src)); -+ g_source_destroy(src); - g_source_unref(src); - - src = aio_get_g_source(qemu_aio_context); - g_source_unref(src); -- g_source_remove(g_source_get_id(src)); -+ g_source_destroy(src); - g_source_unref(src); - - g_array_free(gpollfds, TRUE); --- -2.53.0 - diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 0190aa9..e63aa46 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.1%{?dist} +Release: %{?xsrel}.2%{?dist} License: BSD AND GPL-2.0-or-later Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -80,15 +80,13 @@ Patch1034: 0034-libqcow2-mask-signals-used-by-tapdisk.patch Patch1035: 0035-tapdisk-replace-signals-handling-by-signalfd.patch Patch1036: 0036-qcow2-driver-support-qcow2-images-in-tapdisk.patch Patch1037: 0037-vbd-wake-up-scheduler-to-force-check-ring.patch -Patch1038: 0038-blktap.spec-add-qcow2-dependencies.patch -Patch1039: 0039-tapdisk-support-new-commit-command.patch -Patch1040: 0040-qcow2-support-commit-command.patch -Patch1041: 0041-tapdisk-support-new-query-command.patch -Patch1042: 0042-qcow2-support-query-command.patch -Patch1043: 0043-tapdisk-support-new-cancel-command.patch -Patch1044: 0044-qcow2-support-cancel-command.patch -Patch1045: 0045-libqcow2-fix-abort-commit-without-crash.patch -Patch1046: 0046-qcow2-Auto-finalize-commit-job-to-avoid-never-ending.patch +Patch1038: 0038-tapdisk-support-new-commit-command.patch +Patch1039: 0039-qcow2-support-commit-command.patch +Patch1040: 0040-tapdisk-support-new-query-command.patch +Patch1041: 0041-qcow2-support-query-command.patch +Patch1042: 0042-tapdisk-support-new-cancel-command.patch +Patch1043: 0043-qcow2-support-cancel-command.patch +Patch1044: 0044-libqcow2-fix-abort-commit-without-crash.patch %description Blktap creates kernel block devices which realize I/O requests to @@ -218,6 +216,11 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Thu Jun 18 2026 Anthoine Bourgeois - 3.55.5-9.2 +- Avoid concurrent access on vbd requests. +- Fix deadlock on NBD export. +- Fix use-after-free on VDI close. + * Wed May 20 2026 Anthoine Bourgeois - 3.55.5-9.1 - Sync with 3.55.5-9 - Drop patch 0002-Prevent-segfault-of-vhd-util-scan-on-VHD-with-corrup.patch, upstream now From b777c63b0c2ad34ad2bbd877f7f0135b558c27ce Mon Sep 17 00:00:00 2001 From: Mathieu Labourier Date: Fri, 3 Jul 2026 20:18:22 +0200 Subject: [PATCH 18/20] Release 3.55.5-9.3 Signed-off-by: Mathieu Labourier --- ...eat-pass-error-details-up-to-tap-ctl.patch | 1943 +++++++++++++++++ SPECS/blktap.spec | 8 +- 2 files changed, 1950 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0045-feat-pass-error-details-up-to-tap-ctl.patch diff --git a/SOURCES/0045-feat-pass-error-details-up-to-tap-ctl.patch b/SOURCES/0045-feat-pass-error-details-up-to-tap-ctl.patch new file mode 100644 index 0000000..3f462d4 --- /dev/null +++ b/SOURCES/0045-feat-pass-error-details-up-to-tap-ctl.patch @@ -0,0 +1,1943 @@ +From d3bc87696afd92396a39de63a7302d2dcd9fe08f Mon Sep 17 00:00:00 2001 +From: Mathieu Labourier +Date: Thu, 30 Apr 2026 11:20:20 +0200 +Subject: [PATCH] feat: pass error details up to tap-ctl + +Passing an error structure all the way through the +functions and calls invoked by tap-ctl allows to have detailed errors + +This also paves the way for structured logging +when calling tap-ctl using other program or scripts (e.g. SMAPI) + +(cherry picked from commit b6aba882df7ae943c874e5ca850b36186abc177a) + +Signed-off-by: Mathieu Labourier +--- + control/tap-ctl-unpause.c | 14 ++- + control/tap-ctl.c | 255 ++++++++++++++++++++++++-------------- + drivers/tapdisk-control.c | 116 ++++++++++------- + drivers/tapdisk-image.c | 73 +++++++---- + drivers/tapdisk-image.h | 3 +- + drivers/tapdisk-stream.c | 3 +- + drivers/tapdisk-vbd.c | 26 ++-- + drivers/tapdisk-vbd.h | 4 +- + include/Makefile.am | 1 + + include/tap-ctl.h | 3 +- + include/tapdisk-err.h | 182 +++++++++++++++++++++++++++ + 11 files changed, 501 insertions(+), 179 deletions(-) + create mode 100644 include/tapdisk-err.h + +diff --git a/control/tap-ctl-unpause.c b/control/tap-ctl-unpause.c +index 277ef88..a0d5868 100644 +--- a/control/tap-ctl-unpause.c ++++ b/control/tap-ctl-unpause.c +@@ -44,7 +44,7 @@ + + int + tap_ctl_unpause(const int id, const int minor, const char *params, int flags, +- char *secondary, const char *logpath) ++ char *secondary, const char *logpath, td_err *error) + { + int err; + tapdisk_message_t message; +@@ -54,6 +54,8 @@ tap_ctl_unpause(const int id, const int minor, const char *params, int flags, + message.cookie = minor; + message.u.params.flags = flags; + ++ td_err_init_errno(error); ++ + if (params) + safe_strncpy(message.u.params.path, params, + sizeof(message.u.params.path)); +@@ -63,7 +65,7 @@ tap_ctl_unpause(const int id, const int minor, const char *params, int flags, + secondary); + if (err >= sizeof(message.u.params.secondary)) { + EPRINTF("secondary image name too long\n"); +- return -ENAMETOOLONG; ++ return td_err_set_errno(error, -ENAMETOOLONG); + } + } + if (logpath) { +@@ -73,8 +75,10 @@ tap_ctl_unpause(const int id, const int minor, const char *params, int flags, + err = tap_ctl_connect_send_and_receive(id, &message, NULL); + } + ++ if (message.u.response.message[0]) ++ td_err_set_reason(error, message.u.response.message); + if (err) +- return err; ++ return td_err_set_errno(error, err); + + if (message.type == TAPDISK_MESSAGE_RESUME_RSP + || message.type == TAPDISK_MESSAGE_ERROR) +@@ -87,6 +91,8 @@ tap_ctl_unpause(const int id, const int minor, const char *params, int flags, + + if (err) + EPRINTF("unpause failed: %s\n", strerror(-err)); ++ else ++ return td_err_set_success(error); + +- return err; ++ return td_err_set_errno(error, err); + } +diff --git a/control/tap-ctl.c b/control/tap-ctl.c +index c5597c9..79bcbc6 100644 +--- a/control/tap-ctl.c ++++ b/control/tap-ctl.c +@@ -44,13 +44,27 @@ + + #define MAX_AES_XTS_PLAIN_KEYSIZE 1024 + +-typedef int (*tap_ctl_func_t) (int, char **); ++typedef int (*tap_ctl_func_t) (int, char **, td_err *); + + struct command { + char *name; + tap_ctl_func_t func; + }; + ++static void ++td_err_print_error(const td_err *error) ++{ ++ if (!error) return; ++ ++ fprintf(stderr, "%s [%d - %s]: %s. Reason: %s\n", ++ error->code == TD_SUCCESS ? "SUCCESS" : "ERROR", ++ error->code == TD_USE_ERRNO ? error->saved_errno : (int)error->code, ++ error->cmd[0] ? error->cmd : "", ++ error->code == TD_USE_ERRNO ? strerror(abs(error->saved_errno)) : td_err_code_to_string(error->code), ++ error->reason[0] ? error->reason : "No specific reason provided" ++ ); ++} ++ + static void + tap_cli_list_usage(FILE *stream) + { +@@ -111,7 +125,7 @@ tap_cli_list_dict(tap_list_t *entry) + } + + int +-tap_cli_list(int argc, char **argv) ++tap_cli_list(int argc, char **argv, td_err *error) + { + struct list_head list = LIST_HEAD_INIT(list); + int c, minor, tty, err; +@@ -123,6 +137,7 @@ tap_cli_list(int argc, char **argv) + minor = -1; + type = NULL; + file = NULL; ++ td_err_init_errno(error); + + while ((c = getopt(argc, argv, "m:p:t:f:h")) != -1) { + switch (c) { +@@ -142,7 +157,7 @@ tap_cli_list(int argc, char **argv) + goto usage; + case 'h': + tap_cli_list_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + +@@ -151,7 +166,7 @@ tap_cli_list(int argc, char **argv) + else + err = tap_ctl_list(&list); + if (err) +- return -err; ++ return td_err_set_errno(error, -err); + + tty = isatty(STDOUT_FILENO); + +@@ -186,11 +201,11 @@ tap_cli_list(int argc, char **argv) + + tap_ctl_list_free(&list); + +- return 0; ++ return td_err_set_success(error); + + usage: + tap_cli_list_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -200,13 +215,14 @@ tap_cli_allocate_usage(FILE *stream) + } + + static int +-tap_cli_allocate(int argc, char **argv) ++tap_cli_allocate(int argc, char **argv, td_err *error) + { + char *devname; + int c, minor, err; + char d_flag = 0; + + devname = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "d:h")) != -1) { +@@ -219,22 +235,24 @@ tap_cli_allocate(int argc, char **argv) + goto usage; + case 'h': + tap_cli_allocate_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + err = tap_ctl_allocate(&minor, &devname); +- if (!err) ++ if (!err) { ++ td_err_set_success(error); + printf("%s\n", devname); ++ } + + if (!d_flag) + free(devname); + +- return err; ++ return td_err_set_errno(error, err); + + usage: + tap_cli_allocate_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -244,11 +262,12 @@ tap_cli_free_usage(FILE *stream) + } + + static int +-tap_cli_free(int argc, char **argv) ++tap_cli_free(int argc, char **argv, td_err *error) + { +- int c, minor; ++ int c, minor, err; + + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "m:h")) != -1) { +@@ -260,18 +279,21 @@ tap_cli_free(int argc, char **argv) + goto usage; + case 'h': + tap_cli_free_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (minor == -1) + goto usage; + +- return tap_ctl_free(minor); ++ err = tap_ctl_free(minor); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_free_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -287,9 +309,9 @@ tap_cli_create_usage(FILE *stream) + } + + static int +-tap_cli_create(int argc, char **argv) ++tap_cli_create(int argc, char **argv, td_err *error) + { +- int c, err, flags, prt_minor, timeout; ++ int c, flags, prt_minor, timeout, err; + char *args, *devname, *secondary; + char d_flag = 0; + char *logpath = NULL; +@@ -300,6 +322,7 @@ tap_cli_create(int argc, char **argv) + prt_minor = -1; + flags = 0; + timeout = 0; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "a:RDd:e:r2:st:C:h")) != -1) { +@@ -342,7 +365,7 @@ tap_cli_create(int argc, char **argv) + goto usage; + case 'h': + tap_cli_create_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + +@@ -351,17 +374,19 @@ tap_cli_create(int argc, char **argv) + + err = tap_ctl_create(args, &devname, flags, prt_minor, secondary, + timeout, logpath); +- if (!err) ++ if (!err) { ++ td_err_set_success(error); + printf("%s\n", devname); ++ } + + if (!d_flag) + free(devname); + +- return err; ++ return td_err_set_errno(error, err); + + usage: + tap_cli_create_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -382,14 +407,15 @@ tap_cli_timeout(const char *optarg) + } + + static int +-tap_cli_destroy(int argc, char **argv) ++tap_cli_destroy(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + struct timeval *timeout; + + pid = -1; + minor = -1; + timeout = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:t:h")) != -1) { +@@ -409,18 +435,21 @@ tap_cli_destroy(int argc, char **argv) + goto usage; + case 'h': + tap_cli_destroy_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_destroy(pid, minor, 0, timeout); ++ err = tap_ctl_destroy(pid, minor, 0, timeout); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_destroy_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -430,10 +459,11 @@ tap_cli_spawn_usage(FILE *stream) + } + + static int +-tap_cli_spawn(int argc, char **argv) ++tap_cli_spawn(int argc, char **argv, td_err *error) + { + int c, tty; + pid_t pid; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "h")) != -1) { +@@ -442,13 +472,13 @@ tap_cli_spawn(int argc, char **argv) + goto usage; + case 'h': + tap_cli_spawn_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + pid = tap_ctl_spawn(); + if (pid < 0) +- return pid; ++ return td_err_set_errno(error, pid); + + tty = isatty(STDOUT_FILENO); + if (tty) +@@ -456,11 +486,11 @@ tap_cli_spawn(int argc, char **argv) + else + printf("%d\n", pid); + +- return 0; ++ return td_err_set_success(error); + + usage: + tap_cli_spawn_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -470,12 +500,13 @@ tap_cli_attach_usage(FILE *stream) + } + + static int +-tap_cli_attach(int argc, char **argv) ++tap_cli_attach(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + + pid = -1; + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:h")) != -1) { +@@ -490,18 +521,21 @@ tap_cli_attach(int argc, char **argv) + goto usage; + case 'h': + tap_cli_attach_usage(stderr); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_attach(pid, minor); ++ err = tap_ctl_attach(pid, minor); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_attach_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -511,12 +545,13 @@ tap_cli_detach_usage(FILE *stream) + } + + static int +-tap_cli_detach(int argc, char **argv) ++tap_cli_detach(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + + pid = -1; + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:h")) != -1) { +@@ -531,18 +566,21 @@ tap_cli_detach(int argc, char **argv) + goto usage; + case 'h': + tap_cli_detach_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_detach(pid, minor); ++ err = tap_ctl_detach(pid, minor); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_detach_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -552,15 +590,16 @@ tap_cli_close_usage(FILE *stream) + } + + static int +-tap_cli_close(int argc, char **argv) ++tap_cli_close(int argc, char **argv, td_err *error) + { +- int c, pid, minor, force; ++ int c, pid, minor, force, err; + struct timeval *timeout; + + pid = -1; + minor = -1; + force = 0; + timeout = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:ft:h")) != -1) { +@@ -583,18 +622,21 @@ tap_cli_close(int argc, char **argv) + goto usage; + case 'h': + tap_cli_close_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_close(pid, minor, force, timeout); ++ err = tap_ctl_close(pid, minor, force, timeout); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_close_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -604,14 +646,15 @@ tap_cli_pause_usage(FILE *stream) + } + + static int +-tap_cli_pause(int argc, char **argv) ++tap_cli_pause(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + struct timeval *timeout; + + pid = -1; + minor = -1; + timeout = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:t:h")) != -1) { +@@ -630,18 +673,21 @@ tap_cli_pause(int argc, char **argv) + goto usage; + case 'h': + tap_cli_pause_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_pause(pid, minor, timeout); ++ err = tap_ctl_pause(pid, minor, timeout); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_pause_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -653,11 +699,11 @@ tap_cli_unpause_usage(FILE *stream) + } + + int +-tap_cli_unpause(int argc, char **argv) ++tap_cli_unpause(int argc, char **argv, td_err *error) + { + const char *args, *logpath; + char *secondary; +- int c, pid, minor, flags; ++ int c, pid, minor, flags, err; + + pid = -1; + minor = -1; +@@ -665,6 +711,7 @@ tap_cli_unpause(int argc, char **argv) + secondary = NULL; + flags = 0; + logpath = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:a:2:c:h")) != -1) { +@@ -690,18 +737,21 @@ tap_cli_unpause(int argc, char **argv) + goto usage; + case 'h': + tap_cli_unpause_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_unpause(pid, minor, args, flags, secondary, logpath); ++ err = tap_ctl_unpause(pid, minor, args, flags, secondary, logpath, error); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_unpause_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -711,11 +761,12 @@ tap_cli_major_usage(FILE *stream) + } + + static int +-tap_cli_major(int argc, char **argv) ++tap_cli_major(int argc, char **argv, td_err *error) + { + int c, chr, major; + + chr = 0; ++ td_err_init_errno(error); + + while ((c = getopt(argc, argv, "bch")) != -1) { + switch (c) { +@@ -729,7 +780,7 @@ tap_cli_major(int argc, char **argv) + goto usage; + case 'h': + tap_cli_major_usage(stdout); +- return 0; ++ return td_err_set_success(error); + default: + goto usage; + } +@@ -740,16 +791,17 @@ tap_cli_major(int argc, char **argv) + else + major = tap_ctl_blk_major(); + +- if (major < 0) +- return -major; ++ if (major < 0) { ++ return td_err_set_errno(error, -major); ++ } + + printf("%d\n", major); + +- return 0; ++ return td_err_set_success(error); + + usage: + tap_cli_major_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -766,10 +818,10 @@ tap_cli_open_usage(FILE *stream) + } + + static int +-tap_cli_open(int argc, char **argv) ++tap_cli_open(int argc, char **argv, td_err *error) + { + const char *args, *secondary, *logpath; +- int c, pid, minor, flags, prt_minor, timeout; ++ int c, pid, minor, flags, prt_minor, timeout, err; + uint8_t *encryption_key; + ssize_t key_size = 0; + +@@ -782,6 +834,7 @@ tap_cli_open(int argc, char **argv) + secondary = NULL; + logpath = NULL; + encryption_key = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "a:RDm:p:e:r2:st:C:Eh")) != -1) { +@@ -848,22 +901,25 @@ tap_cli_open(int argc, char **argv) + if (encryption_key) { + free(encryption_key); + } +- return 0; ++ return td_err_set_success(error); + } + } + + if (pid == -1 || minor == -1 || !args) + goto usage; + +- return tap_ctl_open(pid, minor, args, flags, prt_minor, secondary, ++ err = tap_ctl_open(pid, minor, args, flags, prt_minor, secondary, + timeout, logpath, (uint8_t)key_size, encryption_key); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_open_usage(stderr); + if (encryption_key) { + free(encryption_key); + } +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -876,13 +932,14 @@ tap_cli_stats_usage(FILE *stream) + } + + static int +-tap_cli_stats(int argc, char **argv) ++tap_cli_stats(int argc, char **argv, td_err *error) + { + pid_t pid; + int c, minor, err; + + pid = -1; + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:h")) != -1) { +@@ -897,7 +954,7 @@ tap_cli_stats(int argc, char **argv) + goto usage; + case 'h': + tap_cli_stats_usage(stdout); +- return 0; ++ return td_err_set_success(error); + } + } + +@@ -906,15 +963,15 @@ tap_cli_stats(int argc, char **argv) + + err = tap_ctl_stats_fwrite(pid, minor, stdout); + if (err) +- return err; ++ return td_err_set_errno(error, err); + + fprintf(stdout, "\n"); + +- return 0; ++ return td_err_set_success(error); + + usage: + tap_cli_stats_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -925,22 +982,25 @@ tap_cli_check_usage(FILE *stream) + } + + static int +-tap_cli_check(int argc, char **argv) ++tap_cli_check(int argc, char **argv, td_err *error) + { + int err; + const char *msg; + ++ td_err_init_errno(error); ++ + if (argc != 1) + goto usage; + + err = tap_ctl_check(&msg); + printf("%s\n", msg); +- +- return err; ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_check_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -950,14 +1010,15 @@ tap_cli_commit_usage(FILE *stream) + } + + int +-tap_cli_commit(int argc, char **argv) ++tap_cli_commit(int argc, char **argv, td_err *error) + { + const char *args; +- int c, pid, minor; ++ int c, pid, minor, err; + + pid = -1; + minor = -1; + args = NULL; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:a:h")) != -1) { +@@ -982,11 +1043,14 @@ tap_cli_commit(int argc, char **argv) + if (pid == -1 || minor == -1 || args == NULL) + goto usage; + +- return tap_ctl_commit(pid, minor, args); ++ err = tap_ctl_commit(pid, minor, args); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_commit_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -996,12 +1060,13 @@ tap_cli_query_commit_job_usage(FILE *stream) + } + + int +-tap_cli_query_commit_job(int argc, char **argv) ++tap_cli_query_commit_job(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + + pid = -1; + minor = -1; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:h")) != -1) { +@@ -1023,11 +1088,14 @@ tap_cli_query_commit_job(int argc, char **argv) + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_query_commit_job(pid, minor); ++ err = tap_ctl_query_commit_job(pid, minor); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_query_commit_job_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + static void +@@ -1037,14 +1105,15 @@ tap_cli_cancel_commit_job_usage(FILE *stream) + } + + int +-tap_cli_cancel_commit_job(int argc, char **argv) ++tap_cli_cancel_commit_job(int argc, char **argv, td_err *error) + { +- int c, pid, minor; ++ int c, pid, minor, err; + bool wait; + + pid = -1; + minor = -1; + wait = false; ++ td_err_init_errno(error); + + optind = 0; + while ((c = getopt(argc, argv, "p:m:wh")) != -1) { +@@ -1069,11 +1138,14 @@ tap_cli_cancel_commit_job(int argc, char **argv) + if (pid == -1 || minor == -1) + goto usage; + +- return tap_ctl_cancel_commit_job(pid, minor, wait); ++ err = tap_ctl_cancel_commit_job(pid, minor, wait); ++ if (!err) ++ return td_err_set_success(error); ++ return td_err_set_errno(error, err); + + usage: + tap_cli_cancel_commit_job_usage(stderr); +- return EINVAL; ++ return td_err_set_errno(error, EINVAL); + } + + struct command commands[] = { +@@ -1196,7 +1268,8 @@ main(int argc, char *argv[]) + cargv[cnt++] = arg; + } + +- ret = cmd->func(cnt, cargv); ++ td_err error = td_err_generate_success(); ++ ret = cmd->func(cnt, cargv, &error); + + free(cargv); + free(path); +@@ -1205,5 +1278,7 @@ main(int argc, char *argv[]) + /* FIXME errors are not always returned as negative numbers */ + fprintf(stderr, "%s\n", strerror(abs(ret))); + ++ if (error.code != TD_SUCCESS) ++ td_err_print_error(&error); + return (ret >= 0 ? ret : -ret); + } +diff --git a/drivers/tapdisk-control.c b/drivers/tapdisk-control.c +index 4b7aab2..d6fdf43 100644 +--- a/drivers/tapdisk-control.c ++++ b/drivers/tapdisk-control.c +@@ -52,6 +52,7 @@ + #include "tapdisk.h" + #include "tapdisk-vbd.h" + #include "tapdisk-blktap.h" ++#include "tapdisk-err.h" + #include "tapdisk-utils.h" + #include "tapdisk-server.h" + #include "tapdisk-message.h" +@@ -129,7 +130,7 @@ struct tapdisk_ctl_conn { + + struct tapdisk_control_info { + int (*handler)(struct tapdisk_ctl_conn *, tapdisk_message_t *, +- tapdisk_message_t * const); ++ tapdisk_message_t * const, td_err *); + int flags; + }; + +@@ -548,7 +549,7 @@ tapdisk_control_validate_request(tapdisk_message_t *request) + + static int + tapdisk_control_list(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + td_vbd_t *vbd; + struct list_head *head; +@@ -584,12 +585,12 @@ tapdisk_control_list(struct tapdisk_ctl_conn *conn, + response->u.list.minor = -1; + response->u.list.path[0] = 0; + +- return 0; ++ return td_err_set_success(error); + } + + static int + tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + ASSERT(response); + +@@ -597,12 +598,12 @@ tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn, + response->cookie = request->cookie; + response->u.tapdisk_pid = getpid(); + +- return 0; ++ return td_err_set_success(error); + } + + static int + tapdisk_control_attach_vbd(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + char *devname = NULL; + td_vbd_t *vbd; +@@ -612,6 +613,8 @@ tapdisk_control_attach_vbd(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + /* + * TODO: check for max vbds per process + */ +@@ -656,9 +659,10 @@ out: + if (!err) { + response->type = TAPDISK_MESSAGE_ATTACH_RSP; + response->cookie = request->cookie; ++ return td_err_set_success(error); + } + +- return err; ++ return td_err_set_errno(error, err); + + fail_vbd: + tapdisk_vbd_detach(vbd); +@@ -668,7 +672,7 @@ fail_vbd: + + static int + tapdisk_control_detach_vbd(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + td_vbd_t *vbd; + int err = 0; +@@ -677,6 +681,8 @@ tapdisk_control_detach_vbd(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + vbd = tapdisk_server_get_vbd(request->cookie); + if (!vbd) { + err = -ENODEV; +@@ -699,16 +705,17 @@ out: + if (!err) { + response->type = TAPDISK_MESSAGE_DETACH_RSP; + response->cookie = request->cookie; ++ return td_err_set_success(error); + } + +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { +- int err, ret; ++ int ret, err = 0; + td_vbd_t *vbd; + td_flag_t flags; + +@@ -716,6 +723,8 @@ tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + vbd = tapdisk_server_get_vbd(request->cookie); + if (!vbd) { + err = -EINVAL; +@@ -811,7 +820,7 @@ tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, + } + + err = tapdisk_vbd_open_vdi(vbd, request->u.params.path, flags, +- request->u.params.prt_devnum); ++ request->u.params.prt_devnum, error); + if (err) + goto out; + +@@ -845,16 +854,15 @@ tapdisk_control_open_image(struct tapdisk_ctl_conn *conn, + goto fail_close; + } + +- err = 0; +- + out: + if (!err) { + response->u.image.sectors = vbd->disk_info.size; + response->u.image.sector_size = vbd->disk_info.sector_size; + response->u.image.info = vbd->disk_info.info; + response->type = TAPDISK_MESSAGE_OPEN_RSP; ++ return td_err_set_success(error); + } +- return err; ++ return td_err_set_errno(error, err); + + fail_close: + tapdisk_vbd_close_vdi(vbd); +@@ -869,7 +877,7 @@ fail_close: + + static int + tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + td_vbd_t *vbd; + int err = 0; +@@ -879,10 +887,12 @@ tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + vbd = tapdisk_server_get_vbd(request->cookie); + if (!vbd) { + EPRINTF("VBD %d does not exist", request->cookie); +- err = -ENODEV; ++ err = -ENODEV; + goto out; + } + +@@ -983,14 +993,16 @@ tapdisk_control_close_image(struct tapdisk_ctl_conn *conn, + + out: + response->cookie = request->cookie; +- if (!err) ++ if (!err) { + response->type = TAPDISK_MESSAGE_CLOSE_RSP; +- return err; ++ return td_err_set_success(error); ++ } ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + struct timeval now, next = { 0, 0 }, interval = { 0, 10000 }; + int err = 0; +@@ -1000,6 +1012,8 @@ tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + vbd = tapdisk_server_get_vbd(request->cookie); + if (!vbd) { + /* TODO log error */ +@@ -1033,12 +1047,12 @@ out: + response->cookie = request->cookie; + if (!err) + response->type = TAPDISK_MESSAGE_PAUSE_RSP; +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + int err, ret; + td_vbd_t *vbd; +@@ -1048,6 +1062,8 @@ tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + /* TODO validate secondary */ + + INFO("resuming VBD %d, flags=0x%08x, secondary=%p\n", request->cookie, +@@ -1099,17 +1115,19 @@ tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn, + if (request->u.params.path[0]) + desc = request->u.params.path; + +- err = tapdisk_vbd_resume(vbd, desc); ++ err = tapdisk_vbd_resume(vbd, desc, error); + out: + response->cookie = request->cookie; +- if (!err) +- response->type = TAPDISK_MESSAGE_RESUME_RSP; +- return err; ++ if (!err) { ++ response->type = TAPDISK_MESSAGE_RESUME_RSP; ++ return td_err_set_success(error); ++ } ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_stats(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + td_stats_t _st, *st = &_st; + td_vbd_t *vbd; +@@ -1121,6 +1139,8 @@ tapdisk_control_stats(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); ++ + buf = malloc(TD_CTL_SEND_BUFSZ); + if (!buf) { + rv = -ENOMEM; +@@ -1179,9 +1199,9 @@ out: + response->u.info.length = rv; + tapdisk_control_write_message(conn, response); + conn->out.prod += rv; +- return 0; +- } else +- return rv; ++ return td_err_set_success(error); ++ } ++ return td_err_set_errno(error, rv); + } + + /** +@@ -1193,7 +1213,7 @@ out: + static int + tapdisk_control_xenblkif_connect( + struct tapdisk_ctl_conn *conn __attribute__((unused)), +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + /* + * Get the block interface parameters (domain ID, device ID, etc.). +@@ -1210,6 +1230,7 @@ tapdisk_control_xenblkif_connect( + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + minor = request->cookie; + + vbd = tapdisk_server_get_vbd(minor); +@@ -1242,13 +1263,13 @@ out: + EPRINTF("VBD %d failed to connect to the shared ring: %s\n", + minor, strerror(-err)); + +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_xenblkif_disconnect( + struct tapdisk_ctl_conn *conn __attribute__((unused)), +- tapdisk_message_t * request, tapdisk_message_t * const response) ++ tapdisk_message_t * request, tapdisk_message_t * const response, td_err *error) + { + tapdisk_message_blkif_t *blkif_msg; + int err; +@@ -1256,6 +1277,7 @@ tapdisk_control_xenblkif_disconnect( + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + blkif_msg = &request->u.blkif; + + ASSERT(blkif_msg); +@@ -1270,13 +1292,13 @@ tapdisk_control_xenblkif_disconnect( + EPRINTF("failed to disconnect domid=%d, devid=%d from the " + "ring: %s\n", blkif_msg->domid, blkif_msg->devid, + strerror(-err)); +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_disk_info( + struct tapdisk_ctl_conn *conn __attribute__((unused)), +- tapdisk_message_t * request, tapdisk_message_t * const response) ++ tapdisk_message_t * request, tapdisk_message_t * const response, td_err *error) + { + tapdisk_message_image_t *image; + int err = 0; +@@ -1286,6 +1308,7 @@ tapdisk_control_disk_info( + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + image = &response->u.image; + + vbd = tapdisk_server_get_vbd(request->cookie); +@@ -1304,12 +1327,12 @@ out: + image->sector_size = vbd->disk_info.sector_size; + image->info = vbd->disk_info.info; + } +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_commit(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + int err; + td_vbd_t *vbd; +@@ -1319,6 +1342,7 @@ tapdisk_control_commit(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + INFO("commit %d\n", request->cookie); + + vbd = tapdisk_server_get_vbd(request->cookie); +@@ -1336,12 +1360,12 @@ out: + response->cookie = request->cookie; + if (!err) + response->type = TAPDISK_MESSAGE_COMMIT_RSP; +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_query_commit_job(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + int err; + td_vbd_t *vbd; +@@ -1351,6 +1375,7 @@ tapdisk_control_query_commit_job(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + INFO("query commit job %d\n", request->cookie); + + vbd = tapdisk_server_get_vbd(request->cookie); +@@ -1373,12 +1398,12 @@ out: + response->u.query.current_progress = query.current_progress; + response->u.query.total_progress = query.total_progress; + } +- return err; ++ return td_err_set_errno(error, err); + } + + static int + tapdisk_control_cancel_commit_job(struct tapdisk_ctl_conn *conn, +- tapdisk_message_t *request, tapdisk_message_t * const response) ++ tapdisk_message_t *request, tapdisk_message_t * const response, td_err *error) + { + int err; + td_vbd_t *vbd; +@@ -1387,6 +1412,7 @@ tapdisk_control_cancel_commit_job(struct tapdisk_ctl_conn *conn, + ASSERT(request); + ASSERT(response); + ++ td_err_init_errno(error); + INFO("cancel commit job %d\n", request->cookie); + + vbd = tapdisk_server_get_vbd(request->cookie); +@@ -1401,7 +1427,7 @@ out: + response->cookie = request->cookie; + if (!err) + response->type = TAPDISK_MESSAGE_CANCEL_COMMIT_JOB_RSP; +- return err; ++ return td_err_set_errno(error, err); + } + + +@@ -1541,6 +1567,8 @@ tapdisk_control_process_request(event_id_t event_id, + ASSERT(conn); + ASSERT(event_id == conn->event_id); + ++ td_err error = td_err_generate_errno_error(0); ++ + if (conn->event_id) + tapdisk_server_unregister_event(conn->event_id); + +@@ -1553,11 +1581,13 @@ tapdisk_control_process_request(event_id_t event_id, + memset(&conn->response, 0, sizeof(conn->response)); + conn->response.cookie = conn->request.cookie; + +- err = conn->info->handler(conn, &conn->request, &conn->response); ++ err = conn->info->handler(conn, &conn->request, &conn->response, &error); + if (err) { + conn->response.type = TAPDISK_MESSAGE_ERROR; + conn->response.u.response.error = -err; + } ++ if (td_err_get_reason(&error)[0]) ++ snprintf(conn->response.u.response.message, TAPDISK_MESSAGE_STRING_LENGTH, "%s", td_err_get_reason(&error)); + if (err || conn->response.type != TAPDISK_MESSAGE_STATS_RSP) + tapdisk_control_write_message(conn, &conn->response); + +diff --git a/drivers/tapdisk-image.c b/drivers/tapdisk-image.c +index eca855a..1368188 100644 +--- a/drivers/tapdisk-image.c ++++ b/drivers/tapdisk-image.c +@@ -231,45 +231,52 @@ fail: + + static int + tapdisk_image_open_parent(td_image_t *image, struct td_vbd_encryption *encryption, +- td_image_t **_parent) ++ td_image_t **_parent, td_err *error) + { + td_image_t *parent = NULL; + td_disk_id_t id; + int err; + ++ td_err_init_errno(error); + memset(&id, 0, sizeof(id)); + id.flags = image->flags; + + err = td_get_parent_id(image, &id); + if (err == TD_NO_PARENT) { +- err = 0; ++ td_err_set_success(error); + goto out; + } + if (err) +- return err; ++ return td_err_set_errno(error, err); + + if (((id.flags & TD_OPEN_NO_O_DIRECT) == TD_OPEN_NO_O_DIRECT) && + ((id.flags & TD_OPEN_LOCAL_CACHE) == TD_OPEN_LOCAL_CACHE)) + id.flags &= ~TD_OPEN_NO_O_DIRECT; + err = tapdisk_image_open(id.type, id.name, id.flags, encryption, &parent); ++ if (err) { ++ td_err_set_reason(error, id.name); ++ free(id.name); ++ return td_err_set_errno(error, err); ++ } ++ td_err_set_success(error); + /* Name has been duped to driver_name */ + free(id.name); +- if (err) +- return err; + + out: + *_parent = parent; +- return err; ++ return td_err_get_errno(error); + } + + static int +-tapdisk_image_open_parents(td_image_t *image, struct td_vbd_encryption *encryption) ++tapdisk_image_open_parents(td_image_t *image, struct td_vbd_encryption *encryption, td_err *error) + { +- td_image_t *parent; ++ td_image_t *parent = NULL; + int err; + ++ td_err_init_errno(error); ++ + do { +- err = tapdisk_image_open_parent(image, encryption, &parent); ++ err = tapdisk_image_open_parent(image, encryption, &parent, error); + if (err) + break; + +@@ -279,7 +286,7 @@ tapdisk_image_open_parents(td_image_t *image, struct td_vbd_encryption *encrypti + } + } while (parent); + +- return err; ++ return td_err_set_errno(error, err); + } + + void +@@ -347,16 +354,19 @@ done: + static int + __tapdisk_image_open_chain(int type, const char *name, int flags, + struct td_vbd_encryption *encryption, struct list_head *_head, +- int prt_devnum) ++ int prt_devnum, td_err *error) + { + char *prt_nbd_path = NULL; + struct list_head head = LIST_HEAD_INIT(head); + td_image_t *image; + int err; + ++ td_err_init_errno(error); + err = tapdisk_image_open(type, name, flags, encryption, &image); +- if (err) ++ if (err) { ++ td_err_set_reason(error, name); + goto fail; ++ } + + list_add_tail(&image->next, &head); + +@@ -371,8 +381,10 @@ __tapdisk_image_open_chain(int type, const char *name, int flags, + err = tapdisk_image_open(DISK_TYPE_NBD, prt_nbd_path, + flags | TD_OPEN_RDONLY, + encryption, &image); +- if (err) ++ if (err) { ++ td_err_set_reason(error, prt_nbd_path); + goto fail; ++ } + + free(prt_nbd_path); + prt_nbd_path = NULL; +@@ -380,19 +392,19 @@ __tapdisk_image_open_chain(int type, const char *name, int flags, + goto done; + } + +- err = tapdisk_image_open_parents(image, encryption); ++ err = tapdisk_image_open_parents(image, encryption, error); + if (err) + goto fail; + + done: + list_splice(&head, _head); +- return 0; ++ return td_err_set_success(error); + + fail: + free(prt_nbd_path); + + tapdisk_image_close_chain(&head); +- return err; ++ return td_err_set_errno(error, err); + } + + int +@@ -434,7 +446,7 @@ fail: + + static int + tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryption, +- struct list_head *_head) ++ struct list_head *_head, td_err *error) + { + struct list_head head = LIST_HEAD_INIT(head); + td_image_t *image = NULL, *next; +@@ -442,6 +454,8 @@ tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryptio + FILE *s; + int err; + ++ td_err_init_errno(error); ++ + s = fopen(path, "r"); + if (!s) { + err = -errno; +@@ -506,8 +520,10 @@ tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryptio + } + + err = tapdisk_image_open(type, path, flags, encryption, &image); +- if (err) ++ if (err) { ++ td_err_set_reason(error, path); + goto fail; ++ } + + list_add_tail(&image->next, &head); + } while (1); +@@ -517,9 +533,10 @@ tapdisk_image_open_x_chain(const char *path, struct td_vbd_encryption *encryptio + goto fail; + } + +- err = tapdisk_image_open_parents(image, encryption); ++ err = tapdisk_image_open_parents(image, encryption, error); + if (err) + goto fail; ++ td_err_set_success(error); + + list_splice(&head, _head); + out: +@@ -530,7 +547,7 @@ out: + if (s) + fclose(s); + +- return err; ++ return td_err_get_errno(error); + + fail: + tapdisk_for_each_image_safe(image, next, &head) +@@ -541,15 +558,19 @@ fail: + + int + tapdisk_image_open_chain(const char *desc, int flags, int prt_devnum, +- struct td_vbd_encryption *encryption, struct list_head *head) ++ struct td_vbd_encryption *encryption, struct list_head *head, td_err *error) + { + const char *name; + int type, err; + ++ td_err_init_errno(error); ++ + type = tapdisk_disktype_parse_params(desc, &name); +- if (type >= 0) +- return __tapdisk_image_open_chain(type, name, flags, encryption, +- head, prt_devnum); ++ if (type >= 0) { ++ err = __tapdisk_image_open_chain(type, name, flags, encryption, ++ head, prt_devnum, error); ++ return td_err_set_errno(error, err); ++ } + + err = type; + +@@ -557,12 +578,12 @@ tapdisk_image_open_chain(const char *desc, int flags, int prt_devnum, + switch (desc[2]) { + case 'c': + if (!strncmp(desc, "x-chain", strlen("x-chain"))) +- err = tapdisk_image_open_x_chain(name, encryption, head); ++ err = tapdisk_image_open_x_chain(name, encryption, head, error); + break; + } + } + +- return err; ++ return td_err_set_errno(error, err); + } + + int +diff --git a/drivers/tapdisk-image.h b/drivers/tapdisk-image.h +index dad72d4..bec01fe 100644 +--- a/drivers/tapdisk-image.h ++++ b/drivers/tapdisk-image.h +@@ -32,6 +32,7 @@ + #define _TAPDISK_IMAGE_H_ + + #include "tapdisk.h" ++#include "tapdisk-err.h" + + struct td_image_handle { + int type; +@@ -79,7 +80,7 @@ struct td_image_handle { + int tapdisk_image_open(int, const char *, int, struct td_vbd_encryption *, td_image_t **); + void tapdisk_image_close(td_image_t *); + +-int tapdisk_image_open_chain(const char *, int, int, struct td_vbd_encryption *, struct list_head *); ++int tapdisk_image_open_chain(const char *, int, int, struct td_vbd_encryption *, struct list_head *, td_err *); + void tapdisk_image_close_chain(struct list_head *); + int tapdisk_image_validate_chain(struct list_head *); + +diff --git a/drivers/tapdisk-stream.c b/drivers/tapdisk-stream.c +index 29588d7..809386a 100644 +--- a/drivers/tapdisk-stream.c ++++ b/drivers/tapdisk-stream.c +@@ -339,7 +339,8 @@ tapdisk_stream_open_image(struct tapdisk_stream *s, const char *name) + goto out; + } + +- err = tapdisk_vbd_open_vdi(s->vbd, name, TD_OPEN_RDONLY, -1); ++ td_err error = td_err_generate_errno_error(0); ++ err = tapdisk_vbd_open_vdi(s->vbd, name, TD_OPEN_RDONLY, -1, &error); + if (err) + goto out; + +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 345fba3..8fcabbb 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -605,11 +605,13 @@ unlock: + } + + int +-tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_devnum) ++tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_devnum, td_err *error) + { + char *tmp = vbd->name; + int err; + ++ td_err_init_errno(error); ++ + if (!list_empty(&vbd->images)) { + err = -EBUSY; + goto fail; +@@ -628,7 +630,7 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d + } + } + +- err = tapdisk_image_open_chain(vbd->name, flags, prt_devnum, &vbd->encryption, &vbd->images); ++ err = tapdisk_image_open_chain(vbd->name, flags, prt_devnum, &vbd->encryption, &vbd->images, error); + if (err) + goto fail; + +@@ -681,7 +683,7 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d + if (vbd->nbd_mirror_failed != 1) + goto fail; + INFO("Ignoring failed NBD secondary attach\n"); +- err = 0; ++ td_err_set_success(error); + } + } + +@@ -701,7 +703,7 @@ tapdisk_vbd_open_vdi(td_vbd_t *vbd, const char *name, td_flag_t flags, int prt_d + if (tmp != vbd->name) + free(tmp); + +- return err; ++ return td_err_get_errno(error); + + fail: + if (vbd->name != tmp) { +@@ -722,7 +724,7 @@ fail: + + vbd->flags = 0; + +- return err; ++ return td_err_set_errno(error, err); + } + + void +@@ -1062,23 +1064,25 @@ tapdisk_vbd_pause(td_vbd_t *vbd) + } + + int +-tapdisk_vbd_resume(td_vbd_t *vbd, const char *name) ++tapdisk_vbd_resume(td_vbd_t *vbd, const char *name, td_err *error) + { +- int i, err; ++ int i, err = 0; + struct td_xenblkif *blkif; + ++ td_err_init_errno(error); ++ + DBG(TLOG_DBG, "resume requested\n"); + + pthread_mutex_lock(&vbd->mutex); + if (!td_flag_test(vbd->state, TD_VBD_PAUSED)) { + pthread_mutex_unlock(&vbd->mutex); + EPRINTF("resume request for unpaused vbd %s\n", vbd->name); +- return -EINVAL; ++ return td_err_set_errno(error, -EINVAL); + } + pthread_mutex_unlock(&vbd->mutex); + + for (i = 0; i < TD_VBD_EIO_RETRIES; i++) { +- err = tapdisk_vbd_open_vdi(vbd, name, vbd->flags | TD_OPEN_STRICT, -1); ++ err = tapdisk_vbd_open_vdi(vbd, name, vbd->flags | TD_OPEN_STRICT, -1, error); + if (!err) + break; + +@@ -1107,7 +1111,7 @@ resume_failed: + td_flag_set(vbd->state, TD_VBD_RESUME_FAILED); + pthread_mutex_unlock(&vbd->mutex); + tapdisk_vbd_close_vdi(vbd); +- return err; ++ return td_err_set_errno(error, err); + } + td_flag_clear(vbd->state, TD_VBD_RESUME_FAILED); + +@@ -1130,7 +1134,7 @@ resume_failed: + + DBG(TLOG_DBG, "state checked\n"); + +- return 0; ++ return td_err_set_success(error); + } + + int +diff --git a/drivers/tapdisk-vbd.h b/drivers/tapdisk-vbd.h +index bfac0c6..0993007 100644 +--- a/drivers/tapdisk-vbd.h ++++ b/drivers/tapdisk-vbd.h +@@ -209,7 +209,7 @@ int tapdisk_vbd_close(td_vbd_t *); + * @returns 0 on success + */ + int tapdisk_vbd_open_vdi(td_vbd_t * vbd, const char *params, td_flag_t flags, +- int prt_devnum); ++ int prt_devnum, td_err *error); + void tapdisk_vbd_close_vdi(td_vbd_t *); + + int tapdisk_vbd_attach(td_vbd_t *, const char *, int); +@@ -226,7 +226,7 @@ int tapdisk_vbd_issue_requests(td_vbd_t *); + int tapdisk_vbd_kill_queue(td_vbd_t *); + int tapdisk_vbd_pause(td_vbd_t *); + void tapdisk_vbd_squash_pause_logging(bool squash); +-int tapdisk_vbd_resume(td_vbd_t *, const char *); ++int tapdisk_vbd_resume(td_vbd_t *, const char *, td_err *); + void tapdisk_vbd_kick(td_vbd_t *, bool); + void tapdisk_vbd_check_state(td_vbd_t *); + void tapdisk_vbd_free(td_vbd_t *); +diff --git a/include/Makefile.am b/include/Makefile.am +index 91b3680..604128a 100644 +--- a/include/Makefile.am ++++ b/include/Makefile.am +@@ -16,6 +16,7 @@ blktap_HEADERS += blktap3.h + blktap_HEADERS += xen_blkif.h + blktap_HEADERS += tapdisk-message.h + blktap_HEADERS += tap-ctl.h ++blktap_HEADERS += tapdisk-err.h + blktap_HEADERS += debug.h + blktap_HEADERS += util.h + blktap_HEADERS += ../drivers/tapdisk-metrics-stats.h +diff --git a/include/tap-ctl.h b/include/tap-ctl.h +index f093912..160ecfc 100644 +--- a/include/tap-ctl.h ++++ b/include/tap-ctl.h +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -136,7 +137,7 @@ int tap_ctl_pause(const int id, const int minor, struct timeval *timeout); + * Unpauses the VBD + */ + int tap_ctl_unpause(const int id, const int minor, const char *params, +- int flags, char *secondary, const char *logpath); ++ int flags, char *secondary, const char *logpath, td_err *error); + + ssize_t tap_ctl_stats(pid_t pid, int minor, char *buf, size_t size); + int tap_ctl_stats_fwrite(pid_t pid, int minor, FILE *out); +diff --git a/include/tapdisk-err.h b/include/tapdisk-err.h +new file mode 100644 +index 0000000..b29530a +--- /dev/null ++++ b/include/tapdisk-err.h +@@ -0,0 +1,182 @@ ++/* ++ * Copyright (c) 2026, Vates SAS ++ * ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of its ++ * contributors may be used to endorse or promote products derived from ++ * this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ++ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef _TAPDISK_ERR_H_ ++#define _TAPDISK_ERR_H_ ++ ++#define td_err_init_errno(e) _td_err_init_errno((e), __func__) ++#define td_err_generate_success() _td_err_generate_success(__func__) ++#define td_err_generate_errno_error(e) _td_err_generate_errno_error((e), __func__) ++ ++#include ++#include ++#include "debug.h" ++ ++#define TAPDISK_ERR_STRING_LENGTH 256 ++ ++typedef enum _td_err_code_e { ++ TD_SUCCESS, ++ TD_USE_ERRNO, ++ TD_UNKNOWN ++} td_err_code; ++ ++typedef struct _td_err_s { ++ char cmd[TAPDISK_ERR_STRING_LENGTH]; ++ char reason[TAPDISK_ERR_STRING_LENGTH]; ++ td_err_code code; ++ int saved_errno; ++} td_err; ++ ++static inline const char * ++td_err_code_to_string(const td_err_code code) ++{ ++ switch (code) { ++ case TD_SUCCESS: ++ return "success"; ++ case TD_USE_ERRNO: ++ case TD_UNKNOWN: ++ return "invalid error code"; ++ default: ++ ASSERT(0); ++ return "unhandled code"; ++ } ++} ++ ++static inline const char * ++td_err_get_cmd(const td_err *err) ++{ ++ ASSERT(err) ++ return err->cmd; ++} ++ ++static inline const char * ++td_err_get_reason(const td_err *err) ++{ ++ ASSERT(err) ++ return err->reason; ++} ++ ++static inline td_err_code ++td_err_get_code(const td_err *err) ++{ ++ ASSERT(err) ++ return err->code; ++} ++ ++static inline int ++td_err_get_errno(const td_err *err) ++{ ++ ASSERT(err) ++ return err->saved_errno; ++} ++ ++static inline int ++td_err_set_cmd(td_err *err, const char *cmd) ++{ ++ ASSERT(err) ++ if (!cmd) { ++ err->cmd[0] = '\0'; ++ return 0; ++ } ++ return snprintf(err->cmd, TAPDISK_ERR_STRING_LENGTH, "%s", cmd); ++} ++ ++static inline int ++td_err_set_reason(td_err *err, const char *reason) ++{ ++ ASSERT(err) ++ if (!reason) { ++ err->reason[0] = '\0'; ++ return 0; ++ } ++ return snprintf(err->reason, TAPDISK_ERR_STRING_LENGTH, "%s", reason); ++} ++ ++static inline td_err_code ++td_err_set_code(td_err *err, const td_err_code code) ++{ ++ ASSERT(err) ++ return err->code = code; ++} ++ ++static inline int ++td_err_set_errno(td_err *err, const int saved_errno) ++{ ++ ASSERT(err) ++ return err->saved_errno = saved_errno; ++} ++ ++static inline void ++td_err_reset(td_err *err) ++{ ++ ASSERT(err) ++ memset(err, 0, sizeof(*err)); ++} ++ ++static inline int ++td_err_set_success(td_err *err) ++{ ++ td_err_set_code(err, TD_SUCCESS); ++ return td_err_set_errno(err, 0); ++} ++ ++static inline void ++_td_err_init_errno(td_err *err, const char *cmd) ++{ ++ td_err_set_cmd(err, cmd); ++ td_err_set_code(err, TD_USE_ERRNO); ++} ++ ++static inline void ++td_err_init(td_err *err, const int saved_errno, const td_err_code code, const char *cmd) ++{ ++ td_err_reset(err); ++ td_err_set_code(err, code); ++ td_err_set_errno(err, saved_errno); ++ td_err_set_cmd(err, cmd); ++} ++ ++static inline td_err ++_td_err_generate_success(const char *cmd) ++{ ++ td_err ret; ++ td_err_init(&ret, 0, TD_SUCCESS, cmd); ++ return ret; ++} ++ ++static inline td_err ++_td_err_generate_errno_error(const int err, const char *cmd) ++{ ++ td_err ret; ++ td_err_init(&ret, err, TD_USE_ERRNO, cmd); ++ return ret; ++} ++ ++#endif //_TAPDISK_ERR_H_ diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index e63aa46..471c7b2 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.2%{?dist} +Release: %{?xsrel}.3%{?dist} License: BSD AND GPL-2.0-or-later Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -87,6 +87,7 @@ Patch1041: 0041-qcow2-support-query-command.patch Patch1042: 0042-tapdisk-support-new-cancel-command.patch Patch1043: 0043-qcow2-support-cancel-command.patch Patch1044: 0044-libqcow2-fix-abort-commit-without-crash.patch +Patch1045: 0045-feat-pass-error-details-up-to-tap-ctl.patch %description Blktap creates kernel block devices which realize I/O requests to @@ -216,6 +217,11 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Fri Jul 03 2026 Mathieu Labourier - 3.55.5-9.3 +- Pass error details up to tap-ctl. + * The error printed by tap-ctl is now clearer and parsable for tools like SM. + * Also adds details relative to the tapdisk error that cuased the issue (if any). + * Thu Jun 18 2026 Anthoine Bourgeois - 3.55.5-9.2 - Avoid concurrent access on vbd requests. - Fix deadlock on NBD export. From 519a4985cc934c700ccd66f5a1ff8f37aee74244 Mon Sep 17 00:00:00 2001 From: Mathieu Labourier Date: Mon, 6 Jul 2026 14:18:06 +0200 Subject: [PATCH 19/20] fixup typo introduced in Release 3.55.5-9.3 Signed-off-by: Mathieu Labourier --- SPECS/blktap.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index 471c7b2..f0433e8 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -220,7 +220,7 @@ without requiring other libraries * Fri Jul 03 2026 Mathieu Labourier - 3.55.5-9.3 - Pass error details up to tap-ctl. * The error printed by tap-ctl is now clearer and parsable for tools like SM. - * Also adds details relative to the tapdisk error that cuased the issue (if any). + * Also adds details relative to the tapdisk error that caused the issue (if any). * Thu Jun 18 2026 Anthoine Bourgeois - 3.55.5-9.2 - Avoid concurrent access on vbd requests. From 5115f9b73389b0651307db965b79db8d96df33eb Mon Sep 17 00:00:00 2001 From: Anthoine Bourgeois Date: Mon, 24 Aug 2026 16:48:55 +0200 Subject: [PATCH 20/20] Release 3.55.5-9.4 - Fix commit command with cbt enabled. Signed-off-by: Anthoine Bourgeois --- ...g-disks-in-commit-related-operations.patch | 64 +++++++++++++++++++ SPECS/blktap.spec | 6 +- 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch diff --git a/SOURCES/0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch b/SOURCES/0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch new file mode 100644 index 0000000..0a7a4b2 --- /dev/null +++ b/SOURCES/0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch @@ -0,0 +1,64 @@ +From 7034c039d092ade9ce6eef37f1a249289fd42a88 Mon Sep 17 00:00:00 2001 +From: Mathieu Labourier +Date: Mon, 17 Aug 2026 15:08:15 +0200 +Subject: [PATCH] fix: skip cbtlog disks in commit related operations + +This prevents a bug where tapdisk will return a cbtlog +disk in commit related operations, causing them to fail early +since the cbtlog driver doesn't include a commit action + +Signed-off-by: Mathieu Labourier +--- + drivers/tapdisk-vbd.c | 18 +++++++++++++++--- + 1 file changed, 15 insertions(+), 3 deletions(-) + +diff --git a/drivers/tapdisk-vbd.c b/drivers/tapdisk-vbd.c +index 8fcabbbd..7b5171cd 100644 +--- a/drivers/tapdisk-vbd.c ++++ b/drivers/tapdisk-vbd.c +@@ -167,6 +167,18 @@ tapdisk_vbd_first_image(td_vbd_t *vbd) + return image; + } + ++static inline td_image_t * ++tapdisk_vbd_first_non_log_image(td_vbd_t *vbd) ++{ ++ td_image_t *image = NULL, *tmp; ++ ++ tapdisk_vbd_for_each_image(vbd, image, tmp) ++ if (image->type != DISK_TYPE_LOG) ++ return image; ++ ++ return image; ++} ++ + static inline td_image_t * + tapdisk_vbd_last_image(td_vbd_t *vbd) + { +@@ -1146,7 +1158,7 @@ tapdisk_vbd_commit(td_vbd_t *vbd, const char *name) + INFO("commit %s\n", name); + } + +- err = td_commit(tapdisk_vbd_first_image(vbd), name); ++ err = td_commit(tapdisk_vbd_first_non_log_image(vbd), name); + + INFO("commit started (%d)\n", err); + +@@ -1162,7 +1174,7 @@ tapdisk_vbd_query_commit_job(td_vbd_t *vbd, td_query_t *query) + INFO("query commit job.\n"); + } + +- err = td_query_commit_job(tapdisk_vbd_first_image(vbd), query); ++ err = td_query_commit_job(tapdisk_vbd_first_non_log_image(vbd), query); + + INFO("query commit job (%d)\n", err); + +@@ -1178,7 +1190,7 @@ tapdisk_vbd_cancel_commit_job(td_vbd_t *vbd, bool wait) + INFO("cancel commit job.\n"); + } + +- err = td_cancel_commit_job(tapdisk_vbd_first_image(vbd), wait); ++ err = td_cancel_commit_job(tapdisk_vbd_first_non_log_image(vbd), wait); + + INFO("cancel commit job (%d)\n", err); + diff --git a/SPECS/blktap.spec b/SPECS/blktap.spec index f0433e8..897dc5b 100644 --- a/SPECS/blktap.spec +++ b/SPECS/blktap.spec @@ -7,7 +7,7 @@ Summary: blktap user space utilities Name: blktap Version: 3.55.5 -Release: %{?xsrel}.3%{?dist} +Release: %{?xsrel}.4%{?dist} License: BSD AND GPL-2.0-or-later Group: System/Hypervisor URL: https://github.com/xapi-project/blktap @@ -88,6 +88,7 @@ Patch1042: 0042-tapdisk-support-new-cancel-command.patch Patch1043: 0043-qcow2-support-cancel-command.patch Patch1044: 0044-libqcow2-fix-abort-commit-without-crash.patch Patch1045: 0045-feat-pass-error-details-up-to-tap-ctl.patch +Patch1046: 0046-fix-skip-cbtlog-disks-in-commit-related-operations.patch %description Blktap creates kernel block devices which realize I/O requests to @@ -217,6 +218,9 @@ without requiring other libraries %{_libdir}/libblockcrypto.so.* %changelog +* Wed Aug 19 2026 Anthoine Bourgeois - 3.55.5-9.4 +- Fix commit command with cbt enabled. + * Fri Jul 03 2026 Mathieu Labourier - 3.55.5-9.3 - Pass error details up to tap-ctl. * The error printed by tap-ctl is now clearer and parsable for tools like SM.